Secrets & environments
V1 runs three environments. Every secret is bound on the smallest set of surfaces it needs — server-only secrets (service role, signing keys, peppers) live in exactly one surface and never reach the browser. Public values (SUPABASE_URL, anon key, Turnstile site key) are intentionally available in both the build env and at runtime.
Environment matrix
Section titled “Environment matrix”| Environment | Astro app | Supabase project | Domains |
|---|---|---|---|
| dev | pnpm dev on localhost | Local CLI (supabase start) or a personal Supabase project | localhost:4321, localhost:4322 |
| preview | Cloudflare Pages preview branch | Shared ui-plan-ai-preview Supabase project | preview.ui.plan.ai (+ per-PR *.pages.dev) |
| prod | Cloudflare Pages main branch | ui-plan-ai Supabase project | ui.plan.ai, api.ui.plan.ai |
The Supabase Auth Site URL and Additional Redirect URLs for each project must list exactly the domains in that environment’s row. CF Pages preview URLs change per build, so add the wildcard https://*.ui-plan-ai.pages.dev to preview, never to prod.
Upstream reference: Pages env vars, Supabase redirect URLs.
Secret inventory
Section titled “Secret inventory”The “Canonical name” column is the value’s logical identity. The “Per-surface variable” column shows the literal environment-variable name on each surface, because Astro requires a PUBLIC_ prefix to expose a value to client JS and Supabase Edge Functions reserve SUPABASE_* for the runtime-injected pair. Browser code reads PUBLIC_SUPABASE_URL and PUBLIC_SUPABASE_ANON_KEY; agents and Edge Functions read the unprefixed canonical name.
| Canonical name | Per-surface variable | Bound to | Notes |
|---|---|---|---|
SUPABASE_URL | PUBLIC_SUPABASE_URL (Astro build env), SUPABASE_URL (Edge Functions, agents) | CF Pages env (public), Edge Functions, agents | Same value in browser and server. |
SUPABASE_ANON_KEY | PUBLIC_SUPABASE_ANON_KEY (Astro build env) | CF Pages env (public) | Browser-only key; RLS does the protecting. |
SUPABASE_SERVICE_ROLE_KEY | SUPABASE_SERVICE_ROLE_KEY | Edge Functions only | Never in the Astro build env. |
API_KEY_PEPPER | API_KEY_PEPPER | Edge Functions only | Server pepper for API-key HMAC; rotatable, versioned. |
CF_ACCOUNT_ID | CF_ACCOUNT_ID | Edge Functions | Identifies the Cloudflare account for Images/Stream API calls. |
CF_IMAGES_TOKEN | CF_IMAGES_TOKEN | Edge Functions | Scoped API token, Images write. |
CF_IMAGES_SIGNING_KEY | CF_IMAGES_SIGNING_KEY | Edge Functions | Mints signed delivery URLs for private images. |
CF_STREAM_TOKEN | CF_STREAM_TOKEN | Edge Functions | Scoped API token, Stream write + Direct Creator Uploads. |
CF_STREAM_SIGNING_KEY | CF_STREAM_SIGNING_KEY | Edge Functions | Mints Stream signed playback JWTs. |
CF_STREAM_WEBHOOK_SECRET | CF_STREAM_WEBHOOK_SECRET | Edge Functions | Verifies webhook-signature on Stream callbacks. |
TURNSTILE_SITE_KEY | PUBLIC_TURNSTILE_SITE_KEY (Astro build env) | CF Pages env (public) | Login form widget. |
TURNSTILE_SECRET_KEY | TURNSTILE_SECRET_KEY | Edge Functions | Verifies Turnstile tokens server-side. |
APP_ORIGINS | APP_ORIGINS | Edge Functions | Comma-separated allowlist used by the shared CORS module and as allowedOrigins for Cloudflare Stream direct uploads. Must list every browser origin that talks to Edge Functions (e.g. https://ui.plan.ai,https://preview.ui.plan.ai). |
Upstream references: Supabase Edge Function secrets, Pages env vars.
Migrations & schema flow
Section titled “Migrations & schema flow”- Schema lives in
supabase/migrations/as timestamped SQL files. - Apply via
supabase db pushfrom CI; never edit schema in the Supabase Studio inprevieworprod. - Local dev:
supabase startboots a containerized stack;supabase db resetre-runs migrations + seed. - Seed data for first tenant (one owner, one agent
plan-ai,mainchannel) lives insupabase/seed.sql.
Upstream: database migrations, local development.
Cache & headers (Cloudflare Pages)
Section titled “Cache & headers (Cloudflare Pages)”No public/_headers file is committed today. Header hardening is an explicit deployment decision because it changes repo invariants documented in AGENTS.md and the deployment skill.
If headers are added later, start from a small baseline:
/* X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=()
/docs/_astro/* Cache-Control: public, max-age=31536000, immutable
/workbench/* Cache-Control: private, no-storeUpstream: Pages headers.
Backup & restore
Section titled “Backup & restore”- Supabase Postgres: PITR enabled, retention ≥ 7 days in
prod. - Cloudflare Images + Stream: treated as durable delivery; not a backup tier.
- Original PNG frames in Supabase Storage are the recovery source for re-deriving Cloudflare variants if needed.
Upstream: Supabase backups.