Startora is a personal app dashboard with a React/Vite frontend and an
Express/SQLite backend. Authentication uses revocable, server-side sessions in
an HttpOnly cookie; browser state contains the signed-in user and their apps,
not the session credential.
- Node.js 24 LTS
- Corepack with pnpm enabled
corepack enable
pnpm installThe repository's pnpm workspace configuration allows the required
better-sqlite3 and esbuild install scripts. pnpm blocks other dependency
build scripts by default.
pnpm devThe Vite client runs at http://localhost:5173 and proxies /api to the
Express process on port 3000. pnpm dev loads the committed, non-secret
defaults from .env.development; private-network status checks are enabled for
local development. Put machine-specific overrides in the ignored
.env.development.local file.
Useful commands:
pnpm test
pnpm lint
pnpm build
pnpm build:demo
pnpm startpnpm build loads .env.production through Vite. Production runtime settings
for pnpm start still come from the process manager because build-time Vite
variables are embedded in the browser bundle and must not contain secrets.
pnpm build:demoThe demo build writes static frontend assets to dist/demo. It does not
include the Express service or SQLite database. Accounts, sessions, apps, and
categories are stored in the browser under the versioned
startora.demo.database.v1 localStorage key.
pnpm build:demo loads .env.demo and selects the browser-storage data client.
Legacy schema-v1 records under that key are migrated in place before use, so existing demo accounts and sessions remain available after data-model updates.
Each newly registered demo account is seeded from src/demo.json. Subsequent
changes are retained in that browser profile. Demo credentials and data are
local and inspectable by the visitor, can be removed through browser storage
controls, and are not synchronized to another browser or device.
Pushes to main deploy this build to a pre-created Cloudflare Pages Direct
Upload project. Configure the GitHub Actions variable
CLOUDFLARE_PAGES_PROJECT and the secrets CLOUDFLARE_ACCOUNT_ID and
CLOUDFLARE_API_TOKEN; the token needs Cloudflare Pages edit access.
The repository includes non-secret defaults for the development, production,
and demo modes in .env.development, .env.production, and .env.demo.
Files ending in .local are ignored by Git and can hold machine-specific Vite
overrides. The development server also loads .env.development.local when it
exists. Production server configuration and secrets remain the responsibility
of the process manager.
PORT: Express port, default3000.DATABASE_PATH: SQLite file, defaultdata/startora.sqlite.APP_ORIGIN: optional exact browser origin accepted for mutations. When it is omitted, Startora derives the allowed origin from the request protocol and host. The Vite development server useshttp://localhost:5173.COOKIE_SECURE: optionaltrueorfalseoverride for the session cookie'sSecureattribute. When omitted, Startora follows the request protocol.TRUST_PROXY: optional Express proxy trust setting, such as1for one trusted proxy hop orloopbackfor a local proxy. Leave it unset when the container receives browser traffic directly.STATUS_CHECK_ALLOW_PRIVATE_NETWORKS: optionaltrueorfalse, defaultfalse. This is the deployment-level permission required before an account can enable status checks for private and loopback service addresses.NODE_ENV: useproductionfor production runtime behavior.VITE_DATA_MODE:serverselects the HTTP API anddemoselects the browser-only localStorage API. This value is embedded into frontend builds and is not a secret.
HTTPS is recommended for deployments exposed beyond a trusted network. SQLite is intended for one Startora server process; use a network database before scaling to multiple writers.
Commit app icons to src/assets using .svg, .png, .webp, .jpg,
.jpeg, or .avif. The dashboard builds its picker from those files and saves
only the basename, such as figma.svg, to SQLite.
Users can instead enter a direct HTTP(S) image URL. Startora stores that URL in
the existing icon field and loads the image in the browser; it does not download
or store the image itself. File-page URLs, such as GitHub /blob/ pages, are not
image URLs—use the provider's direct or raw URL.
If a bundled icon is removed or a remote icon cannot be loaded after an app has been saved, the dashboard displays the default app icon instead of a broken image.
- Usernames are case-insensitively unique.
- Passwords are hashed with Node's
crypto.scryptand a random salt. - Random session tokens expire after seven rolling days; only SHA-256 token hashes are stored.
- App queries derive the owner from the authenticated session.
- Service availability is checked from the Startora server or container, not from the browser. Results are short-lived process memory and are not stored in SQLite.
- SQLite migrations run transactionally when the server opens the database.