All documentation

Backup Strategy

What's backed up, how, and — just as importantly — what is NOT backed up by anything in this system and must be handled separately. See docs/DISASTER_RECOVERY_PLAN.md for what to do when a restore is actually needed; this document is about the ongoing strategy that makes that possible in the first place.

What Supabase Cloud backs up

The Postgres database — every table this app's migrations create, including trades, positions, copy_relationships, broker_connections (encrypted credentials included, as ciphertext), subscriptions, audit_logs, everything. Managed entirely by Supabase, not this application's code:

  • Point-in-time recovery (PITR), on plans that include it — restore

to any moment within the retention window, not just a daily snapshot.

  • Daily snapshots, on every plan tier — retention window varies by

plan.

  • **Verify your actual project's tier and retention window in the

Supabase Dashboard** (Database → Backups) — this document intentionally does not state a specific retention period, since it depends on a plan choice this codebase has no visibility into and stating one here would risk being wrong for your specific project.

This app has no application-level backup logic layered on top of Supabase's own (no export cron, no secondary database) — Supabase Cloud *is* the backup strategy for data, by design (see docker-compose.yml's own comment on why this project doesn't bundle a second, self-hosted Postgres instance either).

What is NOT backed up by Supabase — and whose job it is

CREDENTIAL_ENCRYPTION_KEY — the single most important thing in this list

Every broker credential stored in broker_connections.encrypted_credentials is AES-256-GCM ciphertext (src/lib/security/credential-crypto.ts) — meaningless without this one key, which lives only in a server-side environment variable, never in the database. This is deliberate: a database-only compromise (a leaked service_role key, a SQL injection, even a full Supabase-side breach) does not expose plaintext broker credentials, *because* the decryption key was never there to steal.

The direct consequence: a database backup restores the ciphertext, not the ability to decrypt it. If CREDENTIAL_ENCRYPTION_KEY is lost — not leaked, *lost* — every stored broker credential becomes permanently unrecoverable, independent of how good the database backup is. Every affected user would need to reconnect their broker accounts from scratch.

What to actually do: back this value up in a secrets manager or password manager, outside source control, outside the database, separately from any infrastructure-level backup. Treat losing it as equivalent in severity to losing every user's broker credentials at once — because it is.

Application code

Git is the backup — every deployed version is a commit, and docs/DISASTER_RECOVERY_PLAN.md's Procedure B covers rolling back to any prior one. No separate code-backup mechanism exists or is needed beyond a git remote you don't lose (e.g. GitHub, not only a local clone).

Other server-only secrets

SUPABASE_SERVICE_ROLE_KEY, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, METAAPI_TOKEN, alert-channel credentials (TWILIO_AUTH_TOKEN, TELEGRAM_BOT_TOKEN, RESEND_API_KEY, ...) — all regeneratable from their respective provider dashboards if lost (unlike CREDENTIAL_ENCRYPTION_KEY, which is *not* regeneratable without losing access to already-stored data). Still worth keeping in a secrets manager for operational continuity, but losing one of these is an inconvenience (rotate it, update the env var, redeploy), not a data-loss event.

In-memory/per-instance state

Live connector sessions, the observability metrics/recent-logs registries, and the Copy Engine's in-process queue state are explicitly not persisted anywhere and not meant to be — they're reconstructed automatically when a process (the web app or the Copy Engine worker) restarts, from the durable state that *is* in the database. Losing these on a restart is expected behavior, not a backup gap — see docs/ARCHITECTURE.md and DEPLOYMENT.md §11 Scaling for the full "what's per-instance vs. durable" breakdown.

Backup verification

Not independently tested by this codebase — there is no automated job here that periodically attempts a restore to confirm backups are actually valid and restorable (a real, common gap in backup strategies generally, not unique to this project, and worth naming rather than implying it's covered). Supabase's own backup infrastructure is a managed service with its own internal guarantees, but a genuine "restore drill" — actually restoring to a scratch project and verifying the result — has not been performed as part of this pass. See the Disaster Recovery Plan's "Rehearsal" section and the Beta Launch Checklist.

Summary table

DataBacked up byRecoverable if lost?
Database (all tables)Supabase Cloud (PITR/snapshots)Yes, within retention window
Broker credentials (ciphertext)Supabase Cloud (as part of the DB)Yes — but useless without...
CREDENTIAL_ENCRYPTION_KEYNothing in this system — your responsibilityNo — not from any backup. Back it up separately.
Application codeGitYes, any prior commit
Other server secretsRegeneratable from provider dashboardsYes, with rotation effort
In-memory/per-instance stateNothing (not durable, not meant to be)N/A — reconstructed on restart