Session close: REPORT-visitors-2026-10-01, STATUS (go/no-go: the family gate; SparkyFitness licence), CONTEXT, capability map rows, R-783..R-787 (register 422)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
+10
@@ -16,6 +16,16 @@
|
|||||||
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
|
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
|
||||||
|
|
||||||
|
|
||||||
|
> **2026-10-01 (late night) — the box tells visitors apart (R-753 CLOSED), controller v0.286.1 floored, golden 0.286.1
|
||||||
|
> vouched.** cloudflared alone on `felhom-tunnel` at `172.16.253.2` (traefik `.3`; allocation confined to `.4/30`); traefik
|
||||||
|
> trusts forwarded headers from that address only and strips client-writable host/path/address headers
|
||||||
|
> (`felhom-forwarded@file`); the controller reads the hop traefik saw (`clientaddr.go`). **Readers take the visitor from the
|
||||||
|
> RIGHT** — 19 catalog apps that read the leftmost carry `<router>-xff`; BookStack `APP_PROXIES`; checklist 3.10. R-772
|
||||||
|
> (not-checked probe) and R-773 (lock after a restore) closed. Permanent family gate SPIKED and PASSED, not built (R-780,
|
||||||
|
> operator go/no-go, 2 sessions). SparkyFitness record added; its licence is non-commercial only (R-784, operator); a catalog
|
||||||
|
> licence sweep is owed (R-787). `01` §7 corrected: cloudflared runs in the guest (R-754). Report:
|
||||||
|
> `REPORT-visitors-2026-10-01.md`.
|
||||||
|
|
||||||
> **Ruling 2026-10-01 (evening, operator) — `09` §3 decision 63, recorded before the work.** Login problems are solved by
|
> **Ruling 2026-10-01 (evening, operator) — `09` §3 decision 63, recorded before the work.** Login problems are solved by
|
||||||
> EXTENDING THE BOX'S OWN GATE (option A), not by an identity app on every box (option B, Authentik/Authelia — a possible
|
> EXTENDING THE BOX'S OWN GATE (option A), not by an identity app on every box (option B, Authentik/Authelia — a possible
|
||||||
> later step behind the same traefik `forwardAuth` hook). Order: the box tells visitors apart (R-753) → a SPIKE of a
|
> later step behind the same traefik `forwardAuth` hook). Order: the box tells visitors apart (R-753) → a SPIKE of a
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
- *`clientIP` takes 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-Host` and `X-Forwarded-Port` unchanged.
|
||||||
|
- It strips a client's `X-Real-IP`.
|
||||||
|
- It refuses a client-sent `CF-Connecting-IP` at the edge (403).
|
||||||
|
- *cloudflared's address is docker-assigned.* **Right** (`172.18.0.5`, no `ipv4_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-Host` through.
|
||||||
|
Both were closed in the same rollout.
|
||||||
|
- *calibre-web `TRUSTED_PROXY_COUNT`, wger `AXES_*` 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-tunnel` network `172.16.253.0/29` with docker's allocation confined to `.4/30`. The hand prototype found
|
||||||
|
traefik grabbing `.2`, so cloudflared now sits at a fixed `.2` and traefik at a fixed `.3`.
|
||||||
|
- traefik trusts `172.16.253.2/32` only, and runs `felhom-forwarded@file`.
|
||||||
|
- `EnsureBaseStack` reconciles a running traefik or cloudflared.
|
||||||
|
- `clientaddr.go`, plus IPv6 counted per /64.
|
||||||
|
- The dashboard login messages are informal, in both languages.
|
||||||
|
- **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 on
|
||||||
|
`37.191.56.193` and 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 of `health_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.
|
||||||
|
|
||||||
|
## 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-echo` and its image removed; its traefik config was restored after the M2 minute, and is now the
|
||||||
|
release's.
|
||||||
|
- **Host:** the bench 9401 was rebuilt and destroyed (`pvesm` before/after in `C/bench/C0`, `C9`). The drill VM is
|
||||||
|
powered off and reverted to `virgin`; 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.
|
||||||
@@ -2,67 +2,61 @@
|
|||||||
|
|
||||||
**Ready for the first real tester (Tester-2): yes. You confirmed the tunnel route and the connect mails (2026-09-30).**
|
**Ready for the first real tester (Tester-2): yes. You confirmed the tunnel route and the connect mails (2026-09-30).**
|
||||||
|
|
||||||
**Updated 2026-10-01 (night). Both demo boxes run controller 0.285.0 and host agent 0.138.0. Hub 0.126.0. New installs get golden 0.285.0 with agent 0.138.0.**
|
**Updated 2026-10-01 (late night). Both demo boxes run controller 0.286.1 and host agent 0.138.0. Hub 0.126.0. New
|
||||||
|
installs get golden 0.286.1 with agent 0.138.0.**
|
||||||
|
|
||||||
**Tester-2 — read only, from the hub.** The customer record exists. Tester-2's box has not registered yet.
|
**Tester-2 — read only, from the hub.** The customer record exists. Tester-2's box has not registered yet.
|
||||||
|
|
||||||
## Tonight: the first new apps through the checklist
|
## Tonight: the box tells visitors apart
|
||||||
|
|
||||||
- **wger is hidden** from new installs until its two problems are fixed. No box has wger.
|
- **Before:** through the internet every visitor looked like the same address. A stranger's 5 wrong passwords locked the
|
||||||
- **Three new apps are in the catalog, each fully tested:** Radicale (calendar and contacts), Karakeep (bookmarks),
|
whole family out of the box's own dashboard for a minute, and out of some apps.
|
||||||
Dawarich (location history). Each passed all 60 checks, with a tested update and a tested restore.
|
- **Now:** the box sees each visitor's real address, and a stranger cannot fake it. Measured on demo-hp through the real
|
||||||
- **Their logos and screenshots reach the boxes only with the next hub release.** Until then the cards have no picture.
|
internet path: a stranger's wrong passwords locked only that stranger.
|
||||||
- **Grimmory (e-book library) is ready but NOT published.** A stranger who guesses wrong 5 times locks everyone out of
|
- **Apps:** 19 apps would have believed a faked address. They are protected, in the same update. BookStack now locks
|
||||||
its login for 15 minutes. You decide (below).
|
only the stranger, measured. Home Assistant no longer treats every internet visitor as "at home".
|
||||||
- **Not built, on the fit check:** Grimoire (its makers say: never put it on the internet), MeTube (it has no login at
|
- **Not measured:** a second outside address on the real internet path. The test machine abroad was refused by
|
||||||
all), Pinchflat (its makers stopped working on it).
|
Cloudflare before it reached the box. A 2-minute test from your phone would close it.
|
||||||
- **Fit check only, as you asked:** Invidious and moonlight-web. My advice: build neither (below).
|
|
||||||
|
|
||||||
## The fit table, one line each
|
## The permanent family gate: tested, NOT built
|
||||||
|
|
||||||
- **Radicale** — build. Done.
|
- A gate with a login for each family member, in front of Grimmory and MeTube, passed every test on the scratch box.
|
||||||
- **Karakeep** — build with a note (it visits every page you save; AI is off unless you give it a key). Done.
|
- **Strangers:** they reached nothing, and their wrong guesses locked only themselves.
|
||||||
- **Dawarich** — build with a note (a known login at first, replaced at install; map pictures come from its makers' server). Done.
|
- **Family:** members stayed signed in for 30 days, logout worked, and the family login does not open the dashboard.
|
||||||
- **Grimmory** — build with a note. Built, waiting for your word.
|
- **E-readers:** Kobo, KOReader and OPDS readers kept working with Grimmory's own login.
|
||||||
- **Grimoire** — stop. Its makers say it must not be on the internet, and there is no ready image.
|
- **Cost:** under 1 ms per page. If the box's controller is down, the gated apps refuse — they never open.
|
||||||
- **MeTube** — stop for now. No login at all: anyone who finds it downloads through your home connection.
|
|
||||||
- **Pinchflat** — stop. No new version since September 2025.
|
|
||||||
- **Invidious** — stop (my advice). It breaks when YouTube changes, and YouTube can block the household's home address.
|
|
||||||
- **moonlight-web** — stop. It needs a gaming PC and network paths our tunnel does not carry.
|
|
||||||
|
|
||||||
## Earlier today
|
## Also tonight
|
||||||
|
|
||||||
- **The new-app checklist is built** (60 checks; a new app cannot reach the catalog without its filled-in copy). The test
|
- **SparkyFitness passed most of the checklist.** One problem: its **licence forbids commercial use** without the
|
||||||
on wger found its 4 known problems and 3 more (no styling or photos, strangers can sign up, no e-mail).
|
author's written permission (below).
|
||||||
- Your afternoon decisions are built: calibre-web's secret login name; the registry clean-up rule (it deleted nothing).
|
- **Two box fixes:**
|
||||||
|
- The health check now says "not checked" instead of "healthy" when it could not look.
|
||||||
|
- A removed and restored app keeps its sign-up lock.
|
||||||
|
- **A new golden.** Fresh installs get today's version.
|
||||||
|
|
||||||
## What broke tonight, and what I did
|
**Rows.** 4 closed, 12 opened. The list went from 410 to 422 rows.
|
||||||
|
|
||||||
- **Radicale's first version lost your login after a restore.** Found in testing, fixed before it went out.
|
|
||||||
- **After "remove the app, keep backups" and a restore, one of the two sign-up locks is gone** (the app's own lock
|
|
||||||
still holds). Written down for the box.
|
|
||||||
- **The box's health check writes "healthy" when it could not check at all.** The app's state still showed the problem.
|
|
||||||
Written down.
|
|
||||||
- Karakeep needed more memory than first planned. It now has room for saving many heavy pages at once.
|
|
||||||
|
|
||||||
**Rows.** Tonight: 11 opened, one of them already closed (the Radicale restore problem). The list went from 399 to 410 rows.
|
|
||||||
|
|
||||||
## What needs you
|
## What needs you
|
||||||
|
|
||||||
1. **Grimmory: publish it now, or wait?**
|
1. **The permanent family gate: build it or not?**
|
||||||
- **A — publish now** with a sentence on its page: "if someone guesses your password wrong 5 times, the login waits
|
- **Go:** 2 work sessions — the box first, then Grimmory and MeTube published behind it.
|
||||||
15 minutes." E-reader apps keep working during the wait (measured).
|
- **No-go:** Grimmory stays unpublished, and MeTube is not built.
|
||||||
- **B — wait** until the box can tell visitors apart (the bigger build you have not decided yet). Nobody gets Grimmory
|
- **If you say nothing:** nothing is built, and the next sessions do other work.
|
||||||
until then.
|
- I would choose **go**. It also answers Grimmory and MeTube.
|
||||||
- I would choose **A**: the lock is short, it is the web page only, and e-readers are not touched. It is like mealie's lock, which you kept.
|
2. **SparkyFitness's licence** says "non-commercial use only".
|
||||||
2. **Invidious and moonlight-web: build or not?** I advise **no** for both. If you do nothing, neither is built.
|
- **A:** hide it from new installs and ask the author for permission.
|
||||||
3. **MeTube and Pinchflat (video downloaders):** stopped for other reasons (no login; makers stopped). The YouTube
|
- **B:** keep it offered while asking.
|
||||||
sentence („Csak saját használatra…") waits until one of them is built.
|
- I would choose **A**.
|
||||||
4. Older, nothing urgent: should apps see each visitor's real address; is the design document or the build right about
|
- **If you say nothing:** it stays offered.
|
||||||
where the tunnel runs.
|
3. **Grimmory:** publish behind the family gate (if go), or as it is now. Its lock is per visitor since tonight; read in
|
||||||
|
its code, not yet measured.
|
||||||
|
4. **One phone test (2 minutes):** sign in to demo-hp's dashboard from your phone off wifi, while I lock out my own
|
||||||
|
address. If you skip it, the second-address proof stays the simulated one.
|
||||||
|
5. Still open from before: Invidious and moonlight-web — I advise no to both. MeTube and Pinchflat are stopped.
|
||||||
|
|
||||||
## Standing steps
|
## Standing steps
|
||||||
|
|
||||||
- **Monthly security re-test: last run 2026-10-01, next due ~2026-11-01.** (You start it with the standing brief.)
|
- **Monthly security re-test: last run 2026-10-01, next due ~2026-11-01.** (You start it with the standing brief.)
|
||||||
- **Weekly:** the golden bake (around 8 October).
|
- **Weekly:** the golden bake (around 8 October; tonight's bake was 0.286.1).
|
||||||
- **Registry clean-up:** only when the registry disk fills; "show me" mode first, then a person decides.
|
- **Registry clean-up:** only when the registry disk fills; "show me" mode first, then a person decides.
|
||||||
|
|||||||
@@ -191,6 +191,8 @@ likewise silent. Evidence: `audits/DRILL-r361-2026-08-22/evidence/06-part3-decis
|
|||||||
| **Indítópult megosztás (vendég link)** — capability URL `/s/<token>` serves a standalone read-only guest launcher (no account, no admin session); optional per-share password; QR | controller v0.165.0 | **IMPLEMENTED** | 160-bit `crypto/rand` token, constant-time match (empty stored = disabled = byte-identical to the mux default 404); guest headers `noindex`/`no-referrer`/`no-store`; optional SEPARATE bcrypt share password + its own per-IP attempt map; signed cookie `HMAC(token`\|`passwordHash)` keyed with `session_secret` (rotate-token OR change-password invalidates all cookies); token redacted in logs (`/s/<redacted>`). Groups A–G (14 tests) + 3 red-proofs; **§13 endpoint-level live validation on 9201 all-pass** (`felhom-controller/REPORT.md` 2026-07-24). **Design ruling: member accounts SUPERSEDED by this capability-URL model; per-member tile visibility parked under the SSO arc (R-15).** | Full operator browser click-through + a validation doc pending → then PROVEN-LIVE. Accepted residuals: link-preview crawlers fetch once (noindex prevents indexing); reverse-proxy/CF access logs hold the path (ops-tier); the modal link carries the request Host (LAN-IP admin ⇒ LAN-IP link) |
|
| **Indítópult megosztás (vendég link)** — capability URL `/s/<token>` serves a standalone read-only guest launcher (no account, no admin session); optional per-share password; QR | controller v0.165.0 | **IMPLEMENTED** | 160-bit `crypto/rand` token, constant-time match (empty stored = disabled = byte-identical to the mux default 404); guest headers `noindex`/`no-referrer`/`no-store`; optional SEPARATE bcrypt share password + its own per-IP attempt map; signed cookie `HMAC(token`\|`passwordHash)` keyed with `session_secret` (rotate-token OR change-password invalidates all cookies); token redacted in logs (`/s/<redacted>`). Groups A–G (14 tests) + 3 red-proofs; **§13 endpoint-level live validation on 9201 all-pass** (`felhom-controller/REPORT.md` 2026-07-24). **Design ruling: member accounts SUPERSEDED by this capability-URL model; per-member tile visibility parked under the SSO arc (R-15).** | Full operator browser click-through + a validation doc pending → then PROVEN-LIVE. Accepted residuals: link-preview crawlers fetch once (noindex prevents indexing); reverse-proxy/CF access logs hold the path (ops-tier); the modal link carries the request Host (LAN-IP admin ⇒ LAN-IP link) |
|
||||||
| Forgot dashboard password → instant reset code | controller v0.123, hub | **PROVEN-LIVE** | `DRILL-day0-take2-2026-07-12` F-15 (live re-run of the exact failure path: hash applied 1s after request, code accepted first try) | |
|
| Forgot dashboard password → instant reset code | controller v0.123, hub | **PROVEN-LIVE** | `DRILL-day0-take2-2026-07-12` F-15 (live re-run of the exact failure path: hash applied 1s after request, code accepted first try) | |
|
||||||
| **The dashboard in English** — the household picks its language; Hungarian unchanged | controller v0.247.0 (spike) → v0.250.0 (slice 1, R-556 CLOSED) | **PARTIAL — PROVEN-LIVE for every dashboard template** (all 36 templates carry their copy in the bundles; the switch is on every dashboard page) | `audits/i18n-slice1-2026-09-17/C/live/` (and `A/`, `B/`, `audits/i18n-2026-09-17/live/`) — on demo-hp 0.250.0 each Hungarian page equals its 0.249.0 fetch once the new switch form is removed (live numbers aside; login, claim, catch-all byte-identical); `POST /settings/language` made every page English and back; the hub stored `hu`, `en`, `hu` (16:45:10Z, 16:46:06Z, 16:46:43Z). Unit: `TestI18nParity` (106 states vs fixtures from unconverted templates), `TestI18nParityCoversEveryMarker`, `TestI18nEnglishPages`, `TestDirectRenderHandlersFollowLanguage`, all red-proofed. Design `architecture/10-localisation.md` | Go-side messages and three app-name page titles (R-557 after R-553, R-566), hub e-mails (R-558), console/download page (R-559), catalog copy (R-560), guide + English stranger drill (R-561); ASCII-only Hungarian invisible to the English page test (R-565). **Hungarian households see the switch only once the fleet floor reaches 0.250.0** |
|
| **The dashboard in English** — the household picks its language; Hungarian unchanged | controller v0.247.0 (spike) → v0.250.0 (slice 1, R-556 CLOSED) | **PARTIAL — PROVEN-LIVE for every dashboard template** (all 36 templates carry their copy in the bundles; the switch is on every dashboard page) | `audits/i18n-slice1-2026-09-17/C/live/` (and `A/`, `B/`, `audits/i18n-2026-09-17/live/`) — on demo-hp 0.250.0 each Hungarian page equals its 0.249.0 fetch once the new switch form is removed (live numbers aside; login, claim, catch-all byte-identical); `POST /settings/language` made every page English and back; the hub stored `hu`, `en`, `hu` (16:45:10Z, 16:46:06Z, 16:46:43Z). Unit: `TestI18nParity` (106 states vs fixtures from unconverted templates), `TestI18nParityCoversEveryMarker`, `TestI18nEnglishPages`, `TestDirectRenderHandlersFollowLanguage`, all red-proofed. Design `architecture/10-localisation.md` | Go-side messages and three app-name page titles (R-557 after R-553, R-566), hub e-mails (R-558), console/download page (R-559), catalog copy (R-560), guide + English stranger drill (R-561); ASCII-only Hungarian invisible to the English page test (R-565). **Hungarian households see the switch only once the fleet floor reaches 0.250.0** |
|
||||||
|
| **The box tells visitors apart — a stranger's wrong passwords lock only the stranger** (dashboard, setup gate, apps that read the visitor from the right) | controller v0.286.1 (`felhom-tunnel`, traefik trust of cloudflared only, `clientaddr.go`), catalog router resets | **PARTIAL** | `audits/visitors-2026-10-01/A/L2-demo-hp-real-tunnel.txt` (real tunnel: the dashboard counter keyed on the visitor `37.191.56.193`, locked after 5 while forging a new X-Forwarded-For each try); `A/L1-9202-live.txt` (simulated tunnel: the household from another address in at once); `A/bookstack-3.6.txt` | The second OUTSIDE address on the real tunnel was refused by Cloudflare before the box → R-779; right-walking app settings → R-776; Emby/Jellyfin LAN rights → R-777 |
|
||||||
|
| A permanent household gate with family accounts in front of an app (Grimmory, MeTube) | spike only | **MISSING** (spiked, passed) | `audits/permanent-gate-2026-10-01/VERDICT.md` | → R-780 (operator go/no-go) |
|
||||||
| Multiple household users / per-person accounts | — | **MISSING** | — | Single dashboard password; acceptable for alpha → R-15 |
|
| Multiple household users / per-person accounts | — | **MISSING** | — | Single dashboard password; acceptable for alpha → R-15 |
|
||||||
| WireGuard base infra always-on; OOB operator access (felhom-sshd, /32 peer) | agent v0.72, hub v0.35 | **IMPLEMENTED** | `SPIKE-oob-wg-operator-peer-2026-07-05`, `SPIKE-felhom-sshd-2026-07-05` | Mutual-repair desired-state arc not built → R-13. **CHECKED 2026-08-08 (R-260) and this row was NOT claiming something untrue** — it claims the capability is implemented, never that it is monitored, so no correction was owed. What WAS untrue is narrower and sat one layer down: **the hub's own OOB health check could not see whether the operator's key was installed.** `HostOOBRow` mirrored five of the agent's eight OOB fields, so `operator_key_configured` — emitted every heartbeat since agent v0.72.0, i.e. from this row's own vintage — was discarded by `encoding/json` on arrival, and `oobDegraded` returned `ok` for a box with felhom-sshd active, reachable, a valid config, a configured peer and **no operator key at all**. `operator_peer_configured`, which it did read, only says the peer IP is in desired-state — that OOB is MEANT to work, not that entry is possible. Fixed hub v0.99.0; the missing key now degrades and the alert NAMES it; a stanza too old to carry the field is reported distinctly and is never a silent ok. Pinned end-to-end from raw report JSON by `TestHostOOB_MissingOperatorKey_EndToEnd` and `TestHostOOB_NoKeyField_IsNotSilentlyOK_EndToEnd` |
|
| WireGuard base infra always-on; OOB operator access (felhom-sshd, /32 peer) | agent v0.72, hub v0.35 | **IMPLEMENTED** | `SPIKE-oob-wg-operator-peer-2026-07-05`, `SPIKE-felhom-sshd-2026-07-05` | Mutual-repair desired-state arc not built → R-13. **CHECKED 2026-08-08 (R-260) and this row was NOT claiming something untrue** — it claims the capability is implemented, never that it is monitored, so no correction was owed. What WAS untrue is narrower and sat one layer down: **the hub's own OOB health check could not see whether the operator's key was installed.** `HostOOBRow` mirrored five of the agent's eight OOB fields, so `operator_key_configured` — emitted every heartbeat since agent v0.72.0, i.e. from this row's own vintage — was discarded by `encoding/json` on arrival, and `oobDegraded` returned `ok` for a box with felhom-sshd active, reachable, a valid config, a configured peer and **no operator key at all**. `operator_peer_configured`, which it did read, only says the peer IP is in desired-state — that OOB is MEANT to work, not that entry is possible. Fixed hub v0.99.0; the missing key now degrades and the alert NAMES it; a stanza too old to carry the field is reported distinctly and is never a silent ok. Pinned end-to-end from raw report JSON by `TestHostOOB_MissingOperatorKey_EndToEnd` and `TestHostOOB_NoKeyField_IsNotSilentlyOK_EndToEnd` |
|
||||||
| The operator can see WHERE a managed host is — its LAN address and its WireGuard address, on the host page | agent **v0.119.0**, hub **v0.85.0** | **PROVEN-LIVE** (2026-07-31) | `audits/host-addresses-visible-2026-07-31.md` | Before this the LAN IP was **not reportable at all** — `HostMetrics` carried no address of any kind — and the WG IP existed only in `/offsite`'s peer table keyed by pubkey (peer→host, never host→peer). New wire field `addresses[]`, one row per (interface, address); `IsGlobalUnicast()` is the whole filter, chosen by MEASURING both demo boxes, and it needs no veth/fwbr denylist because that plumbing carries no IP. Rendered live on both 0.119.0 hosts matching their `ip addr` ground truth exactly. **Two honesty properties carry the risk and are both red-proofed:** WireGuard shows the hub ALLOCATION and whether the box CONFIRMS holding it (allocation alone cannot tell a live tunnel from a peer never applied), and an agent below 0.119.0 renders **UNKNOWN, never "no addresses"** — proven live on `drill-r50-0a4f9a` (0.113.0). **Not covered:** a two-LAN-bridge box and a real WG drift, neither of which exists to observe |
|
| The operator can see WHERE a managed host is — its LAN address and its WireGuard address, on the host page | agent **v0.119.0**, hub **v0.85.0** | **PROVEN-LIVE** (2026-07-31) | `audits/host-addresses-visible-2026-07-31.md` | Before this the LAN IP was **not reportable at all** — `HostMetrics` carried no address of any kind — and the WG IP existed only in `/offsite`'s peer table keyed by pubkey (peer→host, never host→peer). New wire field `addresses[]`, one row per (interface, address); `IsGlobalUnicast()` is the whole filter, chosen by MEASURING both demo boxes, and it needs no veth/fwbr denylist because that plumbing carries no IP. Rendered live on both 0.119.0 hosts matching their `ip addr` ground truth exactly. **Two honesty properties carry the risk and are both red-proofed:** WireGuard shows the hub ALLOCATION and whether the box CONFIRMS holding it (allocation alone cannot tell a live tunnel from a peer never applied), and an agent below 0.119.0 renders **UNKNOWN, never "no addresses"** — proven live on `drill-r50-0a4f9a` (0.113.0). **Not covered:** a two-LAN-bridge box and a real WG drift, neither of which exists to observe |
|
||||||
|
|||||||
@@ -894,6 +894,11 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
|
|||||||
| **R-780** | **[P2-MEDIUM] A permanent household gate with family accounts — the spike PASSED; the build waits for the operator's go (`09` §3 decision 63).** Measured on 9202 2026-10-01 (`audits/permanent-gate-2026-10-01/VERDICT.md`, exit test committed before): a stranger reached nothing of Grimmory or MeTube (36 requests incl. websockets); family members with their own logins got in for 30 days, logout worked; a stranger's guesses locked only the stranger; Grimmory's OPDS/Kobo/KOReader worked through anchored path exceptions with the app's own login; the family login never opened the dashboard; +0.4 ms per request; the gate down → apps refuse (500). **Build requirement F1:** exceptions must be anchored (`PathPrefix(/api/v1/opds)` also matched `/api/v1/opdsx`). **Cost:** 2 sessions (controller release; catalog — Grimmory and MeTube published behind it). If nothing is decided: Grimmory stays held (R-775) and MeTube unpublished (R-767). | **WAITING-ON-OPERATOR — rank P2-MEDIUM; owner: operator (go/no-go), CC builds** |
|
| **R-780** | **[P2-MEDIUM] A permanent household gate with family accounts — the spike PASSED; the build waits for the operator's go (`09` §3 decision 63).** Measured on 9202 2026-10-01 (`audits/permanent-gate-2026-10-01/VERDICT.md`, exit test committed before): a stranger reached nothing of Grimmory or MeTube (36 requests incl. websockets); family members with their own logins got in for 30 days, logout worked; a stranger's guesses locked only the stranger; Grimmory's OPDS/Kobo/KOReader worked through anchored path exceptions with the app's own login; the family login never opened the dashboard; +0.4 ms per request; the gate down → apps refuse (500). **Build requirement F1:** exceptions must be anchored (`PathPrefix(/api/v1/opds)` also matched `/api/v1/opdsx`). **Cost:** 2 sessions (controller release; catalog — Grimmory and MeTube published behind it). If nothing is decided: Grimmory stays held (R-775) and MeTube unpublished (R-767). | **WAITING-ON-OPERATOR — rank P2-MEDIUM; owner: operator (go/no-go), CC builds** |
|
||||||
| **R-781** | **[P3-LOW] The catalog's `scripts/test_gate_decoys.py` fails 4 of its own "genuine" onboarding cases — on the untouched tree.** Measured 2026-10-01 (same 4 FAIL lines before and after this session's checklist edit): the harness copies the catalog into a temp tree with a fake sibling `felhom.eu`, and the REAL published records (radicale, karakeep, dawarich) name evidence that the fake sibling lacks, so a complete record reads incomplete. The gate itself (`onboarding`) is green on the real tree. **Needs:** the genuine cases build their own records, or the fake sibling mirrors the cited evidence. | **READY — rank P3-LOW; owner: CC (catalog)** |
|
| **R-781** | **[P3-LOW] The catalog's `scripts/test_gate_decoys.py` fails 4 of its own "genuine" onboarding cases — on the untouched tree.** Measured 2026-10-01 (same 4 FAIL lines before and after this session's checklist edit): the harness copies the catalog into a temp tree with a fake sibling `felhom.eu`, and the REAL published records (radicale, karakeep, dawarich) name evidence that the fake sibling lacks, so a complete record reads incomplete. The gate itself (`onboarding`) is green on the real tree. **Needs:** the genuine cases build their own records, or the fake sibling mirrors the cited evidence. | **READY — rank P3-LOW; owner: CC (catalog)** |
|
||||||
| **R-782** | **[P3-LOW] Two side observations of the R-753 sweep, inferred, not measured:** glance's seeded `glance.yml` has no `auth:` block (the dashboard is public to anyone with the address), and homepage's `/api/*` refuses a Host not in `HOMEPAGE_ALLOWED_HOSTS`, which the template does not set (widgets may 400). **Needs:** measure both on 9202; glance: decide whether a public link dashboard is intended (the setup gate does not cover it after setup). | **READY — rank P3-LOW; owner: CC (catalog)** |
|
| **R-782** | **[P3-LOW] Two side observations of the R-753 sweep, inferred, not measured:** glance's seeded `glance.yml` has no `auth:` block (the dashboard is public to anyone with the address), and homepage's `/api/*` refuses a Host not in `HOMEPAGE_ALLOWED_HOSTS`, which the template does not set (widgets may 400). **Needs:** measure both on 9202; glance: decide whether a public link dashboard is intended (the setup gate does not cover it after setup). | **READY — rank P3-LOW; owner: CC (catalog)** |
|
||||||
|
| **R-783** | **[P3-LOW] SparkyFitness: three wrong sign-ins by anyone shut EVERY visitor out of sign-in for ~10 s — a stranger retrying every 10 s keeps the household out.** MEASURED 2026-10-01 on 9202 through the simulated tunnel (`audits/visitors-2026-10-01/C/box/sparky-box.txt`): better-auth's sign-in limit (3 per 10 s) is keyed on one address — it reads the LEFTMOST X-Forwarded-For, whose chain the R-753 router reset removes, so its frontend nginx hands it traefik's address; the household from another address got 429 at 3 s and 10 s, in at 16 s. Not forgeable (a rotating forged address did not escape). better-auth's header setting is not exposed as an env by SparkyFitness. **Needs:** accept (10 s), or an upstream setting for better-auth's `ipAddressHeaders` + a right-walking reader. | **OPEN — rank P3-LOW; owner: CC** |
|
||||||
|
| **R-784** | **[P2-MEDIUM] SparkyFitness's licence forbids commercial use: "may not be used, directly or indirectly, in any product, service … intended for … commercial advantage … without prior written permission from the author" — and Felhom is a paid service that offers it in its catalog.** READ 2026-10-01 (`audits/visitors-2026-10-01/C/C0-license.txt`): a custom licence (GitHub: NOASSERTION), the same at the pinned tag v0.17.3 and at `main`; termination clause 7 ("cease all use"). SparkyFitness was named as wger's replacement for fitness. **Needs (operator):** (A) hide it from new installs (`lifecycle: hidden`) until the author gives written permission, and ask; (B) ask first and keep it offered meanwhile; (C) keep it. Recommended A. If nothing is decided it stays offered. | **WAITING-ON-OPERATOR — rank P2-MEDIUM; owner: operator** |
|
||||||
|
| **R-785** | **[P3-LOW] SparkyFitness is pinned 11 releases and a major behind upstream (v0.17.3; upstream v1.7.3, v1.6.0 dated 2026-07-24).** READ 2026-10-01 (`audits/visitors-2026-10-01/C/bench/C1-previous-tag.txt`). **Needs:** an update walk 0.17 → 1.x through the ladder (bench + box), after R-784 is decided. | **OPEN — rank P3-LOW; owner: CC (after R-784)** |
|
||||||
|
| **R-786** | **[P3-LOW] SparkyFitness's onboarding record has six open rows** (`app-catalog-felhom.eu/onboarding/sparkyfitness.md`): 0.5 runtime internet (food search providers), 0.7 the phone app's sign-in route through traefik, 1.6 the env names the server reads, 1.7 the entrypoint read, 5.4 a second memory watch at another limit, 8.3 no logo/screenshots on felhom.eu (404). Everything else measured this session (bench + 9202). **Needs:** each row measured, or n/a with a reason. | **READY — rank P3-LOW; owner: CC (catalog)** |
|
||||||
|
| **R-787** | **[P2-MEDIUM] No catalog app's LICENCE has been checked against Felhom being a paid service; the first look (SparkyFitness, R-784) found a non-commercial-only licence.** The new-app checklist row 0.1 binds apps opened from 2026-10-01; the 53 exempt apps were never read. **Needs:** a read of every template's licence at its pinned tag (SPDX + any custom terms), one table, and the non-open ones to the operator. | **READY — rank P2-MEDIUM; owner: CC (read), operator (decisions)** |
|
||||||
|
|
||||||
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
|
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
|
||||||
One row per dated check. The R-number must have a row above. Dates are UTC.
|
One row per dated check. The R-number must have a row above. Dates are UTC.
|
||||||
|
|||||||
Reference in New Issue
Block a user