SCRUM-240 — Deploy: the admin plane in compose¶
Plan ref: OPS-1 (docs/11-admin-plane-plan.md). Stacked on SCRUM-241.
What exists¶
| Service | Notes |
|---|---|
admin-auth-migrate, admin-auth |
admin_auth_rw DSN; key/TOTP/root-password paths under ./secrets/admin_auth (mounted writable so genkey can write; the files come from SCRUM-242); issuer https://admin-auth.otomo.internal, audience otomo:staff; public URL http://localhost:8090 (the tunnel) |
config-migrate, config |
config_rw; blobs on the named volume blobs read-write |
patch |
patch_ro on the config DB; the same blobs volume read-only; starts after config (see below) |
dashboard |
Prometheus/Loki URLs (services arrive in SCRUM-230; panels degrade until then), DASHBOARD_TARGETS = every service's internal :9090 |
admin-ui |
no host port; gateway_dev fronts it at /admin/ |
| Staff trust (doc 10 gap 4) | every staff verifier uses http://admin-auth:8080/.well-known/jwks.json and the pinned issuer; the STAFF_JWKS_URL/stand-in override is gone; gateway_dev's admin-auth upstream is http://admin-auth:8080 |
up.sh |
postgres → migrations → services; readiness waits for admin-auth, config, patch, dashboard, admin-ui; stops with a pointer to generate-secrets.sh if the admin-auth key files are missing |
scripts/check-compose.sh |
renders the file with compose and asserts the pinned JWKS/issuer/audience, blobs rw/ro, unchanged published ports, one-shot migrations, DB user ↔ database, and patch-after-config |
No new host ports: registry 127.0.0.1:5000, gateway :8080, gateway_dev 127.0.0.1:8090.
How to verify¶
cd deploy
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
# on a fresh VM, after SCRUM-242: scripts/generate-secrets.sh && scripts/up.sh
Results at time of writing¶
compose config(standalone compose v2.39.4, no daemon on the dev box) andcheck-compose.sh: pass;sh -non every script: pass.- Not yet run on a VM: that needs SCRUM-242's key generation, and the shared host's cutover (SCRUM-244) needs the team's sign-off.
How it was built¶
DeepSeek run scoped (Landlock) to deploy (266 s, ~46k output tokens, reasoning
effort low). Claude review: config and patch could start concurrently, and whichever
first mounts the empty blobs volume sets its ownership; if patch won (its image has
no blob directory) the volume would be root-owned and config could not write blobs.
Patch now depends on config, and check-compose asserts it.