Public: anyone can read this; the crew can change it.
The Backups page takes the snapshot destination: bucket and keys sealed in the forge, not plaintext in its environment #367
Check · Review · Land · Live done — Landed and live in production.
The Backups page now takes the snapshot destination: endpoint, bucket, region, access key and secret key. The keys are sealed in the forge's secrets table, and the `S3_*` environment variables remain a fallback.
shipped production is serving it deploy d63cf43
I merged main into the branch and pushed it. The four conflicts were all in the snapshot and Backups code, and I kept both sides' changes.
I could not typecheck. bun x tsc --noEmit fails here with "Cannot find type definition file for 'bun'", the same as before. The tests for the destination, snapshot, secret store and web pass (63 in total). I did not run the full check from forge.json.
How I resolved each file:
src/main.ts: the snapshot service still resolves its destination on each run, so a sealed bucket takes effect without a restart. It now also passesFORGE_SNAPSHOT_PASSPHRASE, so the wrapped-key behaviour from #366 is unchanged.src/snapshot/service.ts: the destination can still change at runtime, and the handle keepswraps. The startup log line reports both the destination and whether the key is wrapped.src/snapshot/restore.ts: the CLI still reads its destination through the one resolver. It keeps the passphrase handling and the report of sealed values that could not be restored.src/web/pages/backups.ts: the page has the key-wrapping notice and the destination form together.
What to look at: the restore CLI still reads only the environment, because a restore into an empty directory has nothing sealed yet. This is the same as before the merge.
🤖 Generated with Claude Code
The Backups page now takes the snapshot destination: endpoint, bucket, region, access key and secret key. The keys are sealed in the forge's secrets table, and the
S3_*environment variables remain a fallback.I could not run
tsc, becausebun x tsc --noEmitfails here with "Cannot find type definition file for 'bun'". The new destination tests,test/snapshot.test.ts,test/secret-store.test.tsandtest/web.test.tspass. I did not run the full check fromforge.json.What changed
src/snapshot/destination.tsis the one place that decides where snapshots go. Precedence is the sealed destination, thenFORGE_SNAPSHOT_DIR, thenS3_*. The nightly snapshot, "Snapshot now", the test button, the page and the restore command all use it.SNAPSHOT_S3_*. A new destination takes effect on the next snapshot, without a restart. Nothing is ever shown back: the page says which fields are set, and a blank field keeps its sealed value.- Seal destination: saves the fields. - Test connection: lists the bucket and reports one sentence. It tries the typed values, or the destination in effect if all four required fields are blank. The failure message has the keys stripped out. - Clear sealed destination: removes it, so the environment's destination takes over again.
test/snapshot-destination.test.tscovers precedence, a failed test-connection message with no key in it, partial seals, validation, non-admins, and the page not showing the keys. I also added a section todocs/SNAPSHOT.md.Left out
S3_*orFORGE_SNAPSHOT_DIRfor it. The docs say so.secret.readevents to the audit log.To try: sign in as an admin, open Settings → Backups, fill in the form, press "Test connection", then "Seal destination" and "Snapshot now".
🤖 Generated with Claude Code
Closes #338