Skip to content

Backup and restore

Duckbill™ backs up application-owned state. Providers can re-fetch message bodies. Rules, corrections, classifications, attention, audit, and sync metadata cannot (MAIL-46 / MAIL-56 / ADR 0026).

Objectives

ObjectiveMVP target
RPO≤ 24 hours for application-owned persistent state
RTO≤ 4 hours for a recoverable application/database failure

These are release targets, not promises about Microsoft or other providers.

What is backed up

What is skipped

Losing encrypted tokens means reconnecting the mailbox. That is preferred to copying secrets into backup files.

Recovery mode

After a restore:

  1. Enter recovery mode (ops_recovery_mode plus a GLOBAL automation pause).
  2. Provider mutations stay paused.
  3. Run reconciliation and validate checkpoints.
  4. Confirm rules and classifications.
  5. An operator clears recovery before write-back resumes.

assertMutationsAllowed fails closed while recovery is active.

How to run a backup

npx tsx scripts/backup-restore.ts export --out /tmp/duckbill-app-state.json

Requires DATABASE_URL. The JSON file must be stored as a secret artifact, not committed.

How to restore (runbook)

  1. Provision a disposable or recovered Postgres and apply migrations (npm run db:migrate).
  2. Restore users, connected accounts, and mail identity rows from the platform backup or reconnect.
  3. npx tsx scripts/backup-restore.ts restore --in /tmp/duckbill-app-state.json
  4. Confirm recovery mode is active and mutations are paused.
  5. Run reconciliation for each account.
  6. Clear recovery only after attention, rules, and audit match the checklist.

Restore rehearsal

CI exercises the path on ephemeral PGlite with fixtures/ops/backup-restore-sample.json. The recorded checklist is fixtures/ops/restore-rehearsal-checklist.json. That fixture is synthetic. Production mailbox content is forbidden.

Related

Host deploy order and recovery-mode gates are in deploy and operate. Formal NFR restore evidence is owned by MAIL-109.