Auth
DocsPasswords, magic links, passkeys, OAuth and two-factor codes.
Extend it bykeyauth.oauth
Features
Eighteen subsystems, most of them switched from one file, starter.config.ts. Turn one off and its code leaves the deploy.
A board of the eighteen subsystems, grouped like this page, lights up one part at a time while a counter climbs to 18. Then the config line for AI is set to false: AI goes dark, the counter drops to 17, and a note says its code leaves the deploy.
Subsystems so far
0of 18
01Accounts
Who signs in, which organization they act for, and what they may do.
Passwords, magic links, passkeys, OAuth and two-factor codes.
Extend it bykeyauth.oauth
Organizations with roles, seats and invitations, and a second level such as branches when you need one.
Extend it bykeyorgs.roles.<role>
Capabilities granted by role and by plan, enforced on the server.
Extend it bykeyaccess.permissions
A grid of roles and permissions fills in: staff can read users and author the blog, and superadmin can also manage users. Maya, a plain user, asks for insights and is refused with 402 upgrade-required. Her organization buys Pro, the plan grants insights:read, and the same check answers 200.
02Payments
Four providers behind one plan catalogue, from checkout to refund.
Stripe, Polar, Lemon Squeezy or Creem, with trials, coupons, refunds and a grace period.
Extend it bykeypayments.plans.<plan>
A Pro subscription moves along its timeline: a trial starts on October 1, the first charge of $4.99 goes through on October 15, and the renewal on November 15 is declined. The subscription goes past due, and a banner says access stays on until November 29 and links to the billing portal. The provider retries on November 18, the charge is paid and the subscription is active again, without access ever stopping.
03Platform
Tables, work that runs later, files and messages.
Drizzle on Postgres through Hyperdrive, with your tables on a migration chain of their own.
Extend it byfileserver/
Cloudflare Queues with retries and a dead letter queue.
Extend it byfileserver/
Typed events your code reacts to without editing the code that fires them.
Extend it byfileserver/
R2 uploads by purpose, checked by their real file type.
Extend it bykeystorage.purposes.<purpose>
Transactional mail with provider fallback, categories and unsubscribe.
Extend it bykeyemail.providers
An in-app bell, with categories a user can turn off.
Extend it bycallnotify(event, { type, … })
pnpm db:migrate applies the template's five migrations, then your own chain. Below, three jobs run on the queue: email succeeds at once, billing-email fails, waits a minute and succeeds on attempt 2, and crm-sync fails all six attempts and lands in the dead letter queue, where it is reported.
04Product
What your users see, and what you learn from them.
Text, image, speech, transcription, embedding and video over one binding.
Extend it bykeyai.roles.<role>
Declared flags, evaluated during SSR and stored in KV or R2.
Extend it bykeyflags.declarations.<flag>
Server-side events to Cloudflare, Umami or PostHog, with no pixel to block.
Extend it bykeyanalytics.providers
Full i18n, with tests that fail on a missing key.
Extend it byfilei18n/
Nuxt Content posts, and a CI job that drafts new ones as pull requests.
Extend it byfilecontent/
A pricing page in a browser. The newPricing flag is switched on and the page renders again with a New badge. The language switch goes from EN to SR and the card re-renders in Serbian, with the price as 4,99 US$. A visitor asks in Serbian whether they can cancel, and the AI answers in Serbian. Each page view appears in the analytics stream: /pricing twice, then /sr/pricing.
05Edge and ops
Headers and limits in front of every request, and errors forwarded when one fails.
A Content Security Policy and hardening headers on every response.
Extend it bykeysecurity.cspExtend
A cap per route and caller, counted in a Durable Object, with floods shed at the edge first.
Extend it bycallrequireRateLimit(event, …)
Request ids, a health endpoint, and errors forwarded to Sentry or Better Stack without an SDK.
Extend it bykeymonitoring.providers
A GET request to /api/report gets the request id 8f2a61c4bd9e3a10-FRA, passes the rate limit at 12 of 60 this minute, and fails with a 500. The response still carries its six security headers, and the error reaches Sentry tagged with the same request id.
Docs
How it works, how to set it up and where your own code goes. The docs are public, so you can read any page before you buy.
AGENTS.md Briefs AI coding agents on the conventions, the seams and the traps.