`grep -oE "^[A-Z_]+="` cannot match a key name containing a digit. This estate has exactly six such names — all the *_S3_* keys — so a prod audit returned 26 keys against 32 real ones, and reported the S3 and lake credentials as missing from both live hosts. They were present the whole time. The phantom was independently reproduced twice by re-running the same pattern, which is what made it convincing, and it was briefly recorded as one of two root causes of the lake being unqueryable (#56). That issue has one cause: the missing duckdb-lake image. An audit that under-reports is worse than no audit. A missing-secret finding sends someone to provision a credential that already exists, and in a rotation tool it would justify writing over one. Fixed to `^[A-Z][A-Z0-9_]*=` in both the skill and key_gaps.py's docstring. The match still stops at the `=`, so no value is read — that property is the reason this grep exists rather than a parser. Centralis's secrets_gaps carried the same bug and is fixed separately, with a regression test. Claude-Session: https://claude.ai/code/session_0182KTMrsTHJc3TcewCatJFY |
||
|---|---|---|
| .. | ||
| .claude/skills/key-gaps | ||
| deploy | ||
| lake-iceberg | ||
| ops | ||
| terraform | ||
| .env.example | ||
| .gitignore | ||
| .sops.yaml | ||
| ACCESS.md | ||
| CLAUDE.md | ||
| DEPLOY-DEV.md | ||
| DEPLOY.md | ||
| docker-compose.dev.yml | ||
| docker-compose.openmeteo.yml | ||
| docker-compose.yml | ||
| docker-stack.yml | ||
| Makefile | ||
| README.md | ||
thermograph-infra
Infrastructure for Thermograph: Terraform host
provisioning, the SOPS+age secrets vault, Docker Swarm/WireGuard networking,
Forgejo, Caddy, and the deploy scripts that run the already-built app image on
each host. Extracted from the app monorepo (emi/thermograph) — the app repo
owns building and testing the app; this repo owns running it.
terraform/— provisions/configures hosts (SSH-driven by default; an optional GCP-creating module is scaffolded, no live resources yet) and triggers each deploy. Seeterraform/README.md.deploy/secrets/— the git-native SOPS+age secrets vault (every app secret, encrypted at rest, rendered at deploy time). Seedeploy/secrets/README.md.deploy/swarm/,deploy/forgejo/— the 3-node WireGuard/Swarm cluster that hosts Forgejo (git + CI + registry); does not run the app itself. SeeACCESS.mdand the READMEs under each directory.deploy/deploy.sh— pulls the pinned app image (IMAGE_TAG) and rolls the compose stack; invoked by Terraform and by the app repo's.forgejo/workflows/deploy.ymlover SSH.docker-compose*.yml,docker-stack.yml— how the app image runs (compose in production today;docker-stack.ymlis a design record for a possible future Swarm-based app deploy, not currently live).
The app's own source, Dockerfile, and build/test CI stay in the app repo —
this repo never checks out app source; hosts only pull tagged images from the
registry. See ACCESS.md for host access and the Swarm/Forgejo topology, and
terraform/README.md for the day-to-day plan/apply workflow.
Branches & how changes reach each environment
main— what prod and beta run: their/opt/thermographcheckoutsgit reset --hard origin/mainat the start of every deploy (deploy/deploy.sh). A merge tomainreaches those hosts on the next app deploy (or a by-handdeploy.shrun); there is no separate infra deploy trigger.dev— what LAN dev runs:~/thermograph-devresets to it viadeploy/deploy-dev.sh. Keep it fast-forwarded tomain(infra changes are not environment-staged today; the branches exist so LAN dev can trail or lead when needed).release— currently consumed by nothing (prod tracksmain, notrelease). It exists to mirror the app repos' dev→main→release promotion shape if per-environment infra staging is ever wanted; until then, treatmainas live-everywhere.
Note the asymmetry with the app repos: app code IS environment-staged (dev→main→release maps to LAN→beta→prod via image tags), infra is not.