A fresh one-disk install (512 GB VM, installer 1.33.0, agent 0.158.1 + golden 0.318.0 vouched): data volume 100 GiB, data
area 213 GiB registered as the default drive with no press, Jellyfin with no extra step, nothing of the area in a whole-box
copy; partitions formatted and used; the honest backup offer on a VM; /mnt/sys_drive never offered (no box had it).
16 §13 build B status; capability map D15; poster facts (drawing needs a refresh, STATUS); the backup page HU+EN now that
it is true; REPORT-storage-identity-buildB-2026-10-16.md.
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
The operator ruled D1-D6 as recommended. Build A shipped (agent v0.157.0, controller v0.317.0 MinAgent 0.157.0,
hub v0.154.0): live on Tester 1 (unknown/vm, the "cannot check" notice, copies made not counted), demo-hp and
the N100 (proven, different, unchanged). NAS notice and the no-drive sentence are unit-proven only.
STATUS: D10 removed; vouch agent 0.157.0 and rule on R-1082 added. Poster: the Tier-2 line, text only.
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
02-controller-module-map.md "The app page" gains the layout rules [DESIGN] — one column of one
width, a section is a hairline not a card, a box means something (blue = the app is waiting for
you, amber = something is risky, nothing else), the hero is not a box, and a success is not a
banner. Plus the asset-tag rule: every shell's stylesheet address is derived from the BUILD, not
the version, because the origin sends no validator and a cached copy can therefore only expire or
be addressed differently.
09-update-architecture.md gains §0 [DESIGN]: a version number is pushed once. It belongs at the TOP
of that document because everything below it compares versions — the floor moves a box by comparing
a number, the button offers the highest tag, and a box re-pulls a tag only when the tag changes.
Two images under one number is therefore not untidiness; it is a box running the wrong code that
can never be told.
R-1080 filed and closed: 0.315.0 was pushed three times from three different commits. Closed rather
than left open because no box is stranded — exactly one guest ran it and it ran the registry's
current digest, and the floor was 0.314.0 so nothing else could have taken it.
audits/app-page-2026-10-15b/P0/ carries the measurement and its honest caveat: the stale Cloudflare
object is no longer reproducible, because the first probe of the session forced its revalidation.
What is shown is every precondition, the proof an aged entry existed (cf-cache-status EXPIRED), and
the elimination of the only other candidate. The six stylesheet bodies are reduced to their sizes,
sha256s and rule counts — one 99 KB file does not need six copies in git to make the point.
.claude/rules/unprompted-work.md item 6, all four versioned copies: a version number is pushed once,
stated as a FACT about what the build now enforces rather than as a new rule.
STATUS: the operator's one look, through the public address.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
Spike (read-only probe, tools/physdisk_probe.py): the agent-side walk (partition -> disk, dm/LVM
slaves, loop -> backing file) names the right physical disk for one-disk boxes, two disks, two
partitions of one disk and a pass-through USB bridge; inside a VM (Tester 1) every disk is "unknown".
Today's agent lists whole disks only and calls an empty partition table "data-bearing"; the
controller's Tier-2 "different disk" is st_dev. sys_drive is a folder of mp0 (backup=1), so the
design gives one-disk boxes a separate internal data place outside the whole-guest copy instead.
Draft only; waits on the operator's rulings D1-D6 (16 section 12). 07 section 6.1's stale mp1 cell
corrected. Register 163 -> 169. Scratch loop disk and Tester 1 scratch disk removed.
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
On demo-hp: OMV 8.5.11-1 + openmediavault-kvm 8.3.4 in a VM, every OMV step done in its web pages
(headless browser). Public ISO 1.29.0 on the whole passed NVMe, BIOS firmware, bound and claimed as
throwaway customer Tester4, Vaultwarden + Jellyfin, local backups, two OMV restarts (clean guest
shutdown, AutoStart back in 1 min 52 s). Guest memory 7,863 MiB of 12 GiB.
Traps for the guide: the bridge changes OMV's IP; the plugin boots the disk first (wipe the NVMe);
the host-disk list offers OMV's own disk. Rows: one-disk Jellyfin refusal (R-1068), virtio disk not
formattable (R-1069), tunnel-less OS-update alarm (R-1070), NAS films for Jellyfin (R-1071, operator),
mixed formal/informal backup text (R-1072), no SMART in a VM (R-1073, operator). Register 157 -> 163.
Everything removed; Tester 1 back ONLINE. Customer Tester4 waits for the operator's delete.
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
02-controller-module-map.md gains "The app page" under web/: the URL map, why the default tab is
RENDERED at the bare URL and not redirected to, why the tabs are links and not panels, the
302-never-301 rule for the old /stacks/{name}/... GETs and the untouched POSTs, the after-start
registry (and why its actions are Go, never catalog data), and the guide's checked_version gate.
Each marked [DESIGN]. There was no document covering the app page at all, which is why the six-page
sprawl and the "Vissza goes to the list" behaviour were never called wrong — R-1066, filed and
closed by the same change.
10-localisation.md §7 gains the guide block, including the one exception it has to state out loud:
guide sections and FAQ entries are matched BY INDEX, the only lists in the copy model that are,
because a section's title is the thing being translated and so there is no key to match on. Safe
only because the copy gate requires equal counts AND the fallback stays field by field.
R-1067 (P4, BLOCKED): the catalog still carries pi_compatible: and nothing reads it. Left on
purpose — a box on 0.314.0 would show "csak x86" on every app — and gated on the fleet floor
reaching 0.315.0.
STATUS.md: one line asking the operator to look at the new app page on demo-hp, and the controller
version line.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
- 09 §3 decision 254 (operator decisions 240/243 written down); target-selection: a "lived-in" tier, Tier 0 = demo-hp
alone; nodes.md role + drive; 11-os-updates ring 0 wording; CLAUDE.md + docs.md: the diary.
- documentation/lived-in/n100.md (new diary); audits/n100-lived-in-2026-10-15/ (state, drive read, Part C capture sets,
installs, wiring, stranger probes, demo-hp clean-up, the operator's SHEET.md).
- Register: R-1062 (the split, operator decides), R-1063 (decommission half-state on an agent refusal), R-1064 (path
removal silent about cross-drive copies), R-1065 (a claimed Plex gets no libraries until a wiring run).
- demo-hp: /mnt/sizing-bench removed through the dashboard; the hub shows demo-hp OK (14:56 UTC).
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
- Configuration → Access tokens: named, scoped (group read/write), expiring (≤365 d), shown once,
stored SHA-256 only, every use logged by name. Bearer fhp_… on the web surface only; /api/v1 refuses it.
- Route table = the code's single source (web/routes.go), pinned to ServeHTTP's switch; session-only:
secrets shown/minted, deletes/resets, password, tokens.
- R-952 closed: /api/v1 route table names each caller, pinned to the switch; no-credential refusal test.
- Fail-open fixed: an empty operator password served the UI open (main skipped RequireAuth). Live hub never open.
- 05 §17, 04 §6.2, 13, 09 §3 251; register 155→157 (+R-1054, R-1055, R-1056, −R-952); STATUS.
- website: prowlarr-logo.svg (byte copy from felhom.eu-media) — the site gate refused every push.
Not deployed — waits for the operator's yes.
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
- 15-media-library.md §10: what the box does on each drive event, per app, with the measured call; the guards
that ARE needed and the ones that are NOT (K1: nothing lost while a drive is gone).
- Evidence K4 (dead-drive check: mountinfo `shutdown`, stat unreliable), K5 (restart), K6 (unclaimed Plex),
K7 (old Sonarr/Radarr repair on the bench), K8 (hub received the drive-return events and mailed none), K9 (files identical).
- nightly-rotation.md runs scripts/https_only_check.py first every night; scripts/CHANGELOG.md entry.
- STATUS, CONTEXT (decisions 244-246), REPORT-night-2026-10-14.md.
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Docs 02/05/07/13 and capability F12 (PROVEN-LIVE); the audit's C and D sections; R-1020 (the
duration reads 0) and R-1021 (the hub build builds a stale checkout) filed; STATUS one screen.
Register 133 -> 140 (this session: opened 2, closed 1; another session added 6). Both plugs ON.
Report: REPORT-immich-repair-2026-10-14.md.
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
The operator's half of the repair after an unclean boot (controller 0.311.0): counts only, never a
file name; warning mails, info is recorded. Registered in allowedEventTypes and operatorOnlyEvents in
the same commit, plus the per-app cooldown list; tests pin the pair, a household that enabled the type
gets nothing, and the counts line (red-proofed). Decision 236 (operator, 2026-10-14): the box asks
Immich through Immich's own job queue. R-1007 updated; the scripts' run on 9202 added as evidence.
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
03 §6 lists /guest/reboot and /host/poweroff; 13 counts 38 local-API routes and names the new
power of attacker (e). Hub CHANGELOG corrected: os_ring0_stalled is NOT held (a fleet rollout
alarm, on purpose). R-983: the GET reboot fixed (six remain). R-1000: the website FAQ line.
- 09 §3: the decision sheet S1-S38, the operator's words quoted where they differ (S1, S2, S8, S32, S37)
- closed: R-831 R-870 R-698 R-327 R-230 R-719 R-352 R-526 R-770 R-771 R-913 R-290 R-209a R-769 R-616 R-884, and R-243 (dated check: the mail fired 2026-10-11 16:13:51Z, hub log agrees)
- R-698: the known limit written in 07 §6.6; new row R-933 (mirror supported catalog images, P3)
- R-327: capability-map row BUILT; stands claim re-titled and moved to built; page re-rendered
- R-913: the customer Cloudflare token recipe in day0-install A.1; 01 §7 points at it
- R-884: read only - ArgoCD Synced, live Deployment equals git
- R-887 dated check NOT met (4 lost jobs 10-10/11); re-dated 2026-10-19 after the S21 change
- 22 open rows carry their ruling; STATUS: the sheet is ANSWERED
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Needs attention, Waiting for you (one card per kernel/Docker/Proxmox set the button may approve), a
7-column Boxes table whose rows open to every old value, and a closed Details (release ids, cancelled
approvals, ring-0 counts, floors, crash guard and root files, Approve now - which now asks first).
Same data, buttons, routes and CSRF field. No deploy. Screenshots in audits/hub-system-page-2026-10-10/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
PART A -- the poster. documentation/architecture/felhom-system-poster.html
(307 KB). Secret scan first: ZERO IPv4, zero PEM blocks, zero ssh keys, zero
Bearer. The one EAA... match is base64 inside an embedded "mime":"font/woff2"
blob, not a Facebook token. "token"/"secret"/"password" appear 11 times and
every one is a NAME ("6. ep0 read token", "the hub seal key"); the poster
itself says "Names only; no secret values". All five long base64 blobs are
declared assets: 1 image/png, 3 text/javascript, 1 font/woff2.
It renders with NO network: the source mentions cdn.jsdelivr.net and Google
Fonts, but the loaded requests are only the HTML plus blob:/data: URLs -- the
bundler inlined everything. Measured, not assumed, and it matters: this is a
disaster-recovery document, so needing the internet to draw would be a defect.
No console errors.
The operator's three Claude Design fixes are all present: (a) no "WG" badge,
WireGuard only for the tunnel, no badge on the ep0-copy tile; (b) the box ->
ep0 arrow reads "encrypted on the box, sent through WireGuard"; (c) "Known
gaps" holds two items and NOT the household-keys sentence, which is now a
neutral "By design" note under the ep0 household namespace.
ONE FACT ON IT WAS WRONG. The felhom.eu tile said "served from DooPlex through
Cloudflare". It is not: Cloudflare is DNS only and the traffic goes direct --
measured this morning for the privacy notice, which states exactly that. The
poster would have contradicted a published page. Fixed in place (a label):
"served from DooPlex, Cloudflare DNS only". The first wording overflowed the
fixed-size tile, so it was shortened to fit and the evidence lives in the
facts file instead -- checked by re-rendering, not by hoping.
PART B -- the facts and the rule. DESIGN-PROMPT-...md is renamed
felhom-system-poster.facts.md (one home per fact), with the three fixes folded
in as explicit instructions so a regeneration cannot undo them, plus a new
"Badges" section saying a "WG" chip must never come back.
New rule, section 6 "The system poster stays true", added IDENTICALLY to all
five copies of unprompted-work.md (the four repos and the workspace root on
DooPlex; verified identical by diff before and after) and to
PROMPT-TEMPLATE.md's end-of-session checklist as a FIFTH coupled artifact.
scripts/poster_facts_gate.py WARNS when the facts file has a newer commit than
the poster. It never fails a push, deliberately: a refresh needs Claude Design
and the operator, --no-verify is forbidden here, so a blocking gate would leave
deleting it as the only way out. It compares COMMIT times, not mtimes, because
a checkout rewrites mtimes and every fresh clone would shout.
RED-PROOF -- and it found a real bug in the gate. The first run warned
correctly but exited 1: a single non-ASCII character in its own warning raised
UnicodeEncodeError on this cp1250 console. A gate whose entire contract is
"never fails a push" was failing pushes. Fixed (ASCII output + an encode
guard), and the decoy now asserts BOTH the warning and exit 0. Three branches
proven: facts newer -> warns, rc 0; poster newer -> quiet, rc 0; poster
missing -> "could not tell", rc 2, not a false all-clear.
The decoy itself was seen to fail, twice, on Linux (the suite needs fcntl and
cannot run on Windows): breaking the warning gives STALE_WARNS=False, and
making it exit 1 gives RC_STALE=1. All 80 felhom.eu decoys behave.
PART C -- do box reports pass through Cloudflare? NO. Two channels. DNS from
PUBLIC resolvers (not DooPlex's own, which answers the LAN address):
hub.felhom.eu is a CNAME to dooplex.hopto.org -> 37.191.56.193, not a
Cloudflare address, and no cf-ray comes back. The manifest: an ordinary k3s
Ingress, Cloudflare named only in a DNS setup comment. THE CONTROL that makes
the negative mean something: iso.felhom.eu resolves to 172.67.x / 104.21.x,
real Cloudflare addresses -- so the method does detect proxying.
So nothing is added to the Cloudflare row: the hub path does not touch it.
06-offsite-connectivity.md section 1 claimed the public edge is a
Cloudflare-Tunnel and "DooPlex has no public IP" -- both untrue today. Kept
and marked STALE with the measurement rather than rewritten, because that
paragraph is the reason ep0 exists and the argument needs its premise visible.
total-loss-of-dooplex.md's "today a CNAME to dooplex.hopto.org" is confirmed
correct.
Register: 137 before, 137 after, 0 opened, 0 closed -- every finding here was
small and fixed in the session.