1. Set up Supabase
Everything the web UI and agent need — schema, storage bucket, realtime policies, and a login user — is created by one script, in one run, against a fresh project.
Create a project
Create a project at supabase.com/dashboard . The free tier is enough. A fresh, empty Postgres is what the script expects.
Collect four values
From Project Settings → API Keys:
- Project URL —
https://[REF].supabase.co - Publishable key —
sb_publishable_… - Secret key —
sb_secret_…(keep it secret; create one if none is listed)
Older projects show anon and service_role instead — the legacy names for the same two slots. Pass them in the same variables. Projects created after 2025-11-01 only get the new format.
From Settings → Database → Connection string, a Postgres URL. Pick one:
| Path | When | Form |
|---|---|---|
| Direct | Your machine has IPv6 | postgresql://postgres:[PW]@db.[REF].supabase.co:5432/postgres |
| Pooler (session mode) | IPv4-only networks | postgresql://postgres.[REF]:[PW]@aws-[REGION].pooler.supabase.com:5432/postgres |
Use session mode (port 5432), not transaction mode (6543) — the init schema’s session settings must persist across its statements.
Run the script
You need psql and curl on your machine, and a clone of the repo.
SUPABASE_URL=https://[REF].supabase.co \
DB_URL="postgresql://postgres:[PW]@db.[REF].supabase.co:5432/postgres" \
SECRET_KEY=sb_secret_... \
PUBLISHABLE_KEY=sb_publishable_... \
./scripts/setup-supabase.shThe script is idempotent — every statement is if not exists /
on conflict do nothing, so re-running is always safe and is the standard fix
for a half-finished setup.
Keep the output
It prints two blocks: a WEB env block and an AGENT/CLI config block. You need both in the next two steps.
What it created
- The schema — tables, RPCs, indexes, triggers, RLS, grants, and the realtime publication.
- The
imagesstorage bucket + its RLS policy, so agents can offload image bytes instead of broadcasting them. - Realtime private-channel policies. A fresh project ships
realtime.messageswith RLS on and no policies, soprivate:{userId}channels would fail to join. These authorize each user to their own topics. - A confirmed login user — email/password, so you can sign in without configuring any OAuth provider.
- A JWT signer probe — it detects whether your project verifies ES256 or the legacy HS256 secret, and prints only the line you need.
Expected noise. Step 1 prints ~20 WARNING: no privileges were granted for "…similarity…" lines. Harmless: pg_trgm lives in public, so the
blanket grant sweeps functions that belong to the extension rather than to
you. Trigram search still works.
Deliberately left out
Not gaps — decisions. OAuth providers, SMTP, email templates, and redirect URLs are dashboard settings with no SQL surface; the seeded email/password login exists precisely so none of them block first use. No row data is seeded.
Reference: the full walkthrough — every input, the API-key generations and
why they are not just a rename, and the failure mode of each step — is in
docs/reference/setup-supabase.md.
The schema contract itself is
docs/reference/database.md.