diff --git a/docs/superpowers/specs/2026-09-07-control-plane-backup-restore-design.md b/docs/superpowers/specs/2026-09-07-control-plane-backup-restore-design.md index f1ac675..ac680d7 100644 --- a/docs/superpowers/specs/2026-09-07-control-plane-backup-restore-design.md +++ b/docs/superpowers/specs/2026-09-07-control-plane-backup-restore-design.md @@ -223,9 +223,21 @@ fingerprint — and contacts no database. It is what an operator runs to find ou whether an archive they have found is worth anything. `verify` adds a live check: whether the archive's fingerprint matches the key in -the current environment, and whether it matches the database being pointed at. -This is the command that distinguishes "we have backups" from "we have backups -that will restore", and the documentation recommends running it on a schedule. +the current environment, and — when `--mongo-uri` is given — whether that key +actually decrypts the target database. The second half is a probe: read one +ciphertext field from `secrets`, `keys` or `auth_providers` and attempt to open +it. A fingerprint comparison proves two archives agree; only a probe proves the +key in hand opens the data in front of you. `verify` is the command that +distinguishes "we have backups" from "we have backups that will restore", and +the documentation recommends running it on a schedule. + +The probe needs AES-256-GCM open, which today lives in +`server/internal/services/crypto.go` and cannot be imported from another module. +Rather than copy it — the exact hazard `CLAUDE.md` names around mirrored token +blocks and `web/lib/targets.ts` — the primitives move to a new `shared/cryptobox` +package, and `services/crypto.go` becomes a thin delegation that keeps its +existing unexported function names and its `KEY_ENCRYPTION_KEY` lookup. One +implementation of the cipher, two callers. `--out -` streams the tarball to stdout, so piping into `aws s3 cp -`, `restic` or `age` covers storage and archive encryption without the tool growing backends @@ -241,7 +253,9 @@ Kubernetes, or neither. `linux/arm64`, `darwin/arm64` and `windows/amd64` with `CGO_ENABLED=0`, writes `checksums.txt`, and creates a Gitea release. -**Container image.** `deploy/docker/vantagectl.Dockerfile` produces a `scratch` +**Container image.** `vantagectl/Dockerfile` — the repo's convention is a +Dockerfile per module built from the repository root, because every Go module +depends on `shared` through a replace directive — produces a `scratch` image holding the static binary and an explicitly copied `/tmp`, which the archive is staged in before compression — the same omission that silently disabled `vulnsched` on a scratch image. Pushed by `server-deploy.yml` as an