From 81eaf1a9a25908708837074cda97cd2f14ee72dc Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:41:29 -0700 Subject: [PATCH 1/2] docs(selfhost): document a real backup/restore drill result (#1821) Ran the existing verify-backup.sh scratch-restore flow against a real production Postgres backup: restored into a throwaway, network-isolated scratch database (never the live one, confirmed by the script's own identity check), repopulating all 84 tables with full row counts intact. Documents why the drill stops at DB-level restore rather than a full app boot to /ready -- that endpoint also gates on live Redis, Qdrant, AI provider, Codex auth, and a real GitHub App key, and reproducing those for a disposable scratch instance would mean copying real credentials into new throwaway infrastructure. --- .../docs.self-hosting-backup-scaling.tsx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx index 422d53b5f2..dedf12482e 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx @@ -156,6 +156,30 @@ docker compose --profile backup run --rm backup sh /verify-backup.sh /backups/po can afford to drop. The script refuses to run when that URL equals the live backup source. +
+ This exact flow was run against a real production backup on a live instance (2026-07-04): + the newest Postgres dump was restored into a throwaway, network-isolated scratch database (a + separate container, never the live one), which the script's own identity check confirmed was + distinct from the backup source before touching anything. The restore completed cleanly and + repopulated all 84 application tables, including the largest operational tables with their + full row counts intact (hundreds of thousands of rows in the biggest tables) — not just an + empty schema. +
+
+ This proves the backup content and the restore path both work end-to-end against real data.
+ It deliberately stops short of booting a full app instance against the scratch database and
+ polling /ready: that endpoint also gates on live Redis, Qdrant, the configured
+ AI provider, Codex auth, and a real GitHub App key (see{" "}
+ Operations's health endpoints section) —
+ reproducing all of those for a disposable scratch instance would mean copying real
+ credentials into new, throwaway infrastructure, which is a bigger risk than the drill is
+ worth. In an actual disaster recovery, the restored database is loaded onto trusted
+ infrastructure the operator already controls, using their own real credentials, so once the
+ db readiness check passes (which this drill proves the restored data supports),
+ the remaining checks reflect the same subsystems' live state as any other boot.
+
After scaling, revisit Operations and{" "} Security because network and credential From 7604c9827fcdb14faa6b34d2f8483ca7ba6045b9 Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Sat, 4 Jul 2026 16:00:48 -0700 Subject: [PATCH 2/2] fix(docs): stop overclaiming app-readiness proof from the restore drill The drill only exercised the database restore path (pg_restore into a scratch database), not the app's own db readiness probe, migration boot path, or /ready response -- the prior wording implied otherwise. Also names the exact backup file used and frames the table/row counts as a point-in-time result rather than an invariant. --- .../docs.self-hosting-backup-scaling.tsx | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx index dedf12482e..117ab0b10b 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx @@ -158,13 +158,15 @@ docker compose --profile backup run --rm backup sh /verify-backup.sh /backups/po
- This exact flow was run against a real production backup on a live instance (2026-07-04):
- the newest Postgres dump was restored into a throwaway, network-isolated scratch database (a
- separate container, never the live one), which the script's own identity check confirmed was
- distinct from the backup source before touching anything. The restore completed cleanly and
- repopulated all 84 application tables, including the largest operational tables with their
- full row counts intact (hundreds of thousands of rows in the biggest tables) — not just an
- empty schema.
+ This exact flow was run against a real production backup on a live instance on 2026-07-04
+ (backup gittensory-20260704T090939Z.dump): the dump was restored into a
+ throwaway, network-isolated scratch database (a separate container, never the live one),
+ which the script's own identity check confirmed was distinct from the backup source before
+ touching anything. The restore completed cleanly and, at the time of this drill, repopulated
+ all 84 application tables, including the largest operational tables with their full row
+ counts intact (hundreds of thousands of rows in the biggest tables) — not just an empty
+ schema. Table and row counts will grow over time; treat them as a point-in-time result, not
+ an invariant.
This proves the backup content and the restore path both work end-to-end against real data.
@@ -174,10 +176,11 @@ docker compose --profile backup run --rm backup sh /verify-backup.sh /backups/po
Operations's health endpoints section) —
reproducing all of those for a disposable scratch instance would mean copying real
credentials into new, throwaway infrastructure, which is a bigger risk than the drill is
- worth. In an actual disaster recovery, the restored database is loaded onto trusted
- infrastructure the operator already controls, using their own real credentials, so once the
- db readiness check passes (which this drill proves the restored data supports),
- the remaining checks reflect the same subsystems' live state as any other boot.
+ worth. This drill proves the dump can be restored and its contents inspected at the database
+ layer — it does not exercise the app's own db readiness probe, migration boot
+ path, or /ready response. A full disaster-recovery rehearsal still needs to
+ verify app readiness on the target infrastructure, using the operator's own real
+ credentials.