Production
What to verify before real users arrive. Deployment gets you live; this page keeps you safe once you are.
Make It Yours
Kit ships with the author's defaults baked in. Change these before launch:
apps/web/src/lib/site-config.ts- name, description, production URL.siteConfig.urlfeeds metadata, sitemap, robots, andllms.txtpackages/api/src/auth/auth.ts-oAuthProxyderivesproductionURLfromVERCEL_PROJECT_PRODUCTION_URL; deploying off Vercel means setting that env var (or replacing the derivation)apps/web/src/app/(docs)/docs/layout.tsx-githubUrlpoints at the upstream repopublic/og.jpg- the OpenGraph image referenced insrc/app/layout.tsx
Secrets
BETTER_AUTH_SECRET- unique per environment,openssl rand -base64 32. Rotating it invalidates existing sessions- Separate GitHub OAuth apps for dev and prod; prod callback is
https://your-domain.com/api/auth/callback/github SUPABASE_SERVICE_ROLE_KEYbypasses RLS - it must only appear in server code (apps/web/src/lib/supabase-server.tsand the avatar route are the only consumers)- Nothing secret in
NEXT_PUBLIC_*- those ship to the browser
The Authorization Model
Two layers, know both:
- RLS deny-by-default - every table calls
.enableRLS()with no policies, so the anon key gets nothing through PostgREST - tRPC procedures - the real authorization.
protectedProcedurerequires a session; org-scoped routers verify membership per call (ensureOrganizationAccessintodo-router.ts)
On a hosted Supabase project, also disable the Data API (Settings → API) - local dev already has it off in packages/db/supabase/config.toml. And if you regenerate the auth schema, re-add .enableRLS() to every table.
What Kit Doesn't Ship
Honest gaps. Each is a deliberate "add when you need it":
- Email delivery - set
RESEND_API_KEY(andEMAIL_FROM) in production. Password reset and organization invite emails send through Resend's REST API (packages/api/src/email/send-email.ts); without the key they log to the server console instead of sending - Rate limiting - better-auth's built-in limiter covers auth routes (in-memory, so per-instance on serverless), but tRPC procedures and
/api/chathave none. Add Vercel WAF rules or a middleware backed by a shared store - Billing - Stripe via
@better-auth/stripe. SetSTRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET, andSTRIPE_PRO_PRICE_ID, then point a Stripe webhook at/api/auth/stripe/webhook(events:checkout.session.completed,customer.subscription.created/updated/deleted). The webhook keeps thesubscriptiontable in sync;dashboard/[slug]/billinghandles checkout and the billing portal. Subscriptions are org-scoped — only owners/admins can manage them - Email verification - verification emails send on signup. Uncomment
requireEmailVerificationinpackages/api/src/auth/auth.tsto block unverified logins - Background jobs - no queue. Long work either fits in a request (the chat route caps at
maxDuration = 30) or needs Inngest/Trigger.dev/QStash
Database Safety
drizzle-kit pushhas no down migrations. Back up before schema changes:pg_dump $POSTGRES_URL > backup.sql- Prefer add-column → backfill → drop-column over destructive changes on live data
POSTGRES_URLmust be the transaction pooler (:6543); the client already setsprepare: falsefor it
Cost Guards
The AI chat proxies every message to a model through the AI Gateway. Before opening it to the public, set a spending cap on the gateway key - it's the only per-request variable cost in the stack.
CI Gate
.github/workflows/ci.yml runs on every PR and push to main:
pnpm typecheck && pnpm lint && pnpm format && pnpm test && pnpm buildGreen CI is the merge bar. Note apps/web/next.config.js sets typescript.ignoreBuildErrors: true - type safety is enforced by the typecheck step, not the build, so don't skip it.
Launch Checklist
-
site-config.ts(name, description, Twitter),githubUrl,og.jpgupdated - Unique
BETTER_AUTH_SECRET; separate GitHub OAuth apps - Production schema pushed;
avatarsbucket created; Data API disabled (Settings → API) -
RESEND_API_KEY+EMAIL_FROMset (verification, password reset, invite emails) - Stripe keys set + webhook pointed at
/api/auth/stripe/webhook - Spending cap on
AI_GATEWAY_API_KEY - Uptime monitor on
/api/health - Error tracking installed (Monitoring)
- Database backup taken; restore path tested once
Next Steps
- Monitoring - Know when it breaks
- Analytics - Know how it's used