Repository navigation
Back up and restore an instance (spec 030, #41) - #124
Merged
Merged
Conversation
plainprogrammer
force-pushed
the
030-backup-restore
branch
from
October 23, 2026 22:40
1f8eb17 to
bf33179
Compare
Issue #41, roadmap theme A item 6. Decisions 1 to 3 (mechanism, contents, secrets and sessions) ruled by the maintainer; 4 to 14 are recommendations. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Records the maintainer's 2026-10-21 rulings (decisions 1 to 15; the Kamal restore sequence as option (a)) and fixes M1 to M4 and m1 to m11 of the round 2 review; review.md maps each finding. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The maintainer approved spec 030 on 2026-10-21 with rulings on decisions 1 to 15 (4 to 15 as recommended, the Kamal restore sequence as option (a)); both Mode A reviews' findings are fixed. Status and index only, no change to any requirement. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…o a fresh volume Records the backup mechanism spec 030 rules (decisions 1 to 3, 5 to 7) and the plan's placement of the code in lib/collector/backup, with tasks that don't boot the application. Status Proposed until the maintainer approves the plan. Numbered 0024: origin/main holds up to 0022 and 029-manage-locations takes 0023. The index loses the backup mechanism from its Expected line. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Nine phases in one pull request: the namespace and guards, the file (manifest and archive), the SQLite snapshot, backup:create, backup:restore, the restored instance and upgrade proofs, bin/backup-smoke with its CI step, the documents, and the measurements. With the contracts for the two tasks and the archive, the data-model note (no schema change), research (the secret_key_base trace: tasks that skip :environment read none), and the draft of the separate art-index issue for the coordinator. Not reviewed or run yet. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The plan publishes by hard link then unlink (a rename only where a share can't link), which is what makes "never replace a file" hold. The Proposed status stays. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Fixes the critical (Create's read-back refused its own .partial archive), the three major findings (the D4 boot spec's wrong master key, Phase 0's encryption guard under load_defaults 8.1, an unreadable file or unwritable volume ending in a backtrace) and minors m1 to m13, with the maintainer's rulings of 2026-10-21: the room is checked before the checksum (m3), the Kamal volume stays commented out (m5), AC-3.6's construction is accepted (m6) and the measurement runs on a seeded database outside storage/ (m7). Also fixes the expect_refused sentence check, which refused every message that starts with a file name. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Maps the Opus review's C1, M1 to M3 and m1 to m13 to their fixes and commits, with the maintainer's rulings of 2026-10-21 (m3, m5, m6, m7). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…d its approval The Sonnet review (APPROVE WITH NITS, 4 minor) is mapped in plan-review.md: the catch-all line no longer claims nothing was changed, expect_refused keeps its capital rule, the m3 residue is stated in the map row and choice 3, and Phase 5 says the boot examples first run in CI. The maintainer approved the plan on 2026-10-21 and chose Sonnet for execution. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…he suite's guards Plan step: Step 0.8: Commit feat(backup): add the backup namespace, the volume's definition and the suite's guards Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Plan step: Step 0.6: Write the guards. The extraction script had left the file empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Plan step 1.6 (spec 030 Phase 1). Create_private now sets binmode and the archive reads one byte past the end so zlib checks the gzip footer. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Plan step 2.5 (Phase 2). Snapshot owns every SQLite call of both commands. OrphanRows counts rows, not broken keys. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Plan Phase 3, step 3.5: Backup::Create, the backup:create task and their specs (AC-1.1 to AC-1.9, AC-1.11, AC-1.12, AC-4.1). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Plan Phase 4, steps 4.1 to 4.6: Restore places a verified snapshot on a fresh volume through a temporary file linked into place, ends every session, and refuses with two plain sentences; the backup:restore task. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…the guards Plan step 5.5 (Phase 5): AC-2.2, AC-2.4, AC-2.8, AC-2.9, AC-2.10, AC-3.6, AC-6.2, FR-5 and the D4 boot examples; adds with_restored_instance (step 5.1). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…moke test Plan step 6.7 (steps 6.1 to 6.5): the smoke script, its CI step after the image smoke test, the Kamal backup alias with a commented-out backups volume, and keeping the script out of the image (checked by bin/image-smoke). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…lease check Plan Phase 7, steps 7.1 to 7.8: the README's Backup and restore section and pointers, docs/backup-restore.md, the release check and upgrade note in docs/releasing.md, CLAUDE.md, roadmap item 6 and the SQLite rule. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Plan step 8.1: seeds a database under tmp/measure and times a backup, a restore and the longest write wait (decision 13, AC-1.10). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The maintainer accepted it with the spec 030 plan on 2026-10-21 (plan step 8.5). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Plan step 8.3: evidence per criterion, the seeded-database measurements, the local smoke run, bin/ci twice. The NAS restore (AC-5.6) stays owed to the maintainer; the implementation review is not yet recorded. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…o the VACUUM INTO The writers were threads in the backup's own process, with a window over the whole run, so a backup that blocked them for a second would still pass. They are now two child processes with connections of their own, and the example counts the commits that finished between the new :snapshot_starting and :snapshot_written checkpoints (review minor 1). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The run's heartbeat was 20 minutes old, so the page read it as interrupted by the 15-minute stall rule whatever the restored queue held. At 5 minutes only the JOB_ENDED_GRACE rule for a job the queue no longer has can say so (review minor 2). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… AC-5.2 says bin/backup-smoke's copied-in pass and the README's copied-in command ran the restore with no variable at all; both now carry SECRET_KEY_BASE_DUMMY=1 like the host-directory pass (review minor 4, maintainer ruling Q2). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…lete The restore ends sessions with DELETE FROM sessions and relies on the cascade for bulk_removals and bulk_selections; a table with a plain key to sessions would make it stop with a constraint error. The guard names spec 030 decision 3 and has a negative control (review minor 5, maintainer ruling Q5). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A copy of 2 to 9 bytes starts like gzip and stops, which the contract classifies as incomplete; GzipReader raised on the short header and it was reported as not a backup (review minor 6). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…-journal Backup.remove deleted a -wal and -shm as well; FR-6 names only the journal for backup:restore. The suffixes are now an argument, and the restore passes its two (review minor 7). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… filed Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… owed) Spec 030's areas are release and deploy and data portability, which have no area doc yet (#109, #108), so nothing is written to docs/architecture/. The spec supersedes nothing in a shipped spec. The index row's Plan is "retirement owed" and its status names the implementation review and the NAS restore still owed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…c 029) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…c 029 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
plainprogrammer
force-pushed
the
030-backup-restore
branch
from
October 23, 2026 23:46
bf33179 to
f2dbedc
Compare
plainprogrammer
added a commit
that referenced
this pull request
Oct 24, 2026
The specs index conflicted on neighbouring rows: this branch's 029 row (Shipped, plan retired) is kept, with main's new 030 row after it. Merged, not rebased, so the retired plan's commit 88a52cf keeps its sha (docs/git-convention.md, Merging). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #41. Spec 030 (spec, v1.2.1; plan; verification); ADR 0024.
What it does
An operator backs up a running instance and restores it onto a fresh one, following a documented and tested procedure. This is the self-hoster's own backup; the per-collector export (specs 022, 025, 028) is unchanged, and the README says which to use when.
bin/rails "backup:create[DIRECTORY]"takes a consistent snapshot of the primary database with SQLite'sVACUUM INTOwhile the app keeps writing (ADR 0024). The snapshot is checked (integrity and foreign keys) before it counts, written as onecollector-backup-<UTC time>.tar.gz(a manifest plus the database) under a.partialname, mode 0600 from creation, flushed to disk and linked into place; a final name is never overwritten. The destination is required and is never inside the volume it protects. The command refuses to start without room for the snapshot and the archive.SECRET_KEY_BASEorRAILS_MASTER_KEY.bin/rails "backup:restore[FILE]"runs before the instance's first start, as a one-off, and refuses a volume holding any database or-wal/-shm/-journalfile. It never opens the primary's path or runs a migration; it places the snapshot in rollback-journal mode after every check passes, so a failed or killed restore leaves nothing that the next boot'sdb:preparewould mistake for an empty instance. It clears sessions (everyone signs in again; their cascades are guarded). A backup from a newer release is refused; an older one is migrated bydb:prepareon first start.secret_key_base; a production-boot spec proves it in CI.tarandsqlite3. Kamal gets abackupalias and a commented backups volume with itsinstall -dstep (ruled); its restore sequence is a one-offdocker runof the exact release image afterkamal server bootstrap(ruled), documented and not exercised through Kamal itself.bin/backup-smoke, a new step in the image job on both architectures afterbin/image-smoke: back up a running container, copy the file out, restore it into a fresh container under a differentSECRET_KEY_BASE, and check/up, sign-in, the lots and the recurring tasks. It takes an optional second image so Prove an upgrade from the previous release in CI #42 can reuse it.Evidence
bin/backup-smokebin/cion this branchart_agreement_spec, needsCOLLECTOR_ART_AGREEMENT); also passed in the pre-push hookThe measurement used a seeded database under
tmp/, not a real instance (ruling m7).Reviews
plan-review.md.verification.md: SPEC-ALIGNED (independent Sonnet review 2026-10-22, after fixes).Still owed
verification.mdanddocs/releasing.md.76a1040): the release and deploy and data portability areas have no area doc yet (Area doc: release and deploy #109, Area doc: data portability #108), so nothing was written todocs/architecture/; the plan is retired after the merge.🤖 Generated with Claude Code