Wiring Supabase & Cloudflare
This page is the single ordered checklist for connecting Supabase and Cloudflare to V1. Wiring is deferred — the plan is being finalized first. When the team is ready to connect real projects, work top-to-bottom; each step is small, idempotent, and has an upstream reference.
Until then, this page is the operational contract the plan is being designed against. If a step here can’t be satisfied by the V1 plan as written, that’s a plan bug — file it against the linked page.
Before you start
Section titled “Before you start”- The full secret inventory, environment matrix, and binding rules are in Secrets & environments. Don’t paste secrets anywhere not listed there.
- Every config change must be re-verified against the linked upstream page at the time of change — see Upstream docs. Upstream defaults move; this page does not.
- All three environments (dev / preview / prod) are wired with the same steps, against different projects.
Phase 1 — Supabase project
Section titled “Phase 1 — Supabase project”- Create the Supabase project for this environment (
ui-plan-ai-dev,ui-plan-ai-preview,ui-plan-ai). Region: closest to the team. Upstream: organizations & projects. - Install the Supabase CLI locally and run
supabase initinsidesupabase/. Upstream: local development. - Link the local repo to the remote project:
supabase link --project-ref <ref>. - Apply migrations with
supabase db push. Schema lives insupabase/migrations/; never edit schema in Studio for preview or prod. Upstream: database migrations. Schema source of truth: Supabase SQL plan. - Seed the first tenant from
supabase/seed.sql(one owner, one agentplan-ai, one channelmain). Local:supabase db resetre-runs migrations + seed. - Enable point-in-time recovery (≥ 7 days for prod). Upstream: backups.
Phase 2 — Supabase Auth
Section titled “Phase 2 — Supabase Auth”- Disable every sign-in method except email OTP (no passwords, no OAuth in V1). See Auth & sessions.
- Set
Site URL+Additional Redirect URLsto exactly the domains in this environment’s row of the environment matrix. Upstream: redirect URLs. - Enable MFA (TOTP) at the project level. The workbench gates the API-keys screen on
aal2forowner/admin. Upstream: MFA. - Confirm the
profilestrigger (auth.usersinsert →public.profilesrow) is in place from the migrations — see Supabase SQL plan.
Phase 3 — Supabase Storage
Section titled “Phase 3 — Supabase Storage”- Create the
frame-originalsbucket as private. No public read. - Apply Storage RLS policies from Supabase SQL plan — caller must be a member of the tenant prefixing the object path.
- Confirm server-side signed-URL minting uses
expiresIn ≤ 300and uploads sendx-upsert: false. See Media & delivery.
Upstream: access control, signed URLs.
Phase 4 — Supabase Realtime
Section titled “Phase 4 — Supabase Realtime”- Add only these tables to the
supabase_realtimepublication:frame_events,frame_submissions,frames,frame_media. See Realtime operations. - Confirm RLS is
forced on every realtime-published table (RLS doubles as channel auth). - Verify that
frame_eventshas no UPDATE or DELETE policies — append-only is a V1 invariant.
Upstream: Realtime authorization, Postgres changes.
Phase 5 — Supabase Edge Functions
Section titled “Phase 5 — Supabase Edge Functions”- Deploy Edge Functions for the Agent API ingress (
frame-submissions,media-uploads), Stream webhook receiver, and signed-URL minters. Upstream: functions. - Set Edge Function secrets from the inventory in Secrets & environments:
SUPABASE_SERVICE_ROLE_KEY,API_KEY_PEPPER,CF_*,TURNSTILE_SECRET_KEY. Upstream: function secrets. - Pin the JWT verification mode to require a verified Supabase user JWT for member-facing functions, and to accept bearer API keys only on the Agent API ingress functions.
Phase 6 — Cloudflare account scaffolding
Section titled “Phase 6 — Cloudflare account scaffolding”- Get
CF_ACCOUNT_IDfrom the Cloudflare dashboard. - Create scoped API tokens with the minimum permissions per row in Secrets & environments: one for Images write (
CF_IMAGES_TOKEN), one for Stream write + Direct Creator Uploads (CF_STREAM_TOKEN). Never reuse one token across products. - Store both tokens as Edge Function secrets only. They must never reach the Astro build env or the browser.
Phase 7 — Cloudflare Images
Section titled “Phase 7 — Cloudflare Images”- Enable
requireSignedURLs: trueas the account default. - Generate
CF_IMAGES_SIGNING_KEYand add it to Edge Function secrets. - Declare the V1 named variants (
thumb-256,card-768,frame-1920,og-1200x630) — declared in project config, not inferred from request shape. - Confirm delivery URLs are minted server-side with
exp ≤ 1h. See Media & delivery.
Upstream: Images overview, serve private images, variants.
Phase 8 — Cloudflare Stream
Section titled “Phase 8 — Cloudflare Stream”- Generate
CF_STREAM_SIGNING_KEYand add it to Edge Function secrets. - Generate
CF_STREAM_WEBHOOK_SECRETand configure the webhook target at the Supabase Edge Function URL forstream-webhook(e.g.https://<project-ref>.supabase.co/functions/v1/stream-webhook, or theapi.ui.plan.ai/webhooks/streamalias once Phase 10 step 4 binds it). Upstream: webhooks. Payload contract: Stream webhook payload. - Confirm Direct Creator Uploads are created with
requireSignedURLs: true, configuredmaxDurationSeconds, andallowedOriginsmatching the env’s domains. Upstream: direct creator uploads. - Confirm playback uses signed JWT tokens with
exp ≤ 1h. Upstream: securing your stream.
Phase 9 — Cloudflare Turnstile
Section titled “Phase 9 — Cloudflare Turnstile”- Create a Turnstile widget for each environment. Add
TURNSTILE_SITE_KEYto CF Pages public env,TURNSTILE_SECRET_KEYto Edge Functions only. - Wire the widget into the browser login form; Edge Function verifies the token before requesting an OTP. See Auth & sessions.
Upstream: Turnstile.
Phase 10 — Cloudflare Pages (the Astro app) + Agent API hostname
Section titled “Phase 10 — Cloudflare Pages (the Astro app) + Agent API hostname”The Astro app and the Agent API live on different origins: the app is Cloudflare Pages; the Agent API is Supabase Edge Functions. Do not map api.ui.plan.ai as a Pages custom domain.
- Connect the GitHub repo to Cloudflare Pages. Build command
pnpm build, output dirdist. Node pinned via.node-version. Upstream: build configuration. - Set public env vars on Pages (
SUPABASE_URL,SUPABASE_ANON_KEY,TURNSTILE_SITE_KEY) per environment. Upstream: env vars. - Map the Pages custom domains —
ui.plan.ai(prod) andpreview.ui.plan.ai(preview). Upstream: custom domains. - Bind
api.ui.plan.aito Supabase Edge Functions via Supabase Auth’s custom-domain feature (the only Supabase-supported path for a vanity Edge Functions hostname). This is a Cloudflare DNS-only CNAME record at the Cloudflare zone level — never a Pages custom domain. Until that binding exists, agents and webhooks call the rawhttps://<project-ref>.supabase.co/functions/v1/<fn>URL. Upstream: Supabase custom domains. - Decide whether to ship a
_headersbaseline from Secrets & environments. The current repo has no_headersfile, so adding one must update the agent/deployment docs in the same change. Upstream: Pages headers.
Phase 11 — Smoke verification
Section titled “Phase 11 — Smoke verification”Top-to-bottom acceptance for the environment:
- Auth — request OTP, complete MFA enrollment, land in the workbench.
- API key — create a test key as
owner, capture the one-time raw token, confirm onlyprefix+hashpersisted. - Frame submission —
POST /v1/frame-submissionswith multipart PNG; receive 202; see theframe.submission.createdrow inframe_events; the workbench updates via Postgres Changes. - Image variant — workbench renders the frame from a signed Cloudflare Images URL (not from the Supabase original).
- Large video —
POST /v1/media-uploads; upload directly to the returned Stream URL; the Stream webhook updatesframe_media.statusand emitsframe.media.readyon success orframe.media.failedon error (intermediate transitions emitframe.media.status_changed). - Rate limit — exceed the per-key bucket; receive
429 rate_limitedwith aRetry-Afterheader. - Observability — every response carries
X-Request-Id; that ID appears in Edge Function logs and on the frame detail screen.
If any step fails, the bug is upstream of this checklist — fix the V1 plan page first, then re-run.
What you do not do during wiring
Section titled “What you do not do during wiring”- Do not create OAuth providers (Google, GitHub) in Supabase Auth — V2.
- Do not ship
_headerswithout updating the repo invariants inAGENTS.mdand the deployment skill. - Do not add tables to the realtime publication beyond the four listed.
- Do not put the service role key or any signing key in CF Pages env — Edge Functions only.
- Do not edit schema in Supabase Studio for
previeworprod— migrations only.