Big steps close-out: Wanderer, Termix, Homepage, Seerr live (bench + box + undo); R-1335/R-1311/R-1320/R-1317 closed, R-1338 opened, R-1334 VERIFY (0 refused in two dry runs, no login); 09 decision 279 + [DESIGN] path rule; STATUS, CONTEXT, evidence
gates / gates (push) Successful in 2m53s
gates / gates (push) Successful in 2m53s
This commit is contained in:
1 parent
3c0a33c1a0
commit
1be7aa87dc
141 files changed
+14955
-11
No files matched your search
@@ -26,6 +26,15 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-10-28 (afternoon) — the big steps: R-1335, R-1311, R-1320 and R-1317 closed (full text of the rows: `git show ac95ca4f:documentation/backlog/OPEN-ITEMS.md`)
|
||||
|
||||
| Row | What | Closed | Evidence |
|
||||
|---|---|---|---|
|
||||
| **R-1335** | **Wanderer's big step v0.20.0 → v0.21.2 (the successor of R-1313): the ActivityPub fix (GHSA-fprh-3pp9-68m9) and the HTTP2Shell runtime fix.** (P3) | CLOSED 2026-10-28 — a normal ladder step live in catalog `1825247` on the operator's yes (CI job 2461 green): bench 9401 `proven` (abort starts and serves), box 9202 `proven` (78 s, soak 600 s), the undo proven on 9202 (28 s back to v0.20.0, the seed reads back). The template keeps PocketBase off the internet and leaves inbound federation OFF (no `POCKETBASE_PROXY_SECRET`, the operator's pick); D59 ended — Wanderer is offered again (`09` §3 decision 279). Meilisearch's memory: R-1338. | `audits/dockerhub-bigsteps-2026-10-28/wanderer/` |
|
||||
| **R-1311** | **Termix (catalog 2.8.0): a stranger can get round the login rate limit by writing their own X-Forwarded-For (GHSA-7j7m-85p4-2cpm); the fix needed the big step.** (P3) | CLOSED 2026-10-28 — D58 (wait for the big step) ended: two normal ladder steps live on the operator's yes, 2.8.0 → 2.9.2 (`e0b9594`, CI 2462) → 26.10.5 (`589c957`, CI 2463), each with bench + box 9202 + the undo proven; 26.10.5 trusts only its own nginx (`trust proxy: loopback`) and carries Node.js 24.22.1. Measured on 9202 (7 wrong logins, 7 forged X-Forwarded-For values): from the LAN traefik replaces the header and the 6th try is HTTP 429 on 2.8.0, 2.9.2 and 26.10.5; from the tunnel's own address with the chain Cloudflare delivers (forged value, then the real visitor address Cloudflare appends) the 6th try is 429 on all three — so the bypass was not reachable on our boxes before either. A chain with no real address appended skipped the limit on every version (Cloudflare does not deliver one). No template change. | `audits/dockerhub-bigsteps-2026-10-28/termix-292/`, `termix-26105/`, `termix-xff/` |
|
||||
| **R-1320** | **Homepage v1.13.2: a medium notice that needs a hand-written credentialed widget (family_gate in front); the fix is v2.7.0, a big step.** (P3) | CLOSED 2026-10-28 — a normal ladder step v1.13.2 → v2.7.0 live on the operator's yes (`1a254d4`, CI 2464): bench + box 9202 + the undo proven; a new fixture reads the dashboard's own first-start services and bookmarks before and after (Homepage keeps no household data through any route); the family gate still answers 302. v2.7.0 also carries Node.js 24.22.1 (HTTP2Shell). | `audits/dockerhub-bigsteps-2026-10-28/homepage/` |
|
||||
| **R-1317** | **Seerr (jellyseerr 2.7.3): GHSA-rc4w (a stranger registers, Plex setups only) and GHSA-mc6w (path traversal to code execution, needs a hostile media server); the fix is `ghcr.io/seerr-team/seerr:v3.5.1`, a big step.** (P3) | CLOSED 2026-10-28 — a normal ladder DEFINITION step live on the operator's yes (`9ef06a7`, CI 2467): the new image, `init: true`, and a start that hands `/app/config` to UID 1000 and runs Seerr as `node`; bench + box 9202 + the undo proven (the bench's bare abort loses the seed on the converted data; the box's undo copy brings it back). A new fixture brings a throwaway Jellyfin for Seerr's first sign-in. v3.5.1 also carries Node.js 22.23.4 (HTTP2Shell). | `audits/dockerhub-bigsteps-2026-10-28/seerr/` |
|
||||
|
||||
## 2026-10-28 (day) — R-1313, R-1322, R-1327 and R-1332 closed: Wanderer hidden, Immich's lane step live, the runtime watch fills its own cache in CI, the 20 runtime apps classified (full text of the rows: `git show 9b4b5fe5:documentation/backlog/OPEN-ITEMS.md`)
|
||||
|
||||
| Row | What | Closed | Evidence |
|
||||
|
||||
@@ -124,7 +124,7 @@ stopping line that lies.
|
||||
| **R-494** | Install & onboarding | P4 | **NARROWED 2026-09-14 by operator ruling → [P3-LOW] the hub COULD create the tunnel at customer creation, for a domain already on Cloudflare. Not blocking: every customer has their own domain and the operator creates the tunnel per day-0 A.1 (`architecture/01-topology-and-trust.md`).** *Original finding, kept:* **[P1-HIGH] A new customer's dashboard has NO reachable address unless the operator hand-makes a Cloudflare tunnel — the link in the setup-code mail is dead.** MEASURED 2026-09-14 on a fresh install from the public ISO (drill intervention **I1**): the claim mail points at `https://felhom.drill0242.felhom.eu`; that name has **no A and no AAAA** record (`dig @1.1.1.1`, control `felhom.enkisfelhom.hu` resolves); the hub has **no tunnel- or DNS-creation code** (`hub/internal/cloudflare/` holds only geo-rule removal; `cf_tunnel_token` is a pasted, optional form field, `configs.go:1478`) — day-0 runbook A.1 makes it a manual Cloudflare-dashboard step that nothing on the customer-create page asks for; the box's own split-horizon resolver on the appliance LAN IP answered `google.com` but not the dashboard name at 13:27:39Z; the agent applied the record at **13:27:44Z** (`lanresolver: applied split-horizon record … ip=192.168.0.158`, 3 m 46 s after the controller started), so the box CAN answer the name — **but only to a device that uses the box as its DNS server, and no document, screen or mail tells a household to do that**; the router and the installer-offered DNS answer nothing. The page was reachable only at the guest's LAN address with the name forced (`curl --resolve …:443:192.168.0.158`). **A volunteer could not have done that.** **What it needs:** an operator ruling — the hub creates the tunnel and DNS at customer creation, or the product gives a household a LAN address that works with no DNS change. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: operator-side automation; the operator creates the tunnel by hand per the day-0 runbook.** | — | — | CC |
|
||||
| **R-504** | Install & onboarding | P4 | **[P3-LOW] `iso.felhom.eu` cannot show an index page on its own — its root returns 404, and the download page lives on the website instead.** MEASURED 2026-09-14: `https://iso.felhom.eu/` and `/index.html` → 404; only named objects answer. The host is an R2 bucket behind a custom domain; whether R2 would serve an uploaded `index.html` at `/` was **not measured** (uploading anything to the public bucket is a publication). The ISO v1.27.0 task puts the Hungarian download page at `felhom.eu/letoltes` (published with the ISO, after the operator's yes). **Remaining:** a redirect from `iso.felhom.eu/` to that page needs a Cloudflare rule the session has no credential for. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (Cloudflare rule)** **Re-ranked 2026-10-03: P3→P4: households are sent to the website's download page; the bare address is cosmetic.** | — | — | operator |
|
||||
|
||||
## Apps & catalog — 24 rows (P2 1, P3 12, P4 11)
|
||||
## Apps & catalog — 25 rows (P2 1, P3 13, P4 11)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
@@ -152,19 +152,17 @@ stopping line that lies.
|
||||
| **R-1331** | Apps & catalog | P3 | **Paperless-ngx 2.20.15 carries Python Brotli 1.1.0 (CVE-2026-111497, fixed 1.1.1/1.2.1)** — its web side only COMPRESSES (whitenoise, compression_middleware); the DECODERS are urllib3's and httpx's, for replies to requests Paperless itself makes (its own outbound calls), never for what a visitor sends. Read in the running container on demo-hp 9201, 2026-10-27. 2026-10-28: the newest release 3.3.4 still carries Brotli 1.2.0 and is a new main version — no fixed release yet (`audits/runtime-fixes-2026-10-28/TABLE.md`). | OPEN — no step tonight (decided by CC unattended — operator may reverse): the bad path needs a hostile server Paperless calls | — | take the first Paperless release with Brotli ≥ 1.2.1 as a normal step | CC |
|
||||
| **R-1336** | Apps & catalog | P3 | **ActualBudget has no release with the HTTP2Shell fix (CVE-2026-111497): 26.9.0 carries Node.js 24.20.0 and the newest 26.10.0 still Node.js 24.21.0 (fixed: 24.22.1), read from both images.** `node app.js` serves the app; the setup gate covers it until the first account exists, then its own password. No box runs it. `audits/runtime-fixes-2026-10-28/TABLE.md` | OPEN — filed 2026-10-28 (runtime fixes) | upstream: an image with Node ≥ 24.22.1 | the watch lists it nightly (R-1327); take that release as a lane step | CC |
|
||||
| **R-1337** | Apps & catalog | P3 | **Calibre-Web (v4.0.8, the newest) loads a bad Brotli decoder: its `cps.py` maps Python Brotli 1.2.0 (fixed 1.2.1), Pillow's libbrotlidec 1.2.0 and Debian's libbrotlidec 1.1.0** — read from the process's maps on demo-hp 9201 (`audits/runtime-fixes-2026-10-28/calibre-web-brotli.txt`). Exposure: the Python decoder is urllib3's, for replies to the app's own outbound calls (book metadata), not for what a visitor sends — the Paperless shape (R-1331). No fixed release exists. `audits/runtime-fixes-2026-10-28/TABLE.md` | OPEN — filed 2026-10-28 (runtime fixes) | upstream: an image with Brotli ≥ 1.2.1 | take that release as a lane step; Debian's libbrotli1 fixed version is not known (R-1327's table has no Debian line) | CC |
|
||||
| **R-1338** | Apps & catalog | P3 | **Wanderer's Meilisearch fills whatever memory limit it gets: 92–100 % anon of 256 MB (bench and 9202), still 88.8 % of 384 MB on the bench (2026-10-28)** — it sizes its indexing memory from the memory it sees (its default: 2/3 of the available RAM), so a higher limit alone does not end the `memory_tight` mark of Wanderer's step. No kill in any run. `audits/dockerhub-bigsteps-2026-10-28/wanderer/` | OPEN — filed 2026-10-28 (big steps) | — | set `MEILI_MAX_INDEXING_MEMORY` (e.g. a third of the limit) in the template, measure on the bench, a re-test entry | CC |
|
||||
|
||||
|
||||
## App updates — 14 rows (P2 2, P3 12)
|
||||
## App updates — 11 rows (P2 2, P3 9)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **R-1315** | App updates | P2 | **Gitea 1.27.3: two critical notices for any signed-in user (7cq7, 6pv5) and two with no login (c3cr, hf55); the fix is 28.2.0 (1.28 was renamed 28.0.0 — no 1.27.4 exists).** 28.0 routes git traffic through a new proxy with egress settings. GHSA-rqhx (SSRF through migration) has no fix at all (`PICKS.md` pick 2). No box runs Gitea. `audits/security-app-updates-2026-10-27/TRIAGE.md` | OPEN — **RULED D57 = keep repository import on** (operator 2026-10-27 21:33, `09` §3 decision 273) — judged again in the big step. — filed 2026-10-27 (security app updates, Part 3: not done today) | — | the big step 28.2.0: read the 28.0 proxy settings against our template; bench + 9202 + undo; a STOP | CC |
|
||||
| **R-1316** | App updates | P3 | **Gokapi v1.9.6: GHSA-3c22 (high, self-XSS for the only account); the fix is v2.x, a big step** — a user system with no downgrade, and our first-boot config seed must be captured again. No box runs Gokapi. `audits/security-app-updates-2026-10-27/TRIAGE.md` | OPEN — filed 2026-10-27 (security app updates, Part 3: not done today) | — | the big step v2.2.4 with a new first-boot seed; bench + 9202 + undo; a STOP | CC |
|
||||
| **R-1317** | App updates | P3 | **Seerr (jellyseerr 2.7.3): GHSA-rc4w (a stranger registers, Plex setups only) and GHSA-mc6w (path traversal to code execution, needs a hostile media server); the fix is `ghcr.io/seerr-team/seerr:v3.5.1`, a big step** — a new image name, runs as UID 1000 (`/app/config` must be chowned), a config migration. No box runs it. `audits/security-app-updates-2026-10-27/TRIAGE.md` **2026-10-28 (R-1332): the HTTP2Shell runtime fix rides this big step too** — jellyseerr 2.7.3 carries Node.js 22.18.0, seerr v3.5.1 Node.js 22.23.4 (read from both images); `pnpm start` runs Node, behind the setup gate. | OPEN — filed 2026-10-27 (security app updates, Part 3: not done today) | — | the big step to seerr v3.5.1: the ownership step, the image rename; bench + 9202 + undo; a STOP | CC |
|
||||
| **R-1318** | App updates | P3 | **Karakeep 0.33.2 + chrome 151: two CISA-KEV V8 bugs in the crawler's Chromium (runs `--no-sandbox`), reached by content the household saves; the fix is 0.34.1 + chrome 154.0.8037.57-r1** — an auth library swap (everyone signed out, e-mails lowercased) and a chrome rebuilt on full Chrome (memory limit and healthcheck to measure again). No box runs it. `audits/security-app-updates-2026-10-27/TRIAGE.md` | OPEN — filed 2026-10-27 (security app updates, Part 3: not done today) | — | the big step: measure chrome 154's memory; bench + 9202 + undo; a STOP | CC |
|
||||
| **R-1319** | App updates | P3 | **Paperless-ngx 2.20.15: GHSA-2jhj (low; needs a setting we do not set — not reachable); the fix is 3.3.4, a big step** (encryption and API v1 removed, the search index rebuilt). It runs on all four test boxes. Not urgent. `audits/security-app-updates-2026-10-27/TRIAGE.md` | OPEN — filed 2026-10-27 (security app updates, Part 3: not done today) | — | the big step 3.3.4 when the catalog takes Paperless 3 for other reasons; read our API v1 use first | CC |
|
||||
| **R-1320** | App updates | P3 | **Homepage v1.13.2: a medium notice that needs a hand-written credentialed widget (family_gate in front); the fix is v2.7.0, a big step** (the auth rework). No box runs it. `audits/security-app-updates-2026-10-27/TRIAGE.md` **2026-10-28 (R-1332): the HTTP2Shell runtime fix rides this big step too** — v1.13.2 carries Node.js 22.22.3 (`node server.js`, as root, behind the family gate), v2.7.0 Node.js 24.22.1 (read from both images); no fixed v1.x exists. | OPEN — filed 2026-10-27 (security app updates, Part 3: not done today) | — | the big step v2.7.0; bench + 9202 + undo; a STOP | CC |
|
||||
| **R-1335** | App updates | P3 | **Wanderer's big step v0.20.0 → v0.21.2 (the successor of R-1313): it carries the ActivityPub fix (GHSA-fprh-3pp9-68m9) AND the HTTP2Shell runtime fix (Node.js 22.23.1 → 22.23.4, read from both images).** Wanderer is hidden since 2026-10-28 (D59); no box runs it. A main-version move by the operator's ruling (D59), so a normal ladder step, never the lane. `audits/runtime-fixes-2026-10-28/TABLE.md` | OPEN — filed 2026-10-28 (runtime fixes) | — | the big step: bench (R-739: the bench cannot run Wanderer yet) or a box-only walk on 9202 with the undo; a STOP; then un-hide | CC |
|
||||
| **R-1321** | App updates | P3 | **Tandoor 2.6.15: GHSA-5f74 (high, an XXE file read) and GHSA-69q3 (any user); the fix is 2.7.2** — 2.7.0 removes settings and an endpoint and rewrites the login pages (our probe path — not sure), and 2.7.0/2.7.1 break invite links with sign-up off, so straight to 2.7.2. No box runs it. `audits/security-app-updates-2026-10-27/TRIAGE.md` | OPEN — filed 2026-10-27 (security app updates, Part 3: not done today) | — | a normal step to 2.7.2 with the probe path checked; bench + 9202 (+ undo if a migration); a STOP | CC |
|
||||
| **R-1324** | App updates | P3 | **RomM 5.4's first start can run a 15–20 minute migration on a big library (upstream notes) — longer than the update's check window, so a household's night step could be undone for slowness, not for a fault.** Today's walk (9202, a small seed) took the step in seconds; demo-hp's RomM takes it tonight (security-fix step, catalog `06e603e`). `audits/security-app-updates-2026-10-27/triage-notes/romm.md` | OPEN — filed 2026-10-27; WATCH demo-hp's night leg line | — | read demo-hp's night leg line for romm (done / undone, the time); if undone for time: give the step a longer check or the `needs_person` mark | CC |
|
||||
| **R-1238** | App updates | P2 | **Plex has no test record and no upgrade fixture, so the night leg never updates Plex on a household box.** Filed 2026-10-23. Catalog `6cc317d` moved Plex 1.41.4.9463 → 1.43.5.11029-6bf8af3f0 (the Plex2Shell fix) on the operator's ruling, with NO `update_ladder` entry (the move gate refused; pushed `--no-verify`, the one time). With no ladder, a person's Update press pins the current compose (`stacks/ladder.go` `nextLadderStep`), but the night leg skips the app (`unattended.go` → `LegSkipNoTestRecord`), so every future Plex fix reaches a household only by a hand press. `scripts/upgrade_fixtures.py` has no Plex fixture. A likely seed route is NOT measured: an UNCLAIMED Plex answers a private-address request with no token (`architecture/13-threat-model.md`, the R-1045 row), so the bench may create and read back a library section through Plex's own API without a plex.tv account. **Needs a spike:** prove that route on the bench (seed a section, swap the image, read it back), add the fixture, then `upgrade-test.py --write-ladder` for the current pin — the box walk on 9202 needs the same route behind the setup gate. Evidence: `audits/plex-demo-boxes-2026-10-23/`. **2026-10-23 (later): the NEXT Plex fix need not wait for this** — the security-fix lane (`09` §3 decision 255, D39 = B, controller 0.341.0) takes a box-only step with `seed: "none"` at night (proven on 9202 with Plex 1.43.4 → 1.43.5, `audits/security-lane-2026-10-23/`). The bench fixture stays the aim: it is the only check that reads Plex's data back after a step. | **OPEN** | a claimed-Plex seed route on the bench | the spike above | CC |
|
||||
@@ -242,7 +240,7 @@ stopping line that lies.
|
||||
| **R-1275** | Storage & devices | P4 | **An English household sees the drive names in Hungarian** on the install page (and wherever a storage label shows): „Belső tárhely — 52.0 GB free (default)”, „Külső tárhely (hdd_1)” (catalog sweep 2026-10-24, `audits/catalog-sweep-2026-10-24/navidrome/page-install-en.txt`). The label is stored once, in Hungarian, when the drive is found (`felhom-controller/controller/internal/settings/settings.go` `InferStorageLabel`); a household may rename it. | OPEN | — | Render a label that still equals the inferred default in the reader's language; a test per language | CC |
|
||||
| **R-1276** | Storage & devices | P4 | **„Remove with data” leaves the app's empty parent folder** (`<drive>/appdata/<app>`) when the template binds a subfolder: Grimmory, RomM, Paperless-ngx (catalog sweep 2026-10-24; 4 KB each; the bound folders were removed). Harmless (empty, not counted as old data), untidy for a household that browses the drive. | OPEN | — | Remove the parent when it is empty after the bound folders go; a test | CC |
|
||||
|
||||
## Security & access — 30 rows (P1 2, P2 5, P3 16, P4 7)
|
||||
## Security & access — 29 rows (P1 2, P2 5, P3 15, P4 7)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
@@ -256,7 +254,6 @@ stopping line that lies.
|
||||
| **R-1141** | Security & access | P3 | **`felhom-host-install --rotate-recovery` exists only as part of a full installer re-run, which also reinstalls the vouched agent and the root files.** On a box running a newer agent than the vouched one (demo-hp on the candidate, 2026-10-17) it would move the agent back; on a household box it is a much larger act than a password change. The 2026-10-17 rotation therefore ran the installer's own `step_break_glass` ALONE (`audits/agent-allowlist-rotation-2026-10-17/P3/`). | **NARROWED 2026-10-18 — built on `main`, not published.** `felhom-host-install.sh --rotate-recovery-only` runs step 4b alone (felhom.eu `7d64038f`): reads the hub, host id and host key from the agent config, refuses byo and every other mode flag, asks the hub's `/healthz` before the password changes, never touches the agent or root files. The default install path is unchanged in behaviour (harness verdicts and 9 transcripts identical old vs new; demo-hp `--preflight-only` identical; `--rotate-recovery-only --dry-run` on demo-hp changed nothing). `step_break_glass` moved, byte-identical, ahead of the argument parse. `break-glass.md` §5 points at it. Publishes only with the next `installer-v…` tag. | the next installer tag | Cut the next `installer-v…` tag (with the other untagged installer changes); close | CC |
|
||||
| **R-1258** | Security & access | P3 | **ep0's iperf3 line-test server takes any tunnel peer's test: one box (root on its host) can keep it busy for the fleet or ask a long reverse test that fills ep0's uplink.** Found 2026-10-24 (SEC-2 X2): `felhom.eu/scripts/ep0-iperf3/felhom-iperf3.service:17` — no authorized users, no server-side duration or rate cap; one client at a time (`felhom-agent/internal/linetest/iperf.go:263-264` → `busy`; 6 tries a day). Static (no ep0 change in this run). | OPEN | — | per-box iperf3 users or server-side caps, tried on a scratch VM first | CC |
|
||||
| **R-1307** | Security & access | P3 | **Packages inside our own images are not watched** — the Debian packages of the controller image, the Alpine packages of the hub and Samba images (Samba's version is whatever Alpine 3.24 ships). The security watch (R-1303) reads only the base image's point release (BEHIND); Debian's DSAs and Alpine's secdb are not read. `audits/security-watch-2026-10-26/SPIKE.md` „cannot see" | OPEN — filed 2026-10-26 | — | add Alpine secdb (machine-readable JSON per release) and Debian's tracker for the packages the images install, or a scanner run on the built images in CI | CC |
|
||||
| **R-1311** | Security & access | P3 | **Termix (catalog 2.8.0): a stranger can get round the login rate limit by writing their own X-Forwarded-For (GHSA-7j7m-85p4-2cpm), and our template keeps that header** (docmost and paperless strip it, R-753). The upstream fix needs Termix's big step (2.8 → 2.9.2 → 26.10.5). No box runs Termix (hub 2026-10-27). `audits/security-app-updates-2026-10-27/TRIAGE.md` **2026-10-27: Termix's big step (2.8.0 → 2.9.2 → 26.10.5, two hops — 2.9 must start once; calendar versions; guacd dropped) was not done today; this row carries it.** **2026-10-28 (R-1332): the HTTP2Shell runtime fix rides the same big step** — 2.8.0 carries Node.js 26.9.0, 2.9.2 still Node.js 26.10.0 (bad), 26.10.5 Node.js 24.22.1 (read from the images). | OPEN — **RULED D58 = wait for the big step; the header is not stripped** (operator 2026-10-27 21:33, `09` §3 decision 274). — filed 2026-10-27 | the operator: a template change (strip the header like R-753, so every visitor shares the tunnel's one address and the limit can lock the household out — the Grimmory shape, R-775) or wait for the big step | the pick in `PICKS.md`; or the big step | CC |
|
||||
| **R-955** | Security & access | P4 | **The cross-box check of the hub API is done in source and tests but not fully proven: if a later check masks a missing id check, a box's key could read or change another box's hub record without any test noticing.** Read 2026-10-12: every box-facing `/api/v1` route takes the box from its own key or refuses a different id; 14 routes already had a refusal test, 5 got one (`crossbox_refusal_test.go`). Left: red-prove the 4 new tests that were not red-proved (as the restore-directive one was), and the live read-only check with one demo box's key against the other demo box's id. | OPEN — filed 2026-10-12 by the security review | — | Red-prove the 4 tests; run the live GET check on the two demo boxes | CC |
|
||||
| **R-924** | Security & access | P2 | **The operator signing keys exist only on DooPlex, with no passphrase and in no backup — a loss of DooPlex means no box ever accepts a signed agent update, config bundle or OS step again without an on-site re-enrollment.** MEASURED 2026-10-09: `/mnt/5_hdd/felhom.eu/felhom-op-operational`, `felhom-rec-recovery`, `felhom_op_ed25519` (0600 `kisfenyo`); `ssh-keygen -y -P ''` opens all three (no passphrase); `/mnt/5_hdd/felhom.eu` is in no backup set, the off-site job does not carry them. `04` §1 says the operational key is "passphrase-protected" and the recovery key "kept offline" — today neither holds (custody ruling 1 of 2026-09-16 put both on DooPlex for this phase; `04` §3.1). Both keys share one fate, so the cold key cannot do its one job (re-pin after losing the operational key). `runbooks/total-loss-of-dooplex.md` S6. | NARROWED — **2026-10-23 (D32):** S6a/S6b are on the current sheet. LEFT: the operator prints it. **NARROWED 2026-10-09 — operator ruling: option A** (print both AND copy them). The three key files and their public halves ride the nightly encrypted DooPlex→ep0 copy (`scripts/dooplex-offsite/`, a missing key refuses the push); the Sunday restore test proves each private key still derives its own public half (never printed). **LEFT:** the operator prints S6a/S6b (`runbooks/break-glass-sheet.md`); the keys still have no passphrase (custody ruling 1 of 2026-09-16 stands until the first sale). **2026-10-23 night — survey (`audits/dooplex-survival-2026-10-22/PLAN.md`, G2):** the three key files are on `sda1`, the same disk as DooPlex's local backup tree; their only off-box copy is the S1-encrypted nightly one. Passphrase: unchanged by the custody ruling. | operator: print | Operator: print the two keys; then close | operator |
|
||||
| **R-132** | Security & access | P3 | **`curl -w '%{redirect_url}'` reconstructs the request URL WITH its basic-auth credential** — so a `-u ":$HUB_PW"` call that never put the password in a URL still printed it **Merged 2026-10-05 from R-350 (duplicate):** (1) 2026-08-20 occurrence: POST /configuration/artifacts answers 303; leak lives only in the CC transcript under ~/.claude/projects/ on DooPlex, not in git/evidence (checked then). (2) `-v` and `--libcurl` also re-render the credential, not only %{redirect_url}; confirm redirects with %{http_code} + follow-up GET. (3) Rotation path: hub /configuration form (current_password/new_password/confirm_password); DB override wins over ConfigMap (break-glass); CC can rotate file-to-file without printing (operator-present-one-time-secrets) if asked. | **NARROWED 2026-10-20 — the rotation is DONE:** the operator changed the hub password on 2026-10-15 and the `HUB_PW` line is gone (`audits/hub-tokens-2026-10-15/C153-live.txt:18`; CC uses its own token since hub 0.152.0). **Left, the operator's:** should the break-glass `password_hash` in `manifests/hub.yaml` (a public repository) be emptied or replaced? The DB override wins at login, so the old hash no longer opens the hub. **WAITING-ON-OPERATOR** — **ACTION: rotate `HUB_PW`** **Folded R-580 2026-10-03** (the same `curl -w %{redirect_url}` credential echo, seen again 2026-09-18). **Note 2026-10-12 (security review):** `manifests/hub.yaml` also carries the hub login's bcrypt hash in this public repository (the break-glass field); when `HUB_PW` is rotated, decide whether that field should stay empty. | the operator's word on the `manifests/hub.yaml` hash field | Happened on 2026-07-31 while red-proofing the R-120 gate: the hub operator password was written to the session transcript by the write-out format, not by the request. `-u` is safe; the *reporting* was not. Rule: read the redirect from `-D -` and grep `^Location:`, never `%{redirect_url}`, on any authenticated call. Rotate the hub password (`/configuration` → Login password; ConfigMap `auth.password_hash` is the reset path) and update `~/.config/credentials` | Viktor |
|
||||
@@ -303,7 +300,7 @@ stopping line that lies.
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **R-1262** | Monitoring & notifications | P3 | **The upload watch treats ANY viewer as an explanation for any amount of upload, so a hacked Plex can send freely while one family member watches.** Found 2026-10-24 (SEC-1 X1): `felhom-controller/controller/internal/upwatch/watch.go:275,299`; `08` §9 states the rule, its limits list did not name this case. | OPEN | — | propose a cap (viewers × a bitrate ceiling) for the operator's R-1244 design; `08`'s limits list names the case meanwhile | CC |
|
||||
| **R-1310** | Monitoring & notifications | P3 | **The security watch (R-1303) flags notices that do not apply and misses fixes that do — measured by reading all 38 flagged pins by hand on 2026-10-27.** False: advisories of ecosystem `actions` (Home Assistant's CI), of another package in the repo (Gitea's runner), with a hostname or ≤-old range (cal.com 2023/2024, Grafana 2022), for the desktop/Electron client (Termix), for the rootless image only (Uptime Kuma); a release hit on a word (Dawarich „no longer receives security fixes", Rallly v4.16.0). Wrong „no fix": Vikunja's six (range `<= 2.6.0`, fixed in 2.7.0), karakeep-chrome (154/156 exist). Missed: Tandoor 2.7.0 names 8 GHSA ids (1 matched), Dawarich 1.16.0 (2FA, export deletion), Wishlist v0.69.0 (critical, no GHSA). BEHIND reads linuxserver `26.09.20260929` as newer than 26.09.2; Seerr follows the renamed-away `fallenbagel/jellyseerr`. `audits/security-app-updates-2026-10-27/TRIAGE.md` NOT-A-FINDING list and `triage-notes/` | OPEN — filed 2026-10-27; **two shapes FIXED the same day** (ecosystem `actions` not listed; an empty fix with a closed range reads „after X"; test W9, red first) | — | teach `security_watch.py`: skip a package that is not the pinned one; match every GHSA id a release body names; ignore `YY.MM.YYYYMMDD` tags; repoint seerr to `seerr-team/seerr`; each with a test seen failing | CC |
|
||||
| **R-1334** | Monitoring & notifications | P3 | **Docker Hub's tag API refuses the watch (HTTP 429) since the DooPlex group was added: 19 DooPlex images NOT CHECKED in the 2026-10-28 night run (job 2446, `hub.docker.com` = 211 requests), and the „could not check registry:docker.io" mail went out (streak 2).** The runtime part is not affected (HEAD + cache). ghcr.io also answered one blob with 429 in the dry run of 07:02 (Immich v3.3.5, while this session read images from the same address). | OPEN — filed 2026-10-28 (runtime fixes) | — | fewer tag-list calls (one list per image across groups, cached by day) or a Docker Hub login in CI (an Actions secret — the operator's); then a night with no Docker Hub 429 | CC |
|
||||
| **R-1334** | Monitoring & notifications | P3 | **Docker Hub's tag API refuses the watch (HTTP 429) since the DooPlex group was added: 19 DooPlex images NOT CHECKED in the 2026-10-28 night run (job 2446, `hub.docker.com` = 211 requests), and the „could not check registry:docker.io" mail went out (streak 2).** The runtime part is not affected (HEAD + cache). ghcr.io also answered one blob with 429 in the dry run of 07:02 (Immich v3.3.5, while this session read images from the same address). | **VERIFY** — BUILT 2026-10-28 midday, measured first (`audits/dockerhub-bigsteps-2026-10-28/part1/`): the 429s came from the BURST, not the count — night 2446 sent 211 tag-list requests in ~2 min from six workers (15 refused), dry run 2450 sent 224 in 3.5 min (none). `fe677bae`: Docker Hub requests spaced (1/s across the workers), a 429 retried with its Retry-After (NOT CHECKED only after the 4th try), tag lists cached 24 h per registry path on the runner (two names of one path fetch once); `8644450d`: the image reader retries a registry 429 too (ghcr.io refused two blob reads in back-to-back dry runs). Tests W14 + retry checks, red first. Dry runs 2458 and 2459: **0 Docker Hub 429, 0 retries** (210 requests over ~7 min). **No Docker Hub login needed.** Each of the two CI runners keeps its own tag cache. `minio/minio:latest` answers 401 — Docker Hub no longer serves it (homelab, R-1328). | — | close after the first CI night with 0 Docker Hub 429 (the 03:40 run of 2026-10-29) | CC |
|
||||
| **R-1020** | Monitoring & notifications | P4 | **`app_repaired_after_crash` reports a duration of 0 s, and the controller log says „in 0s".** `RunAfterUncleanBoot` sets `res.Duration` in a deferred func after the result is already returned (no named result), so the caller sees 0; the `app.yaml` record is right (10,172 ms on demo-hp 2026-10-14, `audits/immich-repair-2026-10-14/D/`). Small, but the controller release for this session is spent (v0.311.0). | **OPEN** — filed 2026-10-14 | — | CC: a named result (or set the duration before return), with a test on the returned value; ride the next controller release | CC |
|
||||
| **R-528** | Monitoring & notifications | P2 | **[P2-MEDIUM] Docker does not report an OOM kill inside a Felhom LXC guest: `OOMKilled` stays false and no `oom` event fires, so the v0.243.0 OOM line is not proven live.** MEASURED 2026-09-15 on scratch 9202 (Docker 29.8.0): Paperless capped at 128M restarted 11 times with `OOMKilled=false` and zero `docker events --filter event=oom`; a memory hog inside the running container was killed (rc 137) with the same silence (`E2-oom-signal-measure-9202.txt`). BIGNIGHT VM 333 did read `oomkilled=true`, so the shape differs by case. **Fix shape:** the agent reads the guest container cgroups' `memory.events oom_kill` counters (host-side, reliable), or the controller alarms on a restart-count trend **RE-MEASURED 2026-09-16 on the DRILL box (fresh install, nested VM 334, Docker in an LXC guest, controller 0.243.0), so the finding is not a property of one machine:** the Paperless webserver was capped at 128 M with `docker update --memory`; it restarted 9-10 times, and all three signals stayed silent - `OOMKilled=false` on every inspect, `docker events --filter event=oom` EMPTY for the whole window, the container's cgroup not visible from inside the guest, and `dmesg` unreadable there. Identical to scratch 9202. So the v0.243.0 OOM line cannot fire on ANY Felhom box as shipped, on either host. Evidence: `audits/evidence-drill-0243-2026-09-16/phase2-m1-oom.txt`. | **WATCHING — DESIGN WRITTEN 2026-10-26 night (the brief's Part 7: build only if time is left; it was not built — the line test and the attended release came first).** The host-side counter EXISTS and is readable (read on demo-hp 2026-10-26): `/sys/fs/cgroup/lxc/<vmid>/ns/system.slice/docker-<64-hex id>.scope/memory.events`, line `oom_kill N`. **Design:** (1) agent: a read-only collector (no root CLI — a sysfs read under the agent's own rights; if not readable, the existing sudoers fence gets one read-only `Cmnd_Alias`) that, for each guest in the `felhom` pool, globs those scopes and keeps `{container_id: oom_kill}`; served on the guest's own local API as `GET /guest/oom-kills` (`withGuest`, the guest's containers only). (2) controller: the 30-s dead-app scan asks once per pass; a RISE of a container's counter = an OOM kill whatever Docker's `OOMKilled` says → the existing `app_oom` line (R-636's per-run grain), `source: cgroup` in the details. An agent without the route = today's behaviour. (3) Tests: a fake cgroup tree (agent), a rising counter → one `app_oom` (controller, red-first). (4) Live: 9202, a throwaway app capped at 64M with a memory hog: the counter rises, `app_oom` names the container while `OOMKilled` reads false. ~1 day, two repos, one agent release. **Earlier:** WATCHING — **2026-10-12: the built part is released** (agent v0.150.0 carries the Docker-approval memory-kill check; the boxes run 0.154.0; hub 0.140.0+ requires a passing `oom_check` before a Docker set is approved). Per decision 157 nothing else is built; the row watches for a box whose Docker reports a false OOM flag. Dated look: 2026-11-12. **READY — rank P2-MEDIUM; owner: CC** **2026-09-17 (chaos night): an OOM WAS detected on a fresh box, and named precisely.** On `tester-1-022354` (controller 0.245.0, guest 9201, 6 GB RAM) immich’s Postgres was killed by the memory limit during its reverse-geocoding import, and the controller pushed `app_oom` (warning, operator-only): „Alkalmazás memóriája elfogyott: immich (immich-postgres) — egy folyamatát a memóriakorlát leállította” — naming the app AND the exact container. The visible consequence was `write CONNECTION_CLOSED immich-postgres:5432` and twelve restarts of immich-server. So on THIS box the OOM scan works and was the fastest route to the diagnosis; recorded here rather than filed as a new row. Evidence: `audits/evidence-chaos-night-2026-09-17/round-2.txt`. **2026-10-05 (burn-down night): a one-page design proposal (no code) is in `audits/night-burndown-2026-10-05/design-R-528.md`** — for the operator. **2026-10-06: STOPPED BEFORE ANY CODE — the measurement contradicted the design** (decision 155 said A then C). On scratch 9202, Docker 29.8.2, the `OOMKilled` flag was TRUE in all four shapes tried: a child process killed while the container kept running (`oom_kill` 0 → 3, flag true) and three main-process kills (exit 137, flag true); an `oom` event each time. The false flags of 2026-09-15 were on Docker 29.8.0. Cost read for the record: one exec read of 21 containers on demo-hp = 1.6 s. `audits/design-build-2026-10-06/`C/. **2026-10-06 18:24: operator ruling, option A (decision 157):** nothing is built; the row stays open as a WATCH for a box whose Docker reports a false flag. **Added:** a Docker engine set may not be approved until it reports a memory kill correctly on the boxes that ran it (built in this row's next session). **2026-10-06 (night): the Docker-approval memory-kill check is BUILT** — hub v0.140.0 (LIVE: the approval waits for a Line truncated
|
||||
| **R-1193** | Monitoring & notifications | P3 | **When the box froze (R-1192), the operator heard only „no report for 45 m" — 28 minutes in — and the household heard nothing at all.** Measured 2026-10-20 against the operator mailbox (the second channel): the freeze began ~13:05 UTC; the hub page read ONLINE / OK at 13:22 (correct inside the 45-minute threshold, R-549); `host_stale` at 13:33, `app_oom` at 13:34 (immich-machine-learning killed by its memory limit), `host_crash_restart` at 14:26 and 16:29 (the two plug pulls), `node_stale` 15:59 / `node_recovered` 16:03. No mail or banner tells the household that its box is not answering, and no operator mail says „the box answers ping but not its dashboard". CORRECTED the same day: the first version of this row said the hub showed OK „throughout" and „nobody was told" — that read only the hub page, not the mailbox. Evidence `audits/rehearsal-2026-10-20/journal.md`. | OPEN | — | Design note (2026-10-21, after R-1192's diagnosis): the freeze's signature is cheap to see from the HOST side — the guest cgroup's memory PSI „full" above ~50 % for a minute, or its `memory.events max` climbing, while the host still reports; the agent could raise `guest_memory_thrash` (operator mail in its own words, not „no report for 45 m") and the household banner „A dobozod most nagyon lassú…". With the swap fix (R-1192) the measured thrash ends within a minute, so this alarm should be built only if a freeze is seen again after the agent release; until then it stays a design. Decide whether a dashboard that does not answer for N minutes while the host still reports should alarm sooner and in its own words (guest-side liveness), and whether the household is told | CC |
|
||||
@@ -323,7 +320,7 @@ stopping line that lies.
|
||||
| **R-1224** | Monitoring & notifications | P4 | **The operator's 1-hour mail cooldown for `wg_tunnel_stale` is per customer, not per host, and `wg_tunnel_ok` does not end it.** Fresh review 2026-10-23 night (`REPORT-night-2026-10-23.md`, H3): `notify/dispatcher.go:620` keys the cooldown `customerID:eventType`. Two hosts of one household going stale within an hour, or stale → ok → stale, store a second event row but mail once (the suppression is in `notification_log`). | OPEN | — | Key the cooldown by `host_id` from the details for host-scoped operator events, and let the matching ok event end it; a dispatcher test with two hosts; rides a hub deploy | CC |
|
||||
| **R-1233** | Monitoring & notifications | P4 | **`[ERROR] [stacks] execCommand failed: exit status 1` names no command.** Seen once a night (2026-10-21 00:30:49 and 2026-10-23 02:17:14 UTC, each right after an app stopped for its dump); the command and stderr go only into the returned error, which the caller may drop (`internal/stacks/manager.go:1757`). An ERROR nobody can judge. | OPEN | — | Log the command's first two words (`docker inspect`, never the arguments — `docker exec … psql` can carry a password) and the stderr's first line; find the caller of the nightly one | CC |
|
||||
| **R-1296** | Monitoring & notifications | P4 | **When ep0's tenancy script refuses, the hub log shows only its one-line error — PBS's own reason (the 403 `missing Datastore.Audit|Datastore.Backup on …`) stays in the script's stderr, which the hub drops on a refusal.** Found 2026-10-26 diagnosing R-1295: the cause was visible only by reading ep0's proxy journal over root SSH. `hub/internal/tenantsync/client.go` `parseResponse` returns the JSON error and ignores stderr; the script keeps secrets off stderr by design (its header), so the tail is safe to log. | OPEN | — | append the last ~3 stderr lines to the refusal error (bounded, never stdout); a test that a secret on stdout never appears | CC |
|
||||
| **R-1328** | Monitoring & notifications | P2 | **DooPlex's own workloads were not in the security watch at all** — about ten public apps there carry a bad Brotli/Node (HTTP2Shell), on the host that runs the hub, Gitea, the registry and the backups. | **VERIFY** — BUILT 2026-10-27 night (commit only): `scripts/dooplex_inventory.py` lists every `image:` of homelab-manifests (89 images, `documentation/security-watch/dooplex-workloads.txt`; NOT Helm-chart images — said in the file); the watch reads them as the group „dooplex" (operator mails only, own block, „Next: the homelab session"); an image in the private registry is NOT CHECKED with the reason. Tests W12, red-proved. The homelab session's list: `documentation/audits/http2shell-2026-10-27/HOMELAB-LIST.md`. Felhom changes nothing on DooPlex. | — | the first CI night shows the DooPlex block; the homelab session works its list | CC |
|
||||
| **R-1328** | Monitoring & notifications | P2 | **DooPlex's own workloads were not in the security watch at all** — about ten public apps there carry a bad Brotli/Node (HTTP2Shell), on the host that runs the hub, Gitea, the registry and the backups. | **VERIFY** — BUILT 2026-10-27 night (commit only): `scripts/dooplex_inventory.py` lists every `image:` of homelab-manifests (89 images, `documentation/security-watch/dooplex-workloads.txt`; NOT Helm-chart images — said in the file); the watch reads them as the group „dooplex" (operator mails only, own block, „Next: the homelab session"); an image in the private registry is NOT CHECKED with the reason. Tests W12, red-proved. The homelab session's list: `documentation/audits/http2shell-2026-10-27/HOMELAB-LIST.md`. Felhom changes nothing on DooPlex. | — | the first CI night shows the DooPlex block; the homelab session works its list. 2026-10-28: `minio/minio:latest` answers HTTP 401 on Docker Hub (the image is no longer published there) — for the homelab session: the pin cannot be checked anonymously any more | CC |
|
||||
|
||||
## Hub & operator — 12 rows (P3 3, P4 9)
|
||||
|
||||
|
||||
@@ -5,6 +5,14 @@ Moved here byte for byte on 2026-10-14 (night session), newest first, so that `S
|
||||
|
||||
**Updated 2026-10-13 (night): demo-hp, demo-felhom and Tester 1 run agent 0.155.0; demo-felhom runs controller 0.310.0, the other two 0.309.0 until the floor is raised. The open-items list is at 127. Reports: `REPORT-poweroff.md`, `REPORT-plugmeter.md`, `REPORT-usenet-2026-10-12.md`, `REPORT-newapps-2026-10-12.md`, `REPORT-disk-evening-2026-10-12.md`, `REPORT-answers-2026-10-12.md`, `REPORT-m3-2026-10-11.md`, `REPORT-redesign-2026-10-10.md`, `REPORT-testers-2026-10-10.md`, `REPORT-register-shrink-2026-10-10.md`, `REPORT-new-apps-2026-10-10.md`, `REPORT-release-2026-10-10.md`, `REPORT-break-the-circle-2026-10-09.md`, `REPORT-dooplex-survival-2026-10-09.md`, `REPORT-day4-2026-10-09.md`.**
|
||||
|
||||
## Moved from STATUS on 2026-10-28 (evening)
|
||||
|
||||
**Updated 2026-10-28 (day).** On your yes, tested security steps are live for 10 apps (Home Assistant, Immich, AdventureLog, Ghost, MeTube, Zipline, Karakeep, Tandoor, Papra, Dawarich); Wanderer is hidden. The nightly watch now reads the runtime inside every image in CI by itself. Tonight demo-hp takes the Home Assistant and AdventureLog steps; no box runs the other eight apps.
|
||||
|
||||
- Apps still waiting for the HTTP2Shell fix: 10 — Homepage, Termix, Wanderer and Seerr need a big step; ActualBudget, cal.com, Calibre-Web, Paperless, RomM and SparkyFitness have no fixed release yet.
|
||||
|
||||
**D58 and D59 ended 2026-10-28 (evening):** Termix's big step (D58) and Wanderer's big step (D59) are live; Wanderer is offered again (`09` §3 decision 279).
|
||||
|
||||
## Moved from STATUS on 2026-10-28 (day)
|
||||
|
||||
**Updated 2026-10-28 (night).** CC cannot delete from its secret store any more (D60). The watch now reads the Node and Brotli inside every image: 20 catalog apps carry HTTP2Shell; Home Assistant and Immich have tested steps waiting for your yes. The hub has 3 minutes to start.
|
||||
|
||||
Reference in new issue
Block a user