From b552b08b1072755105eb672c511f593a7c7d2524 Mon Sep 17 00:00:00 2001 From: rob thijssen Date: Fri, 4 Sep 2026 13:56:42 +0300 Subject: [PATCH] docs: record the mesh-bind, hairpin and runner-label gotchas 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) Claude-Session: https://claude.ai/code/session_01MSDYiibCtELsrjQq6KXnoi --- CLAUDE.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6bf0f56..fa653de 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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.