SCRUM-242 — Deploy: secrets and root user bootstrap¶
Plan ref: OPS-3 (docs/11-admin-plane-plan.md), decision D6. Stacked on SCRUM-240.
Needs the admin-auth image from SCRUM-208 (genkey -totp).
What exists¶
| Piece | Notes |
|---|---|
generate-secrets.sh |
also creates deploy/secrets/admin_auth/ (0700) and root_password (32 chars, 0600) if missing; chowns both to uid 65532 when run as root, otherwise prints the exact sudo chown. Idempotent; valkey.conf is rewritten only when its content changes |
up.sh |
after postgres + migrations: checks secrets/admin_auth is owned by 65532 (prints the sudo chown if not); genkey -totp if totp.key is missing; genkey (key file + DB row) if signing_key.pem is missing; bootstrap-root every run (idempotent) |
rotate-root-password.sh |
new password (0600, owner kept), bootstrap-root -rotate (also revokes root's sessions), restores the previous file byte for byte if rotation fails |
.gitignore |
deploy/.gitignore ignores secrets/, .env, valkey/valkey.conf |
| README "Root account" | where the password lives, sudo cat over SSH, first sign-in at http://localhost:8090/admin/ through the tunnel, rotation; root is password-only by design (D5) and exists to create personal admin accounts |
First run on a fresh VM: generate-secrets.sh → (sudo chown if it asks) → up.sh.
How to verify¶
cd deploy
sh scripts/test-generate-secrets.sh
sed 's/^\([A-Z_]*PASSWORD\)=$/\1=x/' .env.example > /tmp/t.env
docker compose --env-file /tmp/t.env -f compose.yaml config --quiet && sh scripts/check-compose.sh /tmp/t.env
git check-ignore -v deploy/secrets/admin_auth/root_password
Results at time of writing¶
test-generate-secrets.sh: PASS — files and modes created, a second run changes no checksum, mode or mtime, no secret value in the output.- compose config, check-compose,
sh -n: pass; the secret path is git-ignored. - Not yet run end-to-end on a VM (no docker on the dev box; the shared host's cutover, SCRUM-244, needs the team's sign-off).
How it was built¶
DeepSeek run scoped (Landlock) to deploy (150 s, ~28k output tokens, reasoning
effort low). Claude review added the ownership preflight in up.sh: without the
sudo chown, genkey would have failed inside the container with a bare
permission error.