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

58 lines
3.1 KiB
Markdown

# 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.