Replace the Supabase application data plane with the Node/PostgreSQL API, migrate mobile, admin, web, and MCP callers, add controlled cutover tooling, and define hardened Dokploy infrastructure.
4.5 KiB
Standalone backend migration
Sauci's standalone Node API, PostgreSQL data plane, worker, and private filesystem media store are implemented; production cutover is still pending. Hosted Supabase remains only as the identity provider: the mobile and admin applications obtain a Supabase access token, and the API verifies that token against the project's public JWKS. Product data, files, realtime events, webhooks, scheduled work, admin operations, and MCP tools do not use the Supabase data plane.
Target runtime
api: Node 20, Hono, Drizzle, PostgreSQL 17worker: Node 20 process sharing the API's domain modules and PostgreSQLstorage: private filesystem-backed Docker volume with authenticated uploads and API-issued, expiring HMAC download URLs; the metadata/deletion queue lives in PostgreSQLrealtime: authenticated polling, with optimistic concurrency for collaborative stateauth: hosted Supabase Auth only. A server-only service-role credential is used solely for the Auth Admin account-deletion endpoint, never for database, Storage, Realtime, or Functions access.
The API derives every user identifier from the verified bearer token. Mobile-supplied user IDs are never an authorization boundary.
Delivery slices
| Slice | Backend | Mobile proof | Status |
|---|---|---|---|
| Identity | profiles, feature interests | bootstrap and interest hook tests; native launch | implemented |
| Couples | create/join/read/leave relationship | pairing and profile state | implemented |
| Catalogue | categories, packs, enabled packs, progress | pack list and toggling | implemented |
| Answers | questions, responses, matches, streaks | swipe/answer/match flows | implemented |
| Chat | messages, reports, deletions, typing/polling | message and typing store tests | implemented |
| Media | avatars, response media, chat media, feedback screenshots, cleanup | upload/view/expiry | implemented |
| Account | settings, notifications, feedback, deletion | settings and account lifecycle | implemented |
| Billing | subscriptions and RevenueCat webhook idempotency | entitlement refresh | implemented |
| Operations | reminders, digests, scheduled releases, moderation and cleanup | worker/integration tests | implemented |
| Admin/MCP | role-scoped management API and audited automation | admin and MCP contract tests | implemented |
Gate for every slice
- Add an idempotent standalone PostgreSQL migration and domain repository tests against a temporary real schema.
- Add API authorization/validation tests, including a second-user isolation test where the domain is user-scoped.
- Move the mobile domain to
apiClient; hosted Supabase remains reachable only through the dedicated Auth client. - Run focused mobile tests, the complete mobile Jest suite, API tests/typecheck/build, and a native simulator flow when the slice is user-visible.
- Do not remove the legacy source or cut production traffic until a stopped-source final sync, row/file parity checks, and application-level staging flows have passed.
Authenticated native acceptance requires an isolated hosted non-production Supabase
Auth project plus an onboarding-complete test user whose Auth subject has a matching profile
in the staging PostgreSQL database. Supply its public URL/anon key to the mobile build and
E2E_EMAIL/E2E_PASSWORD to Maestro at execution time. Never use a production project,
subscriber, service-role key, or production data-plane credential for local or E2E work.
The credential-free e2e/maestro/login-screen-smoke.yaml remains the safe build/login-shell
check when that fixture is unavailable; it is not proof of authenticated API behaviour.
Deployment sequence
Dokploy staging receives separate persistent PostgreSQL and private-media volumes. The API migration job must complete before the API starts. Cutover is deliberately later:
- Export a first production snapshot into staging and compare table counts plus sampled relational invariants.
- Exercise the mobile app against staging while it still uses hosted Supabase Auth.
- Stop product-data writes to the old backend, perform the final delta sync, and repeat parity and app-level tests.
- Switch the mobile/API endpoint. Retain the Supabase data plane read-only until rollback is no longer required; only then reduce the hosted project to the Auth-only footprint.
The executable import, parity, final-sync, and rollback procedure is maintained in
docs/operations/backend-cutover.md.