# infra/ — agent instructions How and where the already-built app images run: Terraform, the SOPS+age secrets vault, compose and Swarm stack files, deploy scripts, host provisioning, mail, and the ops cron (DB backup + IndexNow). Read the root `CLAUDE.md` first — it owns the branch model, the deploy contract, and which environment runs which orchestrator. ## The machines Per `ACCESS.md`: **dev** (operator's box — LAN dev server and CI runner), **prod** (`169.58.46.181`, thermograph.org, `agent` user, passwordless sudo), **beta** (`75.119.132.91`, beta.thermograph.org + Forgejo + Grafana/Loki, `agent` user). All four are on a WireGuard mesh. Hosts' `/opt/thermograph` is a checkout of **this monorepo**, not an infra-only repo. ## Deploy paths - **`deploy/deploy.sh`** — the single entry point for beta and prod. Resets the host checkout to `BRANCH` (default `main`), renders secrets, then routes: if `/etc/thermograph/deploy-mode` says `stack` it execs `deploy/stack/deploy-stack.sh`, otherwise it rolls compose services. Per-service tags persist in untracked `deploy/.image-tags.env` (compose) and `deploy/.stack-image-tags.env` (stack) so the two never mix. - **`deploy/stack/deploy-stack.sh`** — the Swarm path, live on prod. `backend` rolls web **and** worker; `frontend` rolls frontend; `all` runs a full `docker stack deploy`. Start-first, health-gated, auto-rollback. `STACK_TEST=1` rehearses the whole thing under stack name `thermograph-test` on throwaway volumes and ports `18137`/`18080`. - **`deploy/deploy-dev.sh`** — thin LAN-dev wrapper (dev compose overlay, `~/thermograph-dev`). Its CI trigger is inert; see root `CLAUDE.md`. - **`Makefile`** — compose orchestration only: `up`, `down`, `db-up`, `dev-up`, `om-up`, `om-backfill`. Volumes are the reason the compose project name is pinned: compose creates `thermograph_pgdata`/`_appdata`/`_applogs`, and the Swarm stack declares those same names as `external: true` at the same mount paths. ## Rules - **Never run `terraform apply` casually.** No tfstate is persisted anywhere, so an apply would attempt full re-provisioning of live hosts. Terraform here is executable documentation until state is bootstrapped. - **Secrets: SOPS vault only** (`deploy/secrets/*.yaml`, `sops edit` → commit → deploy). Never hand-edit `/etc/thermograph.env` — it is a rendered artifact. `secrets-guard` CI rejects plaintext. `deploy/secrets/seed-from-live.sh` reads production secrets and is **not** for an agent to run. - **The ops cron (`.forgejo/workflows/ops-cron.yml`, at the repo root) is THE prod backup.** It uses `PROD_SSH_*`, not `SSH_*` — an earlier revision reused `SSH_*` and silently dumped beta while prod had no backup at all. If you touch it, verify a dump actually lands in `agent@prod:~/thermograph-backups/`. - Shell here runs as root over SSH against live hosts with no test suite in front of it. `shell-lint` CI (pinned shellcheck) is the only guard — keep the tree at zero findings. ## Commits & PRs Concise and technical. Never mention AI, assistants or automated authorship.