Skip to content

Verified, restorable PostgreSQL backups#976

Open
atomantic wants to merge 18 commits into
mainfrom
feat/verified-pg-backup
Open

Verified, restorable PostgreSQL backups#976
atomantic wants to merge 18 commits into
mainfrom
feat/verified-pg-backup

Conversation

@atomantic
Copy link
Copy Markdown
Owner

Summary

PortOS treats PostgreSQL as an optional accelerator (Memory + creative catalog live there; everything else is file-based, and pgMode='file' runs with no DB at all). Every backup snapshot already dumped Postgres alongside the rsync of data/, but the dump result was silently swallowed — a missing/failed pg_dump reported status: 'ok' with a green file count, the dump was unverified and unhashed, there was no restore path, and the UI never surfaced DB-backup state. This makes the DB data PortOS already stores genuinely recoverable.

This is backup-mechanism only — no on-disk data/ format change, no data migration, and Postgres stays optional (skipped is a first-class, benign outcome).

What changed

  • dumpPostgres returns an explicit, verified statusok (with sizeBytes + tableCount) / skipped (no PG — file mode, benign) / failed (pg_dump_missing | dump_error | empty_dump). A 0-byte/truncated dump that exits 0 is now caught.
  • runBackup propagates the outcome — a failed dump degrades the backup to a new degraded status (files still saved); skipped/ok stay ok. The full result is persisted as pgBackup in backup state, and the dump is hashed into the snapshot manifest.
  • Loud on failure — a degraded dump raises a warning-severity error event (BACKUP_DB_DUMP_FAILED) that toasts even on unattended scheduled runs (via a new getIo() accessor), and the dashboard BackupWidget flags degraded as a warning.
  • restorePostgres + POST /api/backup/restore-db — dry-run by default, same path-traversal guard as restoreSnapshot, refuses a real restore when the DB is unreachable (no half-restore).
  • BackupTab UI — DB-backup status block, degraded banner, snapshots list with a per-snapshot Restore DB action behind a confirmation modal (uses the shared ui/Modal, dry-run preview then confirm).

Design docs

  • Spec: docs/superpowers/specs/2026-06-05-verified-pg-backup-design.md
  • Plan: docs/superpowers/plans/2026-06-05-verified-pg-backup.md

Test Plan

  • Server suite: 10786 passed, 8 skipped (527 files) — incl. new backup.test.js (dump status branches, degraded mapping, restore) + routes/backup.test.js (restore-db route + validation)
  • Client suite: 1634 passed (156 files)
  • ESLint clean on all changed client files; client build succeeds
  • Local code-review gate passed; holistic review caught & fixed the severity:'warning'-dropped-toast seam
  • Manual: trigger a backup with pg_dump missing → expect degraded status + warning toast; Restore DB dry-run preview → confirm

Known follow-up

  • runBackup integration test (the pg-status wiring is currently covered by the pure backupStatusForPg helper + code-reading) — recorded in the plan doc.

atomantic added 18 commits June 5, 2026 13:50
…ore, DEFAULT_STATE.pgBackup, restore-path tests
…n dump for live-DB restore, unlink partial dumps, degraded in City vault
…_BACKEND=postgres) is unreachable; defer manifest-hash restore-verify
…arning in review queue, not high-severity failure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant