# Review round 2 — the architecture documents against the code, and a fresh data-loss and security look (2026-10-24, unattended) **Baselines (read at start, 14:42 CEST):** felhom.eu `44d19042`, felhom-controller `321df1f` (v0.342.0 released; „next v0.343.0" unreleased on top), felhom-agent `5e05627` (v0.164.0; „next v0.165.0"), app-catalog `5e196e1`, hub deployed 0.169.0. Register: 190 rows. **Method.** Thirteen read-only reviewers (each prompt carried the brief's fences word for word — `reviewers/FENCES-given-to-every-reviewer.md`): seven on the documents (the brief's four groups, two of them split, plus a seventh for `02`/`10`/`11`/`14`, which the groups did not name), four on data loss (restore and put-back; delete and retention; keys; time and concurrency), two on security (hub + controller; agent + ep0 + installer + the DooPlex→ep0 jobs). Their answers are CANDIDATES (`reviewers/`). The main session read every item it acted on at `file:line`; the edits and their evidence are in `EDITS.md`. ## Part 1 — the documents (claims checked by the reviewers: 493; stale reported: 121) | Document | Verdict | |---|---| | `01` topology & trust | 7 stale facts corrected; 2 product statements contradict the code → R-1266 (D45) | | `03` host agent | 9 corrected (shipped releases, key files, listen address, swap, cloudflared's 4th state); a note: 41 local-API routes, 24 unlisted; break-glass text → R-1266 | | `04` control-plane authorization | 3 corrected; a note: a signed op's window has no length cap → R-1265 | | `05` hub | 9 corrected (the 6-hourly DB copy, version literals, the fifth bind-link path, 44 event types, the retired DR record) | | `06` off-site connectivity | 7 dated notes (the design-of-record kept); „no inbound listener" → R-1266 | | `07` backup | 12 corrected (portable secrets, R-104 fixed, keep-daily, D22/D20 released, a refusal row); §8.2c notes → R-1265; the kept-data load beside a copy → R-1256 | | `08` alarm ladder | 10 corrected (per-type cooldowns, R-723, per-app grain, R-998 skip, §8.1 built, §9 released, the operator-only box alarms); one ruling line kept with a dated note (operator Q1 made `backup_integrity_failed` a default) | | `09` updates | 19 dated notes and corrections (the §1 measurement, slice 6 built, the undo as shipped, digests, phase table); §6.4.x notes → R-1265; three rulings the code does not do as written → R-1266 | | `10` localisation | 7 corrected (Part B built, ceiling 0, the wizard deleted, counts) | | `11` OS updates | 7 corrected (cloudflared 2026.9.3, sudoers lines, the wrapper built, 32 bundle files, HOST_SLOW_RE); the System page's „Upload line" cell added | | `12` agent tooling | 3 corrected; the five rule copies aligned (the instruction-file edits below) | | `13` threat model | 11 facts corrected, every verdict cell left as it was and marked „re-check"; a note on the file manager's sign-in trust | | `14` power-off | 4 corrected (sudoers vectors, line-test holder, FAQ published, the table split); notes → R-1265 | | `15` media library | a dated note on the §10 measurement table; R-1019's fix; notes → R-1265 | | `16` storage identity | 4 corrected; notes → R-1265 | | `02` controller module map | 8 dated notes (markers, holders, banner alerts, cloudflare still wired, updater live, LockedFields, 22 packages with no row) | | `_hub-review.md` / `_recovery-inventory-2026-07-28.md` | a HISTORICAL banner / a „what has moved since" banner — both dated records kept as they were | | `00` capability map | 20 PROVEN-LIVE rows marked „re-check" with the commit that changed their mechanism (no status lowered); G15's „nightly" corrected | | `felhom-system-poster` facts + html | the timeline's hub-DB push (text only; no drawing needed) | **Rules, fences and promises were not edited.** Seven items that need the operator are in R-1266 (D45); about 30 behaviours with no document are written as `[DESIGN — undocumented, found 2026-10-24]` notes for the operator to confirm (R-1265, D44). **Instruction files edited (unprompted-work §5 — facts only):** the workspace root, felhom-agent and app-catalog copies of `.claude/rules/unprompted-work.md` got the missing „A version number is pushed once" paragraph (now byte-identical to the other two); `felhom.eu/.claude/rules/live-probes.md:41` — the stack path `/opt/felhom/stacks/` → `/opt/docker/ stacks//app.yaml`. Details: `EDITS.md`. ## Parts 2 and 3 — findings, ranked | # | Sev | Finding | What was proven | Row | |---|---|---|---|---| | 1 | **P1** | A power cut during a restore of a drive-backed app: the app runs on the cut restore's data, and a later controller restart pours the pre-restore copy back over newer writes | the real `Recover` code in a lean clone: boot 1 „copy gone → refused → marker kept", boot 2 put-back after the app ran (`proofs/H1-drive-cut-restore/`); the boot recreate's missing check read at `intermediary.go:470-476` | R-1251 | | 2 | P2 | One rejected backup block makes the night's second-drive copy delete the app's household-file copies | **reproduced on 9202**: control kept the copy twice; one bogus path → „removed stale dest dir userdata/media" (`proofs/H2-bad-backup-block/`) | R-1252 | | 3 | P2 | An export with „stop first" overwrites another app's restore marker | static, read at `file:line` | R-1253 | | 4 | P2 | The hub saves the escrow package in two writes; a lost answer tells the household „the previous code still works" | static | R-1254 | | 5 | P2 | D4's sshd drop-in reaches BYO hosts (against `11` §1); the in-force check and the all-or-nothing `sshd -t` | static | R-1257 (D43) | | 6 | P3 | Escrow carry / set-aside name edges | static | R-1255 | | 7 | P3 | Four restore edges (definition write, merge, a mirror race, the unused copy) | static | R-1256 | | 8 | P3 | ep0's iperf3 takes any tunnel peer's test | static (no ep0 change) | R-1258 | | 9 | P3 | The DooPlex→ep0 copies: the vouched agent can drop out; deletions mid-transfer; no future bound; **2026-10-25: the 02:30 push re-sends the 20:00 snapshot** | DST timing verified on DooPlex (`systemd-analyze`, Go) | R-1259 | | 10 | P3 | Spring 2027: the leg's deadline vs the gate's clock; two legs at one instant | Go's mapping verified | R-1260 | | 11 | P3 | The unreleased R-1250 line hold: two seeders, a removed app, alt-speed | static | R-1261 | | 12 | P3 | Any viewer explains any upload (the upload watch) | static; `08`'s limits list now names it | R-1262 | | 13 | P4 | Small items (a mislabelled operator mail, undeclared volumes on Remove, mount views, stale comments, a DNS-name password, rrsync) | static | R-1263 | | 14 | P4 | Skills tooling (no size cap on the grandfathered skill; a new skill not installed) | read | R-1264 | | 15 | P4 | Immich leaves an empty anonymous volume per recreate (296 on 9202); photos unaffected | read in the running image | R-1267 | **Fixed in this session (small, not recovery code, red-proved, commit only — controller `b01bb4b`, CI 2294 / #480 success):** a media restart („Újraindítás most", „Később", the safe removal) no longer starts an app the household stopped (R-721; two tests, each seen failing with the rule off); the file manager's header name file appears whole (a race the existing test caught 44 times in 300 runs on clean `main`; 0 in 300 after, 0 in 50 under `-race`). **NOT-A-FINDING:** SEC-1 X2 (a passphrase enroll after the operator's host delete gets ciphertext — the window D19 accepts); SEC-1 X3 (the refused-passphrase mail is operator-only with a one-hour cooldown); DOC-D1 U4 (the fit check that passes on a statfs error — the copy then fails and the app restarts untouched, `restore_unit.go:502-512`); HUNT-3 D31's comment wording (no data effect); Immich's `/data` volume for data placement (R-1267 holds the leak); the stack folder left after a clean removal (it is every catalog app's template folder). ## Machines - **9202** (Tier 0): as found at 12:46 UTC (`9202/as-found-2026-10-24.txt`); at the end (`9202/at-end-…`) the same containers and images, healthy (restarted by the test night chains), the catalog on the live repo, the controller 0.342.0. The only difference: Immich's running container uses a new empty anonymous volume (R-1267); the two others my test runs made were removed by name. audiobookshelf installed and removed through the product; my marker file removed; its second-drive copy removed by the product's remove. - **DooPlex:** reads, lean clones in the scratchpad, the gates; no service changed. **ep0, household boxes, the hub:** not touched (hub reads only, none needed in the end). ## Teardown (three layers) Machine: 9202 as above. Host (demo-hp): nothing provisioned. Hub: nothing changed.