thermograph/infra
Emi Griffith ce4350c64e
All checks were successful
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Has been skipped
secrets-guard / encrypted (pull_request) Successful in 6s
PR build (required check) / changes (pull_request) Successful in 9s
shell-lint / shellcheck (pull_request) Successful in 6s
PR build (required check) / build-backend (pull_request) Successful in 1m10s
PR build (required check) / gate (pull_request) Successful in 2s
daemon: let the conversational agent own @mentions in server channels
THERMOGRAPH_DISCORD_BOT_MENTIONS=0 makes the gateway bot silent on guild
mentions. Default on, and unset counts as on, so every env file that predates
the knob behaves exactly as before.

The reason is a second responder. The operator's desktop now runs a
conversational agent that replies as this same bot account (discord-voice-bot,
AGENTS.md) and grades through the same API we do, so with both live a single
"@Thermograph Phoenix" gets answered twice — once as a card, once as a reply.
Only one of us can own that surface, and the agent is the one that can also hold
a conversation.

Scoped to guild mentions on purpose. DMs stay here: the agent polls guild
channels only, so staying silent in a DM would just drop the message. And
/grade is a signed HTTP interaction that never touched this path, so it keeps
answering from prod while the desktop is asleep — which is what stops this from
trading an always-on surface for a sometimes-on one.

Claude-Session: https://claude.ai/code/session_015Z1ebLbhUxeZ9ozpNrVTCP
2026-07-24 13:24:21 -07:00
..
.claude/skills/key-gaps Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
deploy daemon: let the conversational agent own @mentions in server channels 2026-07-24 13:24:21 -07:00
lake-iceberg Iceberg conversion container for the ERA5 lake (infra/lake-iceberg) (#24) 2026-07-23 22:56:27 +00:00
ops ops/iceberg.sh: read-only Iceberg lake queries across all environments (#23) 2026-07-24 19:34:30 +00:00
terraform Log hygiene: Alloy CPU, Loki chunks/limits, Caddy field-stripping (#36) 2026-07-24 04:37:41 +00:00
.env.example daemon: move the Discord gateway and scheduler out of the web process into Go (#21) 2026-07-23 22:49:54 +00:00
.gitignore Iceberg conversion container for the ERA5 lake (infra/lake-iceberg) (#24) 2026-07-23 22:56:27 +00:00
.sops.yaml Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
ACCESS.md Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
CLAUDE.md Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
DEPLOY-DEV.md Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
DEPLOY.md Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
docker-compose.dev.yml Reconcile: merge main (shellcheck guard, Go daemon) into dev 2026-07-23 17:06:52 -07:00
docker-compose.openmeteo.yml Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
docker-compose.yml Reconcile: merge main (shellcheck guard, Go daemon) into dev 2026-07-23 17:06:52 -07:00
docker-stack.yml Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
Makefile Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
README.md Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00

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. See terraform/README.md.
  • deploy/secrets/ — the git-native SOPS+age secrets vault (every app secret, encrypted at rest, rendered at deploy time). See deploy/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. See ACCESS.md and 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.yml over SSH.
  • docker-compose*.yml, docker-stack.yml — how the app image runs (compose in production today; docker-stack.yml is 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/thermograph checkouts git reset --hard origin/main at the start of every deploy (deploy/deploy.sh). A merge to main reaches those hosts on the next app deploy (or a by-hand deploy.sh run); there is no separate infra deploy trigger.
  • dev — what LAN dev runs: ~/thermograph-dev resets to it via deploy/deploy-dev.sh. Keep it fast-forwarded to main (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 tracks main, not release). It exists to mirror the app repos' dev→main→release promotion shape if per-environment infra staging is ever wanted; until then, treat main as 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.