From abdcefe764ef403ded5fb4e63d67de10d70e73d9 Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Thu, 20 Aug 2026 10:24:42 -0700 Subject: [PATCH] docs: make the backup guide and capability page match the binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated reference was current — the field pages, error table, CLI page and schema all regenerate with no diff. The hand-written pages had drifted from a day of changes. The guide's transcripts showed output the binary no longer prints: one schedule line where enablement installs two, and a drill result without the point it recovered to or what answered. Both are copied from a live run now. It also documented neither `ob backup status` nor `ob backup verify`, which is most of what an operator does between enabling and needing a recovery. Status now prints the declared window and maximum data loss beside what the repository actually holds, says plainly when the history does not reach back that far yet, and lists any drift in the server's own archiving — none of which was described anywhere. Verify's failure is worth showing, because a green backup and a gapped WAL stream look identical until a restore. Moving a repository is new user-visible behaviour and now has a section, as does the base-backup selection a `--to` recovery performs. The capability page claimed "no drill is run, and no service can currently prove a backup would restore". That has not been true since drills became executable. What is still true — and now says so — is that no *unattended* drill runs and no proof age is recorded. Co-Authored-By: Claude Opus 5 --- .../docs/guides/back-up-a-database.mdx | 73 ++++++++++++++++++- site/src/content/docs/status/capabilities.mdx | 10 ++- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/site/src/content/docs/guides/back-up-a-database.mdx b/site/src/content/docs/guides/back-up-a-database.mdx index e6f2545..a7a8719 100644 --- a/site/src/content/docs/guides/back-up-a-database.mdx +++ b/site/src/content/docs/guides/back-up-a-database.mdx @@ -53,6 +53,7 @@ $ ob backup enable database ✓ backup runtime wal-g v3.0.8 (aarch64) ✓ service database → backup schedule: ob-backup-shop-production-database-backup at 0 2 * * * +→ backup schedule: ob-backup-shop-production-database-verify at 0 4 * * * ✓ backup database ``` @@ -95,6 +96,70 @@ generations, or enough of them to span the window at your schedule's rate: `ob backup prune` prints the number it is using before it costs you storage. +## What the repository can recover + +Every figure comes from the repository, not from the project — and the policy is +printed beside it, because "is my promise being kept" is the question you came +with. + +```console +$ ob backup status database +service database +repository s3://onebox-backups/shop_database +recoverable to 2026-08-19T17:31:44Z or later, as far as the archived WAL reaches +history 2026-08-12T02:00:11Z onwards; declared window 7d: covers the declared window +data loss at most 15m declared; the write-ahead log is archived continuously and every drift in that is listed above + +BACKUP COMPLETED FROM WAL +base_000000010000000000000007 2026-08-19T02:00:09Z 000000010000000000000007 +``` + +A repository younger than its window says so rather than implying otherwise: +`shorter than the declared window — this repository does not reach that far back +yet`. That is not a fault on a service enabled this morning, but it is not the +promise either, and only the report can tell you which one you are looking at. + +Status also asks the server whether it is still archiving. A correct wal-g +binary on the host says the tooling is in place; it says nothing about +`archive_mode` still being on or `archive_command` still being the one Onebox +installed. Anything that drifted is printed as a `drift` line above the figures. + +## The archive check + +A base backup plus a gapped WAL stream recovers to the backup and no further — +a nightly snapshot wearing the label of point-in-time recovery. `ob backup +verify` is the check for that, and the timer from `drill.schedule` runs the same +one unattended. + +```console +$ ob backup verify database +✓ verify database archive +``` + +A repository with a hole in it fails and names the range: + +```console +$ ob backup verify database +✗ ob: the archived WAL has 1 gap(s) — 00000001000000000000002B..00000001000000000000002B missing_lost. A base backup plus a gapped WAL stream recovers to the backup and no further, so any point after the first gap is not recoverable +``` + +A segment the server may still be uploading is not counted as a gap; it resolves +itself and is reported on the next run if it does not. + +## Moving the repository + +Editing a target's bucket or endpoint, or pointing the service at a different +target, takes effect at the next `ob backup enable` — and Onebox says what that +means: + +```console +→ database now archives to backup target "s3://elsewhere/shop_database". What "s3://onebox-backups/shop_database" holds is untouched, but this repository starts from the backup being taken now, so the declared recovery window begins here +``` + +Everything else — a restore, a drill, status, retention — keeps reading the +repository the service was actually bound to, so editing a target cannot +silently point a recovery at a repository the history is not in. + ## Prove it restores ```console @@ -102,7 +167,7 @@ $ ob backup drill database ✓ recovery: fetch base backup ✓ recovery: replay to the newest recoverable point ✓ recovery: verify the recovered cluster answers -✓ drill passed: database recovered from base_000000010000000000000007 and answered. Nothing was changed. +✓ drill passed: database recovered to the newest recoverable point from base_000000010000000000000007 and answered (4 tables in public schema). Nothing was changed. ``` A drill recovers into a throwaway volume, proves the cluster opens and answers a @@ -133,7 +198,11 @@ The service name is typed back with `--confirm` because a recovery has no plan for the approval flow to bind to, so the guard is the name of the thing being replaced. -Without `--to`, recovery goes to the newest recoverable point. +Without `--to`, recovery goes to the newest recoverable point. With `--to`, +Onebox picks the newest base backup that finished at or before your point — +replay only moves forward, so a later base can never reach an earlier moment. A +point older than everything in the repository is refused, and the refusal tells +you when the oldest backup finished. ## Stopping diff --git a/site/src/content/docs/status/capabilities.mdx b/site/src/content/docs/status/capabilities.mdx index 29ec1ff..49c9895 100644 --- a/site/src/content/docs/status/capabilities.mdx +++ b/site/src/content/docs/status/capabilities.mdx @@ -101,8 +101,14 @@ the backup reference. Only the `postgres` driver has an executable contract today; every other driver declares `policy_qualified: false` and its backup policy is refused rather than accepted and ignored. -**Restore proof.** No drill is run, and no service can currently prove a backup -would restore. +**Restore proof.** `ob backup drill` proves it on demand: it recovers the +repository into a throwaway volume, waits for the cluster to promote, and makes +it answer a query — the same code a real restore runs, stopped before the last +step. What is *not* run is an unattended drill. Onebox puts no agent on the +host, so the timer from `drill.schedule` runs the archive-continuity check +instead, and the full proof is yours to run from CI on the cadence your policy +declares. No proof age is recorded, so nothing tells you the last drill has gone +stale. **Log rotation.** Container logs are not bounded by Onebox today.