Implementation Plan: ANU-7 - Migrate Supabase Postgres data and verify local cutover
Scope
Inventory the current Supabase PostgreSQL source without recording secrets or row contents, export checksummed schema and data artifacts with the Supabase CLI, restore the application-owned database objects into the verified local PostgreSQL 17 and pgvector stack, and prove data and application parity. Switch only ignored machine-local CMS configuration after parity passes. Preserve the source unchanged and keep both the accepted local volume and a tested rollback to the prior Supabase URI.
Product Main inputs and dependencies
- Product Main change:
2026-07-20-switch-to-local-postgres - Product Main source:
docs/changes/2026-07-20-switch-to-local-postgres/index.md - Linear parent:
ANU-5 - Linear implementation child:
ANU-7 - Repository label:
anuvax-cms - Required dependency:
ANU-6, completed - Accepted local baseline: PostgreSQL 17.10, pgvector 0.8.2, pgAdmin 9.16,
Node 22.5.1, and the persistent
anuvax-cms-localCompose volumes
The normalized Linear scope, acceptance, documentation, and verification requirements are authoritative. Cross-repository or product-level decisions remain with Product Main.
Implementation approach
- Extend the guarded database tooling with redacted read-only inventory,
checksum verification, reviewed application-schema selection, targeted
parity, and rollback/cutover evidence. Keep every connection string in a
named environment variable and every artifact below ignored
.local-data/database/<run-id>storage. - Pin and record the Supabase CLI version used for export. Run plan and refusal tests before any connection or restore execution.
- Inventory source PostgreSQL/extension versions, application schemas and roles, tables, constraints, sequences, Payload migrations, Mastra tables, RAG tables, and vector indexes. Record names and counts only.
- Export reviewed application schema and data with the Supabase CLI, checksum every dump, verify the checksums, and prove all artifacts are ignored and absent from Git.
- Create a fresh local migration target inside the accepted Postgres volume, install only required extensions, restore without blanket grants or unreviewed Supabase-managed schemas, and retain the prior local database for recovery.
- Run automated parity plus targeted SQL probes for representative keys, constraints, sequences, Payload migration state, workspace isolation, Mastra tables, RAG chunks, and deterministic vector-query behavior.
- Switch the ignored local CMS configuration to the restored database, then verify the containerized CMS, Payload admin/login boundary, workers, primary application paths, targeted tests, restart persistence, and rollback to the untouched Supabase URI without deleting the local volume.
- Update the implementation log, migration runbook, environment/service and testing documentation, then map exact redacted evidence into the completion report and Product Main synchronization section.
Expected tracked surfaces:
package.jsonandpnpm-lock.yamlscripts/local-stack/database.tsscripts/local-stack/*.sqland any focused migration helperstests/unit/local-stack.test.tsdocs/bootstrap/LocalDevelopmentStack.mddocs/core/TestingAndVerification.mddocs/state/EnvAndServices.mddocs/state/ScriptsAndCommands.mddocs/state/KnownGaps.md- this change folder,
docs/changes/index.md, andmkdocs.yml
Documentation impact
- Add the exact redacted inventory, export, checksum, restore, parity, cutover, failure-handling, restart, and rollback procedure to the local-stack runbook.
- Update environment and command state after the local database becomes the accepted development default.
- Record every executed command by purpose and result without copying database URIs, credentials, row contents, dump contents, or personal data.
- Return preview-only URLs after planning, durable-doc updates, and final completion evidence. Product Main owns canonical publishing.
Verification and acceptance checks
| Check | Planned redacted evidence |
|---|---|
| Source inventory is complete and safe | Inventory JSON with versions, object names, and aggregate counts only; source transaction is read-only |
| Exports are reproducible and private | Supabase CLI version, schema/data dump results, SHA-256 manifest verification, ignore and Git-absence checks |
| Restore contains only reviewed objects | Fresh loopback target, reviewed extension/schema list, restore log review, no blanket privilege changes |
| Structural and data parity | Automated summary plus targeted counts, representative key existence hashes/counts, constraints, sequences, and migration state |
| Workspace, Mastra, RAG, and vector behavior | Cross-workspace negative probes, Mastra relation checks, RAG chunk counts, deterministic vector query |
| CMS and worker behavior | Compose health, Payload/admin and auth boundary probes, stack:verify, worker startup probes, primary flow tests |
| Persistence and rollback | Restart/down-up persistence proof and a demonstrated source-URI rollback that leaves the local volume intact |
| Repository quality | Targeted/full lint evidence, typecheck, integration tests, relevant Playwright projects, strict MkDocs, index validation |
Skipped checks, limitations, and recovery state will be explicit. A dump or a successful restore alone is not acceptance.
Non-goals and rollback
Non-goals:
- changing, pausing, deleting, or writing to the Supabase source
- importing Supabase Auth, Storage, Realtime, Vault, or other managed schemas unless an application-owned dependency is explicitly demonstrated and reviewed
- weakening privileges globally or preserving hosted ownership that the local application does not require
- deleting the accepted local Postgres volume
- production cutover or changes outside
anuvax-cms
Rollback retains the untouched Supabase URI in ignored configuration/evidence, stops local application writes before switching, restores the prior URI to the ignored CMS configuration, and verifies a read-only application health path. The accepted local volume and restored database remain intact for diagnosis or forward recovery.