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.
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 toBRANCH(defaultmain), renders secrets, then routes: if/etc/thermograph/deploy-modesaysstackit execsdeploy/stack/deploy-stack.sh, otherwise it rolls compose services. Per-service tags persist in untrackeddeploy/.image-tags.env(compose) anddeploy/.stack-image-tags.env(stack) so the two never mix.deploy/stack/deploy-stack.sh— the Swarm path, live on prod.backendrolls web and worker;frontendrolls frontend;allruns a fulldocker stack deploy. Start-first, health-gated, auto-rollback.STACK_TEST=1rehearses the whole thing under stack namethermograph-teston throwaway volumes and ports18137/18080.deploy/deploy-dev.sh— thin LAN-dev wrapper (dev compose overlay,~/thermograph-dev). Its CI trigger is inert; see rootCLAUDE.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 applycasually. 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-guardCI rejects plaintext.deploy/secrets/seed-from-live.shreads 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 usesPROD_SSH_*, notSSH_*— an earlier revision reusedSSH_*and silently dumped beta while prod had no backup at all. If you touch it, verify a dump actually lands inagent@prod:~/thermograph-backups/. - Shell here runs as root over SSH against live hosts with no test suite in
front of it.
shell-lintCI (pinned shellcheck) is the only guard — keep the tree at zero findings.
Commits & PRs
Concise and technical. Never mention AI, assistants or automated authorship.