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
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.
58 lines
3.1 KiB
Markdown
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.
|