From fb6a8ead2fc2e0784077557a86e2c9ffd0157394 Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Wed, 13 May 2026 01:59:48 +0000 Subject: [PATCH] fix(compose): pin CERTCTL_DATABASE_URL in demo overlay (cold-DB smoke fix #4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fourth latent bug surfaced by the Auditable Codebase Bundle's cold-DB compose smoke. CI run on master tip d92a0c98 fails with: certctl-postgres | FATAL: password authentication failed for user "certctl" (SQLSTATE 28P01 — invalid_password) after every other auth gate has been satisfied. The earlier closures (4737fff DEMO_MODE_ACK, f1aecc6 migration 000043 idempotency, ec72c69 bootstrap-token interpolation) all hold; this one is a different interpolation gap. Root cause: the base compose at deploy/docker-compose.yml:177 builds the certctl-server's database URL via compose-level interpolation: CERTCTL_DATABASE_URL: ${CERTCTL_DATABASE_URL:-postgres://certctl:${POSTGRES_PASSWORD}@postgres:5432/certctl?sslmode=disable} The inner ${POSTGRES_PASSWORD} reads the SHELL environment, not the postgres service's environment: block. The demo overlay sets POSTGRES_PASSWORD: certctl on the postgres service (which feeds postgres's initdb only — that's why the database is seeded with password 'certctl'), but never exports it as a compose-level shell var. In a zero-env-var CI run the shell var is blank, so the generated URL is: postgres://certctl:@postgres:5432/certctl?sslmode=disable ^ empty password while postgres rejects with SCRAM mismatch because its pg_authid holds the hash of 'certctl'. Pre-CI, this gap was masked because every developer running the demo locally had POSTGRES_PASSWORD=certctl in their shell or deploy/.env from earlier sessions; the cold-DB smoke is the first zero-env-var consumer of this overlay. Fix: pin CERTCTL_DATABASE_URL with the literal demo password in the demo overlay's certctl-server environment block. The base compose's ${CERTCTL_DATABASE_URL:-...} default is overlay-overridable, so this literal is overlay-scoped — production deploys that supply their own CERTCTL_DATABASE_URL still win. The overlay was always claimed self-sufficient by its docstring ('Supplies the change-me-... placeholder values for POSTGRES_PASSWORD, CERTCTL_API_KEY, CERTCTL_CONFIG_ENCRYPTION_KEY, and CERTCTL_AGENT_ID so the demo runs without a deploy/.env file') — this commit makes the database URL actually match that claim. Same pattern as the ec72c69 BOOTSTRAP_TOKEN fix: when compose-level interpolation reads from the shell, the overlay's environment: block alone is not enough; the variable that references it must also be pinned explicitly. Verified: YAML parse clean (python3 yaml.safe_load). All 35 scripts/ci-guards/*.sh green, including complete-path-config-coverage.sh (CERTCTL_DATABASE_URL has a non-config consumer in deploy/), G-3-env-docs-drift, B2-compose-base-no-demo-env, S-1-hardcoded-source-counts. --- deploy/docker-compose.demo.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/deploy/docker-compose.demo.yml b/deploy/docker-compose.demo.yml index 9931564..6de9abc 100644 --- a/deploy/docker-compose.demo.yml +++ b/deploy/docker-compose.demo.yml @@ -76,6 +76,22 @@ services: # sentinels outside demo mode, but DEMO_MODE_ACK=true unlocks them. CERTCTL_CONFIG_ENCRYPTION_KEY: change-me-32-char-encryption-key CERTCTL_AUTH_SECRET: change-me-in-production + # Cold-DB smoke fix (2026-05-13): the base compose builds the + # database URL via compose-level `${POSTGRES_PASSWORD}` interpolation + # (deploy/docker-compose.yml line ~177), which reads the SHELL env — + # NOT the postgres service's `environment:` block above (that one + # feeds the postgres container's initdb only). In a zero-env-var + # CI run the shell var is blank, producing + # `postgres://certctl:@postgres:5432/...` and a SCRAM rejection + # against a database that initdb seeded with password `certctl`. + # Pinning the full URL here closes the gap: the demo overlay is + # now fully self-sufficient (matches the file's docstring claim) + # and the cold-DB smoke passes against a fresh GitHub-runner clone + # with no .env file or exported shell vars. Production deploys + # override CERTCTL_DATABASE_URL via the base compose's + # `${CERTCTL_DATABASE_URL:-...}` default, so this literal is + # overlay-scoped and never leaks into a production posture. + CERTCTL_DATABASE_URL: postgres://certctl:certctl@postgres:5432/certctl?sslmode=disable # 180-day simulated history seed applied at boot. CERTCTL_DEMO_SEED: "true"