Sync docs to the 3-node / release-branch / Forgejo architecture (#7)

This commit is contained in:
emi 2026-07-21 15:11:21 +00:00
parent 01e29f38a3
commit e9df43e6c0
3 changed files with 50 additions and 52 deletions

View file

@ -41,9 +41,9 @@ Both VPS boxes are provisioned and reachable as of this writing:
| Host | Role | Public IP | Login | | Host | Role | Public IP | Login |
|------|------|-----------|-------| |------|------|-----------|-------|
| prod | Swarm manager, Thermograph's eventual home | `169.58.46.181` | `agent` | | prod | Swarm manager; Thermograph's live prod home (`release`, thermograph.org) | `169.58.46.181` | `agent` |
| beta | Swarm worker, old VPS | `75.119.132.91` | `agent` | | beta | Swarm worker; `main`/beta.thermograph.org + hosts Forgejo | `75.119.132.91` | `agent` |
| desktop | Swarm worker, LAN dev machine + Actions runner | (local) | (already have access) | | desktop | Swarm worker, LAN dev machine + Forgejo Actions runner | (local) | (already have access) |
``` ```
ssh -i ~/.ssh/thermograph_agent_ed25519 agent@169.58.46.181 # prod ssh -i ~/.ssh/thermograph_agent_ed25519 agent@169.58.46.181 # prod
@ -66,11 +66,13 @@ directory that gets cleaned up, not anywhere-in-repo. Losing it just means
regenerating and re-running the provisioning script; it does not lock either regenerating and re-running the provisioning script; it does not lock either
box, since your own login is untouched. box, since your own login is untouched.
As of this writing, neither VPS has Docker installed yet and Terraform has **Live as of 2026-07-21** (was: "no Docker / Terraform not applied / Swarm
not been applied to either (`prod` is a bare OS install; `beta` has an old inactive" — all now done): Docker is installed on all three nodes; the
`/opt/thermograph` checkout but no Docker) — Track 2 is paused on that. The WireGuard mesh and Docker Swarm are up with all three nodes `Ready` (prod
desktop already has Docker (used for the LAN dev containerized stack) but manager, beta + desktop workers); Terraform has been applied to both VPS
hasn't joined anything yet (`docker info` shows `Swarm: inactive`). boxes (prod stood up fresh on `release`, beta rebuilt on `main`); Forgejo is
serving at `git.thermograph.org`; and the desktop runs the Forgejo Actions
runner. See the verification checklist at the end.
## Track 2 — Swarm ## Track 2 — Swarm
@ -112,23 +114,19 @@ service anymore.
`actions/setup-python`, which resolve unchanged). `actions/setup-python`, which resolve unchanged).
- **The custom auto-merge workflow step is gone.** It existed only because - **The custom auto-merge workflow step is gone.** It existed only because
GitHub's native branch protection/auto-merge is paywalled on private GitHub's native branch protection/auto-merge is paywalled on private
free-tier repos. Forgejo has no such tier — configure it directly: free-tier repos. Forgejo has no such tier: `dev` is protected with the
**Settings → Branches → protect `dev` → Enable Status Check → list `build` `build` status check required (Settings → Branches). Forgejo does **not**
→ merge style: squash.** Then a ready PR gets a real "Auto merge when checks auto-merge on green by itself — a ready, green, mergeable PR is merged
succeed" button. **explicitly** (`POST .../pulls/{n}/merge`, `{"Do": "squash"}`), per
- One knock-on simplification: a native auto-merge is an ordinary git push `CLAUDE.md`. That merge is an ordinary push, so `deploy-dev.yml`'s push
(unlike GitHub's token-based merge commit, which doesn't re-trigger `push`), trigger fires naturally afterward.
so `deploy-dev.yml`'s push trigger fires naturally after auto-merge — no - **Branch/deploy mapping (settled):** `.forgejo/workflows/deploy.yml`
more "direct push vs. PR-merge" double-trigger logic to maintain. ("Deploy to beta VPS") triggers on `main` and SSHes to **beta**
- `.forgejo/workflows/deploy.yml` ("Deploy to beta VPS") triggers on `main`, (`75.119.132.91`) running `deploy/deploy.sh`. **Prod is not deployed by a
same as it always did. This was flagged as a possible mismatch against push-triggered workflow** — it's deployed with `terraform apply`
`terraform.tfvars.example`'s `release` branch for prod, but turned out not (`terraform/README.md`) on the `release` branch. So the promotion chain is
to be one: this workflow predates the prod/beta split, so its secrets almost `dev``main` (this workflow deploys beta) → `release` (Terraform deploys
certainly already point at beta (`main` is beta's branch per Terraform) — prod). There is deliberately no `release`-triggered workflow.
see the file's own header comment. A separate `release` → prod deploy path
doesn't exist anywhere yet; don't invent one speculatively, confirm with
whoever owns Terraform whether prod even wants a push-triggered deploy or
is meant to go through `terraform apply` only.
A second, independent set of Forgejo workflows A second, independent set of Forgejo workflows
(`.forgejo/workflows/{ci,build-push,ops-cron}.yml`) was added by a parallel (`.forgejo/workflows/{ci,build-push,ops-cron}.yml`) was added by a parallel
@ -139,27 +137,25 @@ runner-label and ssh-action guesses to match the real infrastructure here).
reconciliation; `build-push.yml` (image → Forgejo's built-in registry) and reconciliation; `build-push.yml` (image → Forgejo's built-in registry) and
`ops-cron.yml` (backup + IndexNow) are novel and still present. `ops-cron.yml` (backup + IndexNow) are novel and still present.
### 3d. Cut over ### 3d. Cut over — DONE (2026-07-21)
Only after 3a3c are verified: The GitHub → Forgejo cutover is complete; this records what was done:
1. Move the deploy secrets (`SSH_HOST`, `SSH_USER`, `SSH_KEY`, `SSH_PORT`) into 1. Deploy secrets (`SSH_HOST`, `SSH_USER`, `SSH_KEY`, `SSH_PORT`) live in
Forgejo's repo Secrets (Settings → Actions → Secrets). Forgejo's repo Secrets, pointing at beta with a dedicated CI deploy key.
2. Re-point the LAN dev runner: `bash deploy/forgejo/register-lan-runner.sh 2. The LAN dev runner was re-pointed to Forgejo via
<forgejo_url> <token>` — stops/disables the old GitHub runner service on `deploy/forgejo/register-lan-runner.sh` (systemd --user, on the desktop).
that same machine and registers `forgejo-runner` in its place, same 3. The repo was migrated into Forgejo; `dev`/`main`/`release` all promoted
`systemd --user` pattern as before (see `DEPLOY-DEV.md` for the original; through Forgejo and deploys verified live.
this supersedes it for the runner specifically). 4. GitHub is retired as git host and CI (PR "Retire GitHub as the git host and
3. Open a test PR against `dev` in Forgejo, confirm the required check runs, CI platform") — `origin` remains only as a read-only mirror of history.
auto-merge fires, and the LAN deploy lands.
4. Only once that's solid: disable/archive the `.github/workflows/*.yml`
triggers (or the whole GitHub repo) — GitHub was the fallback during this
whole migration; retire it deliberately, not by accident.
## Verification checklist ## Verification checklist — all met (2026-07-21)
- [x] `ssh agent@<prod>` and `ssh agent@<beta>` both work; `sudo whoami``root` - [x] `ssh agent@<prod>` and `ssh agent@<beta>` both work; `sudo whoami``root`
- [x] Password SSH auth confirmed dead on both boxes (tested with an actual - [x] Password SSH auth confirmed dead on both boxes (tested with an actual
failed login attempt, not just config inspection) failed login attempt, not just config inspection)
- [ ] `docker node ls` (from prod) shows both nodes `Ready` - [x] `docker node ls` (from prod) shows all three nodes `Ready`
- [ ] Swarm ports (2377/7946/4789) unreachable from outside the WireGuard tunnel - [x] Swarm ports (2377/7946/4789) unreachable from outside the WireGuard tunnel
- [ ] `https://git.thermograph.org` (or your chosen domain) serves Forgejo over TLS - [x] `https://git.thermograph.org` serves Forgejo over TLS; `/v2/` registry
- [ ] A test PR: required check runs → auto-merges → LAN dev deploy lands API is 403 from off-mesh (firewalled to the WireGuard CIDR)
- [ ] GitHub retired only after all of the above hold - [x] A test PR flow verified: required `build` check runs → explicit squash
merge → LAN dev deploy lands
- [x] GitHub retired as git host + CI; kept only as a read-only history mirror

View file

@ -1,7 +1,9 @@
# Copy to /etc/thermograph.env on the VPS and edit. # Copy to /etc/thermograph.env on the VPS and edit. On prod/beta, Terraform
# Read by the systemd unit (EnvironmentFile) so `docker compose up` can interpolate # renders this file for you (see terraform/README.md) — edit the tfvars, not the
# it, AND loaded into the app container (env_file in docker-compose.yml). Anything # host. Sourced by deploy/deploy.sh (and any `docker compose` invocation) so
# secret the app needs — Postgres password, VAPID keys, auth secret — belongs here. # compose can interpolate it, AND loaded into the app container (env_file in
# docker-compose.yml). Anything secret the app needs — Postgres password, VAPID
# keys, auth secret — belongs here.
# Port uvicorn binds inside the container. The compose stack publishes it on the # Port uvicorn binds inside the container. The compose stack publishes it on the
# host loopback (127.0.0.1:8137) for Caddy to proxy to. Keep 8137. # host loopback (127.0.0.1:8137) for Caddy to proxy to. Keep 8137.

View file

@ -12,11 +12,11 @@ One reusable module (`modules/thermograph-host`) is instantiated per host via
| key | role | VPS | branch | domain | notes | | key | role | VPS | branch | domain | notes |
|--------|--------|-------------------------|-----------|-------------------|------------------------------------| |--------|--------|-------------------------|-----------|-------------------|------------------------------------|
| `prod` | prod | NEW 48 GB / 12-core box | `release` | `thermograph.org` | Caddy TLS; sized up (8/4/16g) | | `prod` | prod | NEW 48 GB / 12-core box (`169.58.46.181`) | `release` | `thermograph.org` | Caddy TLS; sized up (8/8/4/16g) |
| `beta` | beta | old box `75.119.132.91` | `main` | *(none)* | testing/beta; no public domain yet | | `beta` | beta | old box `75.119.132.91` | `main` | `beta.thermograph.org` | Caddy TLS; also hosts Forgejo |
The `dev` branch is **out of scope here** — it deploys to the LAN dev server via The `dev` branch is **out of scope here** — it deploys to the LAN dev server via
`deploy/deploy-dev.sh` (a self-hosted GitHub Actions runner), not Terraform. `deploy/deploy-dev.sh` (a self-hosted **Forgejo** Actions runner), not Terraform.
Per host, over SSH provisioners, Terraform: Per host, over SSH provisioners, Terraform: