docs: record the mesh-bind, hairpin and runner-label gotchas
All checks were successful
deploy / build (push) Successful in 6m44s
deploy / deploy-web (push) Successful in 5s
deploy / deploy-api (push) Successful in 14s

Each of these failed a CI run this session and none of them looks like what it
is: a loopback health probe refused by a healthy daemon, a proxy that cannot
resolve its own public name, and 'cargo: command not found' on a node runner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSDYiibCtELsrjQq6KXnoi
This commit is contained in:
2026-09-04 13:56:42 +03:00
parent 3db86cfc66
commit b552b08b10

View File

@@ -141,12 +141,30 @@ of origins leaves only the last one allowed. It does not break the site — the
frontend is same-origin and never consults CORS — so it is invisible until
something else tries to call the API. Use `AllowOrigin::list`.
**The API binds the host's mesh address, never loopback and never `0.0.0.0`.**
The fleet has one firewalld default zone, so a wildcard bind plus the named
service would publish it on every address the host carries; and the edge proxy
is at another site, so loopback-only would not serve the site at all. Anything
probing `127.0.0.1:25864` gets connection-refused against a perfectly healthy
daemon — which is exactly how the first green build still failed its health
check. Use `$(hostname -f)`, which resolves to the mesh address.
**The edge proxy is at a different site from the API.** oolon (kosherinata)
fronts the name; the API is on bob (hanzalova) because that is where the Planck
node lives. The deploy's loopback health probe cannot see that hop, so there is
a separate check that curls the API *from oolon*. A firewalld service scoped to
node lives. A health probe on the API host cannot see that hop, so there is a
separate check that curls the API *from oolon*. A firewalld service scoped to
bob's own /16 would leave a live site with a dead `/v1` and nothing would fail.
**The edge proxy cannot reach its own public name.** From inside the mesh
`blackbeard.observer` resolves to the site's WAN address and dead-ends on the
OPNsense LAN interface (`reverse-proxies.md` §2). Verify the vhost with
`--resolve blackbeard.observer:443:127.0.0.1`, which still exercises the `:443`
stream router, SNI, the vhost, the cross-site hop and the API.
**Runner labels.** `fedora-*` images have **no cargo** — Rust builds go on
`rust`, which is based on `runner-fedora-44` and so carries node + pnpm too.
Never `corepack enable`; pnpm is already on PATH (`gitea-runners.md` §4).
**WebSocket upgrade tests need `--http1.1`.** The vhost serves HTTP/2, and curl
negotiates h2, where the `Connection: Upgrade` handshake is not how WebSockets
work — you get a 400 from axum that looks like a proxy misconfiguration.