27f4e7d976
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
9.5 KiB
9.5 KiB
REPORT — the box tells visitors apart; the permanent-gate spike; SparkyFitness; R-772/R-773; release + golden (2026-10-01 night)
The Part table
| Part | Result | Notes |
|---|---|---|
| A — visitors apart (build) | DONE, with one change to the brief's sketch and one leg unmeasured | The sketch plus 2 costs paid in the same rollout: a header clean-up at traefik, and 19 catalog router resets for the apps that read the LEFTMOST address. The brief named neither. ep0's one request was refused by Cloudflare before the box (R-779). |
| B — permanent family gate (spike) | DONE — PASS on exit items 1–5; costs measured | Exit test committed before the run. Build plan: 2 sessions. Waits for the operator (R-780). |
| C — SparkyFitness record | DONE, with 7 rows open | Bench (9401 rebuilt and destroyed) and 9202 measured. Its licence forbids commercial use (R-784, operator). |
| D — R-772, R-773 | DONE | Both red-proven and live on 9202. R-772 needed a second fix found live (0.286.0 → 0.286.1). |
| E — release, floor, golden | DONE | Controller 0.286.1 floored (min_agent 0.131.0); both demo boxes took it and reconciled traefik/cloudflared by themselves. Golden 0.286.1 baked, round-tripped and vouched; the currency gate is OK, not waived. |
Baselines (re-verified): controller c1b123c64955, agent d766666ff8cf, felhom.eu bc9c7b49346b, catalog 94477cba435a.
Ends at: controller a4a753b (v0.286.1), catalog de0a9bd, felhom.eu (this commit). Agent untouched.
Architecture read: 01-topology-and-trust.md §5 and §7 (both corrected), 09 §3 decisions 45–49, 57–62; decision 63 added
first (operator ruling).
Claims in the brief that turned out wrong (or right), named
clientIPtakes the leftmost hop. Right (claim.go:232, pre-0.286).- A stranger can lock the household out of the dashboard. Right, through the tunnel only. Every tunnel visitor shared cloudflared's address as the key; a LAN visitor always had its own key.
- Cloudflare appends to a client-sent
X-Forwarded-For. Right, measured:6.6.6.6,37.191.56.193(M2). Also measured, not in the brief:- Cloudflare passes a client's
X-Forwarded-HostandX-Forwarded-Portunchanged. - It strips a client's
X-Real-IP. - It refuses a client-sent
CF-Connecting-IPat the edge (403).
- Cloudflare passes a client's
- cloudflared's address is docker-assigned. Right (
172.18.0.5, noipv4_address). - The setup gate's token is minted only from a dashboard session. Right (
ServeGateStart, one caller). But "the setup gate uses [the visitor address]" was wrong: the gate read no client address at all, so there was nothing to switch over. It now logs the visitor. - "Any reader takes the address at a fixed distance from the RIGHT." Half wrong. The distance differs by path: second from the right through the tunnel, first on the LAN. So a fixed count is wrong for one of the two paths, and readers must skip trusted proxies from the right. Count readers (calibre-web, tandoor, wger) were left alone for that reason.
- The sketch (traefik trusts cloudflared) is enough. Not on its own. Trusting cloudflared makes the leftmost entry
— written by a stranger — reach every app. 19 apps read exactly that one (several for rate limits: audiobookshelf and
docmost by address only, papra skips its limit on a junk value). It also lets a stranger's
X-Forwarded-Hostthrough. Both were closed in the same rollout. - calibre-web
TRUSTED_PROXY_COUNT, wgerAXES_*proxy count. Left as they are. Both are count readers, and both lock per NAME (decisions 58, 61). A count of 2 would break the LAN path, and for calibre-web also its proto/host. - Grimmory improves. Right as read in source; not measured in this session (R-775 updated).
Part A — the box tells visitors apart
- Paths:
audits/visitors-2026-10-01/A/— M1–M4 before, M5 after, DESIGN.md (the path table, the options, the choice, the docs quoted). - Built (controller v0.286.0/.1):
- A
felhom-tunnelnetwork172.16.253.0/29with docker's allocation confined to.4/30. The hand prototype found traefik grabbing.2, so cloudflared now sits at a fixed.2and traefik at a fixed.3. - traefik trusts
172.16.253.2/32only, and runsfelhom-forwarded@file. EnsureBaseStackreconciles a running traefik or cloudflared.clientaddr.go, plus IPv6 counted per /64.- The dashboard login messages are informal, in both languages.
- A
- Red-proofs: RP-A1, RP-A2 and RP-A3 each fail on their mutant.
- Live:
- 9202, simulated tunnel: a stranger rotating a forged leftmost address was locked after 5 tries; the household from another address got in at once; LAN and impostor forgeries were counted as themselves (L1).
- demo-hp, real tunnel: an app sees
6.6.6.6,37.191.56.193, 172.16.253.2. The dashboard counter was keyed on37.191.56.193and locked after 5. Every app answered (L2, M5, E/). - ep0's one request: refused 403 by Cloudflare's edge, with no line in the box's log, so the second outside address is unmeasured on the real tunnel → R-779.
- Apps (sweep of all 56 apps plus Grimmory and MeTube, read in source:
A/sweep/):- 19 router resets, one commit each, shipped before the release. docmost on the real tunnel still throttles a rotating forger.
- BookStack
APP_PROXIES, with 3.6 re-measured before and after: the household is no longer throttled by the stranger. - Gains with no change: Home Assistant (it treated every internet visitor as "local"), actualbudget, immich, dawarich, claper, termix, Grimmory.
- Open: kimai, zipline, vikunja, nextcloud (R-776); Emby and Jellyfin LAN rights (R-777).
- Checklist 3.10 added.
Part B — the permanent gate (spike)
audits/permanent-gate-2026-10-01/VERDICT.md. PASS on items 1–5.
- 36 stranger requests, 0 reached an app.
- Family members got their own 30-day logins, websockets worked, and logout worked.
- A stranger's guesses locked only him.
- OPDS, Kobo and KOReader worked through anchored exceptions, with the app's own login still in force.
- The family login never opened the dashboard.
Costs: +0.4 ms per request; with the gate down, apps answer 500 (closed); the build is 2 sessions. One build
requirement: anchor every exception (/api/v1/opdsx slipped past an unanchored prefix; Grimmory's own login still
refused it). Fully torn down.
Part C — SparkyFitness
app-catalog-felhom.eu/onboarding/sparkyfitness.md.
- Bench: 5.1 server 33 %; 5.2 server 38 % and db 53 % under a heavy burst, 0 kills; 1.4 v0.17.2 → v0.17.3 proven; 2.1 CLEAN; 9.1 green.
- 9202:
- A stranger got nothing during install.
- Sign-up was blocked in 3 spellings, and blocked again after remove + restore.
- Sign-in limit (3.6): ~10 s for everyone (R-783).
- 4.4: the state read degraded within 10 s.
- Backup → remove → restore read the data back.
- Open: 0.1 licence (R-784 — non-commercial only), 0.5, 0.7, 1.6, 1.7, 5.4, 8.3 (R-786). The pin is 11 releases and a major behind (R-785). A licence sweep of the whole catalog is owed (R-787).
Part D — R-772, R-773
- R-772: not-run →
healthy:false, not_checked:true, plus a line on the app page. 0.286.0 still waited for the last healthy record's 5-minute interval; 0.286.1 sees it on the next tick. Live: not_checked 14 s after the stop, healthy 42 s after the start. Red-proofs RP-D1 and RP-D1b. Readers ofhealth_probe: the state override and the API/page only; the hub never sees it. - R-773: a restore of a removed app writes the lock record (
opened_by: restore) and the block before the start. Live on Karakeep and SparkyFitness: a stranger got 403 after remove + restore. Red-proof RP-D2.
Part E — release, floor, golden
- Release: controller 0.286.1. Full suite rc 0, gates green. 0.286.0 ran on 9202 only.
- Floor:
0.286.1+min_agent 0.131.0→ demo-hp and demo-felhom on 0.286.1 within ~60 s. Each created the network, recreated traefik and moved cloudflared by itself (E/rollout-*.txt). - Golden 0.286.1 (
documentation/tests/golden-0.286.1-2026-10-01/):- sha
1dec051d…a89; the round trip is equal; token leaks 0, with the positive control at 1. - Vouched: golden 0.286.1 / agent 0.138.0 / min_agent 0.131.0. The hub then refused golden 0.285.0.
golden_currency_gate: OK (not waived). The waiver was not renewed — the bake ran.
- sha
Rows
- Closed: R-753, R-754, R-772, R-773.
- Narrowed: R-775.
- Opened: R-776..R-787 (12).
- Register: 410 → 422.
- Capability map: two rows added (PARTIAL: visitors apart; MISSING/spiked: the family gate).
unproven.py: unchanged — 35 of 55 not walked.
Teardown (three layers)
- Machine:
- 9202: karakeep, bookstack and sparkyfitness removed with their data and backups through the product; the echo
containers, the spike (gate, Grimmory, MariaDB, MeTube, volumes, network, dynamic file), test images and helper
files removed. 9202 keeps controller 0.286.1, its new traefik config and
felhom-tunnel— the product's own. - demo-hp 9201:
a1-echoand its image removed; its traefik config was restored after the M2 minute, and is now the release's.
- 9202: karakeep, bookstack and sparkyfitness removed with their data and backups through the product; the echo
containers, the spike (gate, Grimmory, MariaDB, MeTube, volumes, network, dynamic file), test images and helper
files removed. 9202 keeps controller 0.286.1, its new traefik config and
- Host: the bench 9401 was rebuilt and destroyed (
pvesmbefore/after inC/bench/C0,C9). The drill VM is powered off and reverted tovirgin; CT 9100 destroyed. - Hub: the floor and the artifact manifest changed on purpose (above); no customer or appliance records created.
- ep0: one HTTP request, nothing else.