thermograph/infra/deploy
Emi Griffith 001e6b1365
All checks were successful
secrets-guard / encrypted (pull_request) Successful in 5s
shell-lint / shellcheck (pull_request) Successful in 8s
PR build (required check) / changes (pull_request) Successful in 13s
PR build (required check) / build-backend (pull_request) Has been skipped
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / gate (pull_request) Successful in 2s
Sync infra to hosts / sync-beta (push) Has been skipped
Sync infra to hosts / sync-prod (push) Has been skipped
Sync infra to hosts / sync-dev (push) Successful in 7s
secrets-guard / encrypted (push) Successful in 7s
shell-lint / shellcheck (push) Successful in 7s
ci: add an always-on Actions runner on vps2
The estate had exactly one registered runner, on the desktop. It went offline
at 2026-07-31 16:31Z; for the next 21 hours no PR could satisfy a required
check, no deploy could run, and the 03:00Z ops-cron -- the only backup for both
application databases and for Forgejo -- did not fire. Forgejo queued that
scheduled run rather than dropping it, so it completed on reconnect and nothing
was lost. A longer outage would have meant real gaps.

Three files claimed an "always-on Swarm-hosted runner" existed and that the
estate therefore no longer depended on the desktop. It did not exist: an early
revision of docker-stack.yml ran one as a Docker-in-Docker sidecar and it was
removed. That claim is why a single point of failure sat unnoticed. Corrected
in docker-stack.yml, forgejo/README.md and register-lan-runner.sh.

The new runner is a plain restart:always container, not a Swarm service: a
Swarm-scheduled runner cannot redeploy the Swarm that schedules it, so CI would
be gone exactly when the cluster is what is broken. It runs from
/opt/forgejo-runner rather than in place, because the checkout is reset on
every prod deploy and one `git clean -fdx` there would destroy the
registration.

vps2 runs prod, so the socket mount is bounded rather than assumed benign:
capacity 1, --cpus=2/--memory=4g on job containers, valid_volumes empty so no
job can bind-mount /etc/thermograph.env, and no thermograph network joined.
This defends against accident, not against a hostile workflow author -- stated
plainly in the compose header rather than implied.

The desktop runner stays registered as extra capacity. Nothing may assume it
is up.
2026-08-01 07:54:28 -07:00
..
backup infra: split the estate into vps1/vps2 — beta joins prod, dev gets a home (#103) 2026-07-26 06:56:38 +00:00
db db: raise max_connections to 200, cap work_mem at 64MB, trim async pool overflow (#124) 2026-07-26 19:48:50 +00:00
forgejo ci: add an always-on Actions runner on vps2 2026-08-01 07:54:28 -07:00
migrations Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
openmeteo infra: split the estate into vps1/vps2 — beta joins prod, dev gets a home (#103) 2026-07-26 06:56:38 +00:00
secrets secrets: make the two-host age key quorum a checked policy 2026-07-31 05:12:58 +00:00
stack infra: split the estate into vps1/vps2 — beta joins prod, dev gets a home (#103) 2026-07-26 06:56:38 +00:00
swarm infra: split the estate into vps1/vps2 — beta joins prod, dev gets a home (#103) 2026-07-26 06:56:38 +00:00
twa Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
Caddyfile.vps1 dev: leave /healthz outside the basic-auth boundary (#113) 2026-07-26 08:28:18 +00:00
Caddyfile.vps2 infra: split the estate into vps1/vps2 — beta joins prod, dev gets a home (#103) 2026-07-26 06:56:38 +00:00
deploy-dev.sh infra: split the estate into vps1/vps2 — beta joins prod, dev gets a home (#103) 2026-07-26 06:56:38 +00:00
deploy.sh deploy.sh: honour TG_POST_DEPLOY on the compose path too (#109) 2026-07-26 07:28:43 +00:00
env-topology.sh openbao: derive the AppRole credential path per environment 2026-07-30 19:49:28 -07:00
migrate-db.py Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
POSTGRES-MIGRATION.md Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
provision-agent-access.sh Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
provision-dev.sh dev: serve at dev.thermograph.org behind basic auth, bound to loopback (#111) 2026-07-26 07:49:43 +00:00
provision-mail-supervision.sh observability: add the estate's first alerting; supervise Postfix 2026-07-24 13:19:19 -07:00
provision-mail.sh infra: split the estate into vps1/vps2 — beta joins prod, dev gets a home (#103) 2026-07-26 06:56:38 +00:00
provision-secrets.sh infra: split the estate into vps1/vps2 — beta joins prod, dev gets a home (#103) 2026-07-26 06:56:38 +00:00
render-secrets-openbao.sh openbao: render Centralis from the vault 2026-07-30 19:55:17 -07:00
render-secrets.sh openbao: render Centralis from the vault 2026-07-30 19:55:17 -07:00
RUNBOOK-vps1-vps2-cutover.md dev: serve at dev.thermograph.org behind basic auth, bound to loopback (#111) 2026-07-26 07:49:43 +00:00
thermograph-dev.service Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00
thermograph.env.example accounts: sign in with Google, on a shared provider-agnostic OAuth engine (#122) 2026-07-27 00:56:43 +00:00
thermograph.service Subtree-merge thermograph-infra (origin/main) into infra/ 2026-07-22 22:01:11 -07:00