Domains and what serves them

Clerk instances

There is one Clerk application with a development instance (active-cheetah-78, *.accounts.dev sign-in pages). All apps — web, growth, demo — currently share it. Demo users are deliberately test-mode accounts (…+clerk_test@acme.example.com, fixed email OTP 424242, no mailboxes).
Because every SPA shares one Clerk instance, signing in cannot be restricted per-application. Any Clerk user can obtain a session on any of the SPAs. Access control is always enforced server-side: RLS for the web app, growth-api’s domain-owner check for growth, demo-api’s domain-owner check for the demo console. “User X can log in to app Y” is therefore not a security finding by itself — the question is always what the session is authorized to do.

Where secrets live (three separate stores)

Local development reads .env.local at the repo root (gitignored; every variable is documented in .env.example) and apps/mcp/.dev.vars for the worker.
LLM configuration precedence: an org’s Settings → LLM values win per-field over the worker/function fallback secrets, everywhere. Change models in Settings, not by editing secrets — unless you really mean to change the global fallback.

Hand-uploading a Pages build is forbidden

The VITE_* values live in each Pages project’s build environment, so a locally built bundle contains localhost URLs and will silently break canonical links and sign-in if uploaded by hand. git push origin main is the deploy. To check what is live:
The custom domain can serve the previous bundle for a few seconds after a deployment reports Active — re-check before concluding a deploy failed.