Files
felhom.eu/documentation/audits/review-round-2-2026-10-24
..

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/<name> → /opt/docker/ stacks/<name>/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.