De-Supabase Migration — Cloudflare-Only Product Plane
Status: SUPERSEDED MIGRATION SNAPSHOT. The no-new-Supabase direction
remains valid, but the Cloudflare-only/D1 destination does not. Current target:
Neon for canonical relational truth, R2 for evidence bytes and D1/KV only for
bounded reconstructable operational state. Use
../architecture/IMPLEMENTATION_CONTRACT_v1.1.md.Original status: TRACKED / IN PROGRESS
Original authority: AGENTS.md DECISION 22 (2026-06-12) — 100% Cloudflare product plane.
Rule: This is the single tracker for removing Supabase from the product. Update it IN PLACE
as services migrate. Do NOT spawn sibling migration docs. Do NOT add new Supabase usage.
Honesty note: Doctrine = Cloudflare-only. Code = NOT there yet. This doc tracks the gap.
Baseline (measured 2026-06-12)
~71 source files reference Supabase across services + packages (excluding node_modules/dist/tests).
The good news: most go through two client chokepoints, so this is far less than 71 independent rewrites.
Chokepoints (migrate these first — everything else funnels through them)
| File | Role | Becomes |
|---|---|---|
packages/db/src/supabase.ts | createDbClient / createAdminClient (service-role, schema hub, exec_sql RPC) | A D1-backed adapter exposing the same surface, OR callers move to D1 bindings |
packages/supabase/src/{client,server,native}.ts | Browser/SSR auth + db client | DELETE — app auth is already CF Gateway credential auth (DECISION 20); product reads D1-direct |
packages/db/src/index.ts | re-exports db client | Re-point to D1 adapter |
Access patterns found (what each call actually is)
createDbClient/createAdminClient(...).from(table).select/insert/update— relational reads/writes → D1 (env.SPINE_CACHE_DBor service D1 binding).- Direct REST
fetch(${SUPABASE_URL}/rest/v1/...)(e.g.services/think/src/tenant-config.ts) → D1 query againsttenant_spine_config(already partly on D1 — see migration 033). .rpc("exec_sql", ...)(raw SQL helper, already@deprecated) → D1 prepared statements (parameterized). Remove the RPC path.- pgvector similarity (memory/knowledge search) → Vectorize or AI Search (
integratewise-knowledge). - Browser auth (
packages/supabaseclient) → already replaced by CF Gateway credential auth (DECISION 20). Just remove dead imports.
Migration phases (by blast radius, smallest first)
Phase 0 — Stop the bleeding (no behavior change)
- Add
supabaseto the doctrine drift-check ratchet (done —scripts/check-doctrine.mjs). New Supabase usage now fails CI. - 🚨 Rotate the Supabase service-role key exposed in
IW-handoff-20260611-213600.md+ scrub from.env/.dev.vars(manual — founder).
Phase 1 — Already-D1 paths (verify + delete Supabase fallback)
Gateway/workspace already read D1-direct. Confirm no Supabase fallback remains.
-
services/gateway/src/index.ts(7 refs) — confirm D1-only; drop Supabase fallback. -
services/think/src/tenant-config.ts(8) —tenant_spine_configis on D1 (migration 033) → swap REST fetch → D1. -
services/pipeline/src/index.ts(14) +spine/index.ts(16) — pipeline writes the Spine; confirm D1 partitions (${domain}_data) are the only write target.
Phase 2 — Memory & knowledge (pgvector → Vectorize/AI Search)
-
services/knowledge/src/{index,iq-hub,memory-consolidator}.ts,search/service.ts,embedding/storage.ts— move pgvector → AI Search (integratewise-knowledge) / Vectorize. -
services/intelligence/src/memory/morning-context-builder.ts— read memory from D1/KV/AI Search. -
services/continuity/src/{index,supabase}.ts— conversational/org/personal memory → D1 + DO SQLite.
Phase 3 — Reasoning & governance
-
services/think/src/{cognitive-brain,index}.ts(70 + 29) — largest single files; cognitive reads → D1/KV/AI Search. -
services/govern/src/proposal.ts,services/act/src/index.ts— proposals/executions → D1 (+ TenantBrainDO already holds approvals/executions). -
services/mcp-connector/src/handlers/tools.ts(58) +index.ts— MCP tool reads → D1/AI Search.
Phase 4 — Connectors, tenants, billing, workflow, admin
-
services/connector-sync/src/index.ts(20),services/connector/src/token-refresh.ts— sync cursors/tokens → D1/KV (tokens stay in Nango). -
services/tenants/src/{index,context,oauth-configs}.ts— tenant/RBAC → D1. -
services/billing/*(42+18+11) — billing records → D1 (Stripe/Razorpay remain source of truth for payments). -
services/workflow/src/*(48+others) — workflow state → D1 + DO. -
services/admin/*— admin reads → D1. -
packages/rbac/src/engine.ts(9),packages/lib/src/{audit,cache,neon}.ts— shared libs → D1/KV.
Phase 5 — Delete the clients
- Delete
packages/supabase/*andpackages/db/src/supabase.tsonce no importers remain. - Remove
SUPABASE_*from allwrangler.toml/.dev.vars/.env. - Remove
@supabase/supabase-jsfrompackage.jsons. - Flip the drift-check baseline for
supabaseto 0 (hard-fail on any reintroduction).
Deprecated services note
services/{normalizer,store,loader,think,act,govern} are pre-consolidation reference workers (per AGENTS.md). Their Supabase refs are lower priority — confirm they're truly inert before spending effort; the live behavior lives in pipeline, connector, intelligence.
Definition of done
grep -rl "supabase" services packages --include=*.ts | grep -v node_modules | grep -v test→ 0- No
SUPABASE_*vars in any wrangler/env file. @supabase/supabase-jsabsent from allpackage.json.- Drift-check
supabasebaseline = 0.