thermograph/infra/CLAUDE.md
Emi Griffith c98512cfcc
All checks were successful
secrets-guard / encrypted (pull_request) Successful in 10s
PR build (required check) / changes (pull_request) Successful in 17s
shell-lint / shellcheck (pull_request) Successful in 14s
PR build (required check) / validate-observability (pull_request) Successful in 44s
PR build (required check) / build-frontend (pull_request) Successful in 2m13s
PR build (required check) / build-backend (pull_request) Successful in 2m31s
PR build (required check) / gate (pull_request) Successful in 3s
docs: rewrite the agent context layer to match the live system
The four domain CLAUDE.md files still described the pre-monorepo split-repo
topology, and several statements were the exact inverse of current reality:
infra/CLAUDE.md told a reader that emi/thermograph is archived and must not be
pointed at, when that is the live repo; backend/ and frontend/ both claimed
there is no Makefile and no requirements-dev.txt when all four files exist;
frontend/ described itself as one of four sibling repos; observability/ claimed
a single unprotected main branch.

These files are read before every change, so a stale one is a correctness
problem rather than a documentation one. Rewritten against the tree:

- Root CLAUDE.md now owns the cross-cutting truth once — branch model, the
  SERVICE + *_IMAGE_TAG deploy contract, image names, which orchestrator each
  environment runs, and that everything is a PR. Domain files carry only what
  differs and are capped at ~70 lines.
- Records that prod runs Swarm from deploy/stack/thermograph-stack.yml while
  beta and LAN dev run compose, routed by /etc/thermograph/deploy-mode.
- Documents the *-deploy-dev.yml workflows as inert rather than leaving a
  reader to discover it.

Deletes infra/docker-stack.yml. It defined db/app/worker, matched nothing that
deploys, and was referenced only by prose describing it as a future design
record — while the real 8-service prod stack lives under deploy/stack/. A file
that looks authoritative and affects nothing is the worst case for a reader
asked to change the prod stack.

infra/README.md claimed "compose in production today" and that Swarm was "not
currently live"; corrected, and infra-sync.yml's existence is now recorded
instead of "there is no separate infra deploy trigger". The compose file's
timescale-pin comment now points at how deploy-stack.sh actually resolves the
digest from the running container.
2026-07-24 20:59:44 -07:00

3.1 KiB

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.