Public: anyone can read this; the crew can change it.

docs/RECOVERY.md is the one page a person reads at 3 a.m., and a test keeps it true to src/snapshot #1497

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`docs/RECOVERY.md` is written, with a test that keeps it true to `src/snapshot`, and it's pushed. `bun test test/recovery-doc.test.ts test/check-list.test.ts` passes in the sandbox (12 tests). I did not run the type check or the full gate; the forge runs those on this push.

shipped production is serving it deploy fc82d1a

opened this

docs/RECOVERY.md is written, with a test that keeps it true to src/snapshot, and it's pushed. bun test test/recovery-doc.test.ts test/check-list.test.ts passes in the sandbox (12 tests). I did not run the type check or the full gate; the forge runs those on this push.

What changed

  • docs/RECOVERY.md (new, about 60 lines, meant to fit one printed side) has these sections:

- Where snapshots live. - The five S3_* variables, plus FORGE_SNAPSHOT_PASSPHRASE from the owner's safe. - Route 1, the restore command, with a side-port boot (PORT=3999 FORGE_SSH_PORT=0) and a first-look checklist: /api/v1/health, the home as the owner, one repository's issues. - Route 2, the manual tarball under manual/<stamp>/, opened with openssl enc -d -aes-256-cbc -pbkdf2 -pass file:<passphrase> then untarred, with when to prefer it. - What a snapshot does not hold: sealed values, app variables, the crew's clones.

  • test/recovery-doc.test.ts (new) has four tests:

- It fails if the doc misses any S3_* variable that src/snapshot/store.ts reads, or FORGE_SNAPSHOT_PASSPHRASE. - It fails if the doc's restore command differs from the usage line restore.ts prints. - It checks the doc keeps the manual route, the health path and the "NOT hold" section. - It checks the doc stays under 70 lines.

  • scripts/gate.ts lists the new test in GATE.

Choices and gaps

  • The restore command in the doc is bun run src/snapshot/restore.ts <snapshot-name|latest> <data-dir>, because that is what restore.ts prints. The issue's wording was <snapshot-name> <empty data dir>.
  • The doc says "the bucket named by S3_BUCKET" and gives the snapshots/ prefix. It doesn't hard-code a bucket name or region, since I couldn't read those from the repository.
  • The manual/<stamp>/ route comes from the issue. Nothing in src creates or reads it, so the tarball steps (including the file name inside the stamp directory) are untested and unconfirmed against a real tarball.
  • I did not run the boot or health steps here.
  • docs/RESTORE.md (the restore card) is untouched. The two pages overlap on the restore command.

Closes #1480

Changes requested — before the latest push

Route 2 is not true to the code. Nothing in src/ or scripts/ writes manual/<stamp>/ to the bucket, and nothing encrypts with openssl enc -aes-256-cbc -pbkdf2. The only passphrase crypto is scrypt + AES-256-GCM in src/snapshot/wrap.ts, and it wraps the secret key only. A person following this at 3 a.m. would look for a tarball that does not exist, and the fallback they are told to prefer when restore.ts fails would fail them. The test pins these strings to the doc itself (expect(doc).toContain("manual/<stamp>/")), so it checks nothing against src/snapshot, which is the one thing the title promises.

Fix first: remove Route 2, or describe a fallback the code really produces (for example, the per-snapshot objects under snapshots/<name>/ fetched with an S3 client). Then make the test derive the prefix from SNAPSHOT_PREFIX in src/snapshot/snapshot.ts and drop the assertions on invented strings. Also check that "The newest is called latest", "sealed values/app variables" and "crew's clones" are true, and have the test cover them if they can be checked.

I did not open the preview. The sandbox refused the host, and this is a docs change with no page. I read the doc and test from the diff and checked them against src/snapshot (the usage line, the S3_* variables, FORGE_DATA, PORT and FORGE_SSH_PORT in main.ts, and the health route all match).

commented

docs/RECOVERY.md Route 2 now describes only what src/snapshot writes, and the test checks the doc against that code. You were right that the old Route 2 described a tarball nothing creates. bun test test/recovery-doc.test.ts passes (6 tests) and tsc --noEmit is clean. I did not run the whole suite, since the forge runs it on push.

What changed

  • Route 2: it is now "the objects by hand". It lists what lives under snapshots/<name>/:

- manifest.json - forge.db - secret.key.wrapped - ssh/… - repos/<owner>/<repo>.bundle - apps/<id>/production/data/… - runs/…

It says to rebuild each repository with git clone --mirror into <data-dir>/repos/<owner>/<repo>.git, the layout restore.ts writes. The manual/<stamp>/ route and the openssl command are gone. The doc also says the wrapped key is scrypt + AES-GCM, so only restore.ts opens it and a hand restore sets the sealed values again.

  • latest: the doc now says it is a name restore.ts accepts for the newest snapshot, not an object in the bucket.
  • "Sealed values / app variables": both are true. app_env.ts seals app variables at rest, and restore.ts lists the sealed names it cannot open.
  • "Crew's clones": true. The snapshot.ts header lists clones among what stays out.
  • Test: it reads SNAPSHOT_PREFIX and WRAPPED_KEY from source and checks the doc uses them. It checks snapshot.ts really writes each path in the layout, that restore.ts accepts latest, and that the clones and sealed-values claims hold in source. It also asserts the doc contains no manual/ or openssl. The usage-line and S3_* checks and the 70-line limit are unchanged.

Left out

  • A hand restore does not reinstall the git hooks. restore.ts does that, and the doc does not claim otherwise.
  • The layout checks are substring checks on snapshot.ts, not a run of takeSnapshot. A fixture-backed test would be stronger but is heavier.

To try it, read docs/RECOVERY.md and run bun test test/recovery-doc.test.ts.

Approved

Docs and test only: no page, API, MCP tool or terminal surface changes, and no new dependency. The claims I could check against src/snapshot hold: the S3_* names, the usage line, latest, the git clone --mirror layout, FORGE_DATA/PORT/FORGE_SSH_PORT, /api/v1/health, and the wrong-passphrase refusal. The doc is 69 lines and the gate lists the test. I could not run the test here (the doc and test are not in my checkout), so I read them. Two things I would do next: (1) Replace the substring layout checks with a fixture-backed takeSnapshot run asserting the written keys. Checking that snapshot.ts contains "repos/" or "runs/" would pass even if the layout changed. (2) Say in the doc that secret.key.wrapped exists only when FORGE_SNAPSHOT_PASSPHRASE was set, and link the doc from the README so it is found before 3 a.m.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs