A private-app starter built with TanStack Start, Cloudflare Workers + D1, Drizzle ORM, and Better Auth.
- Email/password bootstrap and recovery
- WebAuthn passkey registration and sign-in
- New accounts default to
pending - The configured owner can approve or revoke users at
/admin - Revocation deletes the user's active sessions
- Protected routes require both a valid session and active access
The account whose email matches ADMIN_OWNER_EMAIL is always treated as the owner.
Set that value before exposing the app publicly.
pnpm install
cp .env.local.example .env.local
cp .env.production.example .env.productionSet a high-entropy BETTER_AUTH_SECRET of at least 32 characters and set
ADMIN_OWNER_EMAIL to your email. Alchemy declares the Worker, D1 database,
bindings, secrets, and migrations in alchemy.run.ts.
pnpm devpnpm dev loads .env.local and uses Alchemy's default personal stage,
dev_$USER. The Worker runs locally while Alchemy provisions a separate,
stage-namespaced D1 database in Cloudflare. Local development therefore uses
real Cloudflare bindings without changing production resources.
Create the owner account using the exact ADMIN_OWNER_EMAIL. Other accounts remain
pending until the owner approves them.
Set the production HTTPS origin in BETTER_AUTH_URL. Alchemy reads the three
values from .env.production; the auth secret and owner email are deployed as
encrypted Worker secrets. Both environment files are ignored by Git, while
their .example templates are committed.
pnpm check
pnpm plan
pnpm run deployPasskeys are origin/domain sensitive, so use a stable production hostname.
The plan and deploy commands target the isolated prod stage. Production uses
the existing safety-car Worker and safety_car_db database; Alchemy reconciles
those resources and applies pending SQL migrations automatically.
Shell environment variables are a fallback for values absent from the selected
file. This is useful in CI, where secrets can be injected by the CI provider
instead of written to disk; omit --env-file there if no environment file is
created.
src/lib/auth.ts— Better Auth and passkey configurationsrc/lib/auth.functions.ts— server-side session and authorization checkssrc/db/schema.ts— users, sessions, and passkeysalchemy.run.ts— Cloudflare infrastructure, bindings, secrets, and deploysrc/routes/_protected.tsx— protected-route boundarysrc/routes/_protected/admin.tsx— owner access management