Files
felhom.eu/documentation/audits/evidence-drill-fresh-install-0242-2026-09-14/journal.md
T
admin 38848ffbeb
gates / gates (push) Successful in 21s
drill: a stranger's first hour on 0.242.0 — 1 intervention, not ready for a volunteer
Golden 0.242.0 baked, round-trip verified and vouched (cadence rule, R-468).
Fresh box from the public ISO on demo-hp: landed on the vouched set, two apps
deployed and used, backup, remove, byte-identical restore, power cut and code
typo all PASS. Stopped for a volunteer by R-493 (no instructions) and R-494
(the setup mail's dashboard link has no DNS; intervention I1). R-493..R-500
filed. Capability map: first-hour row added (PARTIAL), journey row scoped.
Stopgap Hungarian volunteer guide written. Hub teardown layer pending.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-09-14 16:17:47 +02:00

32 KiB
Raw Blame History

Journal — DRILL fresh install 0.242.0, 2026-09-14

Every observable in the order taken. Times are UTC unless marked. Hub log lines print CEST (UTC+2). Screens are in screens/, named by step.

Baselines (re-verified 12:5x UTC)

repo main version
felhom-controller 406755fa8fba v0.242.0
felhom-agent 4586f0f7f6d1 v0.130.0
felhom.eu 41590f8ee618 hub v0.112.0

Hub, before the bake: agent 0.130.0 vouched · golden 0.236.0 vouched · min_agent 0.129.0 · floor min_controller_version 0.242.0. Highest register id R-492.

Phase 0.1 — golden 0.242.0

Baked 13:01:04 → finished before 13:06:42 · round-trip verified · vouched 13:08:41. Full record: documentation/tests/golden-0.242.0-2026-09-14/README.md.

Phase 0.2 — what a volunteer receives

Searched, with what was searched:

source what was looked for result
website/*.html (9 pages) iso.felhom, iso, letolt, telepit no mention of the installer, of iso.felhom.eu, or of any install step (only technologiak.html "comparison" and two FAQ answers on power loss / backups matched the loose pattern)
https://iso.felhom.eu/ the root, index.html HTTP 404 both. Only a named object answers: felhom-installer-1.26.1-pve9.2-1.iso (200, 1 705 322 496 B, last-modified 2026-07-31)
RUNBOOK-onboarding-draft-v4.md customer steps operator document, operator present at the visit ("operator narrates"); C7 marked never executed
email.md in the workspace find /mnt/5_hdd/felhom.eu -maxdepth 5 -iname '*email*.md' absent (only audits/FINDING-app-email-rollout-2026-06-29.md, unrelated)
the R-11 tester one-pager find … -iname '*one-pager*', register never written — ROADMAP-HISTORY: "RULED 2026-07-21 (channel); doc is the architect's"
hub e-mails to a new customer hub/internal/notify/templates.go two: „Kösd össze a Felhom dobozodat" (self-bind link, at customer creation) and „Elindult a Felhom szervered — beállító kód" (claim code, after day-0). Neither says how to get or install the software

Harness limits, declared before the walk (not interventions — the volunteer is not limited this way)

  • H1 — no mailbox. Both customer mails go only to the registered address. The hub stores hashes; the Resend key on DooPlex is send-only (401 restricted_api_key … only send emails, probed 13:00). A volunteer reads their inbox; this harness cannot.
  • H2 — keyboard. qm sendkey emits US scancodes; the installer defaults to Hungarian. The layout was switched to U.S. English (as walk 5). A volunteer keeps Hungarian.
  • H3 — the USB stick. Auto-reboot was unticked so the VM does not re-enter the installer from the still-attached ISO; the ISO is detached by qm set after the install. A volunteer pulls the stick.
  • H4 — the Terminal UI entry was chosen over the graphical default for key-drivability. Both entries were release-gated.

Scope choice

  • Customer created with the DR tier OFF (the form defaults it ON). The DR tier provisions an ep0 namespace and token; the brief fences ep0. Offsite is OFF by default and stayed OFF.

Step 1 — download

13:01:27 on demo-hp: curl https://iso.felhom.eu/felhom-installer-1.26.1-pve9.2-1.iso → rc 0, 32 s, 1 705 322 496 B, sha256 f3cc86d5f0ec68bba4155c994b4fa84e208d50209bb6e815636c99e5441059a6 = the published .sha256 exactly. (No release report records a sha for 1.26.1; walk 5 recorded "same sha256" without the value.) A volunteer must already know the exact file name — the site lists nothing.

Hub side — the operator's steps

13:03:03 POST /configs/new customer drill0242, name "Drill 0.242 first hour", domain drill0242.felhom.eu, e-mail drill0242@felhom.eu, DR tier off → 303 13:03:04. Hub log (CEST):

15:03:04 [INFO] Customer config created: drill0242
15:03:04 [INFO] self-bind link (hash 4270bdd2…, valid 7 days) emailed to the registered address of drill0242
15:03:04 [INFO] self-bind link auto-minted for drill0242 on customer creation (the console banner's promised email now exists)

The 5-word Tulajdonosi jelmondat was read from the customer page into a 0600 file on DooPlex (shape: 5 words, 42 chars). The operator must hand it to the volunteer out of band — nothing sends it. (The self-bind mail says „amelyet a beállításkor kaptál" — "which you received at setup".)

Step 2 — install

VM 330 drill0242-fresh-install on demo-hp: q35/OVMF (pre-enrolled-keys=0), 4 cores, 8 GB, cpu=host, scsi0 200 G · scsi1 50 G · scsi2 50 G qcow2 on nvme-scratch (dir at /mnt/hdd_1, the mount root), MAC bc:24:11:0c:23:cc.

UTC screen what it said / what was done
13:03:43 power on —
13:03:49 GRUB (s01, s02) Felhom logo, „Saját felhőd, saját szabályaid"; entries „Felhom telepítés" / „Felhom telepítés (szöveges mód)" — the only Hungarian the installer shows
13:05:09 s04 English Proxmox EULA, „I agree"
13:05:27 s05 „Target harddisk: /dev/sda (QEMU HARDDISK) (200.00 GiB)" — default is the right disk here; three disks present, no guidance which
13:05:43 s06 Country Hungary · Timezone Europe/Budapest · Keyboard Hungarian (H2: changed to U.S. English)
13:09:18 s13 Root password [at least 8 characters] · Confirm · Administrator email prefilled mail@example.invalid — the volunteer must decide what to type; set drill0242@felhom.eu
13:12:01 s16 nic0 bc:24:11:0c:23:cc · Hostname pve.example.invalid · IP 192.168.0.134/24 (the DHCP lease, frozen as static) · GW 192.168.0.1 · DNS 192.168.0.250
13:12:21 s17 Next on the defaults → „Invalid values: hostname does not look valid". The default is refused; the volunteer must invent a hostname. Set drill0242.felhom.eu
13:14:40 s22 Summary: ext4 · /dev/sda · Europe/Budapest · U.S. English · drill0242@felhom.eu · nic0 · drill0242.felhom.eu · 192.168.0.134/24 · 192.168.0.1 · 192.168.0.250 · „[X] Automatically reboot" (H3: unticked, s25)
13:15:54 Install pressed —
≤13:19:18 s28 „Success — Installation finished - reboot now?" (≤ 3 m 24 s of copying; disk 4.0 G)
13:19:44 first boot H3: qm stop, --delete ide2, --boot order=scsi0 in its own qm set (verified boot: order=scsi0, ide2 lines 0), qm start

Step 3 — the first screen and the claim

13:21:10 — the hub lists an Unclaimed appliance (86 s after first power-on): appliance 25, pairing code ***-*** (redacted), MAC bc:24:11:0c:23:cc, "Standard PC (Q35 + ICH9, 2009)", 7.7 GB, three SSH host keys.

The console (s29, code redacted), verbatim:

Welcome to the Proxmox Virtual Environment. Please use your web browser to
configure this server - connect to:
  https://192.168.0.134:8006/
drill0242 login:
Felhom — a doboz készen áll, és a párosításra vár.
Párosító kód:  ***-***
Nyisd meg az e-mailben kapott linket, és add meg
ezt a kódot és a jelszavadat.
Ez a képernyő magától frissül — nincs teendő a
doboznál, és nyugodtan itt hagyhatod bekapcsolva.

Two things a stranger meets here: an English line telling them to open the Proxmox admin page, above the Felhom text; and „a jelszavadat" ("your password") for the secret the self-bind mail calls „Tulajdonosi jelmondat" (R-323 renamed the mail, not the console).

The bind (H1 consequence). The volunteer's route is the self-bind link, which is in a mailbox this harness cannot read. The operator's documented fallback was used: POST /appliances/25/bind (customer_id=drill0242) at 13:21:43 → 303 flash=appliance_bound. The self-bind page itself was therefore NOT exercised.

Hub log (CEST = UTC+2):

15:21:44 appliance 25 BOUND to customer drill0242 (mode=appliance) — delivery staged for its next poll
15:21:45 appliance credentials DELIVERED once to appliance 25 (… passphrase withheld)
15:21:52 [claim] claim code (gen 1) emailed to the registered address of drill0242
15:22:21 host enrolled: drill0242-3f4b42 (customer drill0242)
15:22:21 vaulted break-glass recovery credential for host drill0242-3f4b42 (user=root@pam, secret 32 chars)
15:22:22 Artifact manifest served for customer drill0242 (agent=0.130.0 golden=0.242.0)
15:22:38 wg registered: host=drill0242-3f4b42 … ip=10.77.0.5/32 changed=true gen=1 sync=ok
15:22:39 DR-recipe host-half stored for customer drill0242 (host drill0242-3f4b42, v1)
15:23:58 Event from drill0242: controller_started (info) — Controller elindult (0.242.0)
15:23:58 managed floor SERVED for drill0242: floor 0.242.0, agent requirement "0.129.0" from manifest (golden 0.242.0)

Note on ep0: wg registered … sync=ok is a WireGuard peer written on ep0 by enrolment itself, with the DR tier OFF. The brief fenced ep0; the product touches it on every enrolment. Teardown owes its removal.

Step 4 — "ready", and the version

vouched landed
agent 0.130.0 0.130.0 (felhom-agent --version)
controller golden 0.242.0 0.242.0 (docker ps: felhom-controller:0.242.0 … (healthy))

No self-update happened because none was needed — golden == floor. Power-on → controller running 4 m 14 s; bind → controller 2 m 15 s. Infra: traefik:v3.6.7, gtstef/filebrowser:1.3.3-stable. First local whole-guest backup ran unaided 15:28:53–15:29:23 CEST, OK (seen as a backup lock).

The console still shows the pairing banner and the stale code after bind (s30, 13:24:38) — R-214, still open.

Intervention I1 — the dashboard has no address (R-494, filed 13:2x BEFORE acting)

The claim mail says https://felhom.drill0242.felhom.eu. Measured:

felhom.drill0242.felhom.eu @1.1.1.1      A: (none)  AAAA: (none)
control felhom.enkisfelhom.hu @1.1.1.1   A: 104.21.3.175 172.67.130.252
https://felhom.drill0242.felhom.eu       curl rc 6 (cannot resolve)
@192.168.0.1 (router)                    (none)
@192.168.0.250 (installer-offered DNS)   (none)
@192.168.0.134 (the box) 13:27:39Z       (none)      — google.com resolves: the resolver is alive
agent 15:27:44 CEST  lanresolver: applied split-horizon record vmid=9201 domain=drill0242.felhom.eu ip=192.168.0.158
@192.168.0.134 (the box) 13:29:18Z       192.168.0.158

The hub has no tunnel/DNS creation code; cf_tunnel_token is an optional pasted field. Act: the dashboard is reached at the guest LAN address with the name forced (--resolve felhom.drill0242.felhom.eu:443:192.168.0.158), from demo-hp — which is where a browser on the household LAN would be. GET / → 302 /claim; the page, verbatim:

A szerver beállítása — Drill 0.242 first hour — „Add meg az e-mailben kapott beállító kódot, majd válassz saját jelszót a vezérlőpult védelméhez." · Beállító kód (szó-szó-szó) · Új jelszó (min. 12 karakter) · Új jelszó megerősítése · Beállítás és belépés · „Nem kaptad meg a kódot? Új kód kérése"

H1 — the setup code (harness substitution, not a volunteer act)

The code is in the mail. Observe access to the appliance: POST /hosts/drill0242-3f4b42/reveal-recovery-credential → 200 (the designed operator path; it emits an audit event), stored 0600, never printed. Then pct exec 9201 -- docker exec felhom-controller … --print-reset-code.

Harness slip, recorded: the first mint at 13:29:28 (generation 2) was captured with a pattern [a-z-]* that does not match accented letters, and the raw output was shredded before the capture was checked — that code was lost unseen. Re-minted (generation 3) with a byte-agnostic capture. Both mints supersede the mailed generation-1 code.

Step 3 (cont.) — the claim, through the front door

All dashboard requests from here are made from demo-hp (the household LAN) to the guest's traefik on 192.168.0.158:443 with the dashboard name forced — intervention I1, once, for the whole walk. Secrets travel on stdin to curl, never on a command line; the dashboard password is a generated 24-character value in a 0600 file on DooPlex.

UTC request result
13:31:19 GET /claim 200; felhom_claim_csrf cookie (82 B) + form token (64 hex)
13:31:20 POST /claim _csrf, code, new_password, confirm_password 302 → /, Set-Cookie: felhom_session
13:31:2x GET /launcher 200 — „Indítópult — drill0242.felhom.eu", one tile Filebrowser, „Indítópult megosztása … A megosztás jelenleg ki van kapcsolva." Menu: Vezérlőpult · Alkalmazások · Tárhely (Meghajtók, Hálózati tárhely) · Biztonsági mentés (Áttekintés, Távoli mentés, Alkalmazások, Visszaállítás) · Megosztás · Rendszermonitor · Debug · Beállítások · version 0.242.0

Power-on → claimed dashboard: 27 m 37 s (13:03:43 → 13:31:20), of which the harness's own keyboard driving is most of the install phase.

Step 5 — the app list

GET /stacks → 200, „Alkalmazások — drill0242.felhom.eu", filters Mind / Futó / Leállítva / Telepíthető, 52 „Telepítés" links. GET /stacks/bookstack/deploy and /stacks/privatebin/deploy → 200. Both pages, verbatim in the parts a stranger reads:

BookStack — Telepítés · „Egyszerű, könyv-szerű wiki és dokumentáció platform" · ~150M · Pi kompatibilis · Memória 1058 MB / 3712 MB (28%) · „Hol lesznek az adatok: ennél az alkalmazásnál nincs külön adatmeghajtó-választás, ezért az adatai a rendszermeghajtóra kerülnek (/mnt/sys_drive). A mentései így is elkészülnek." · „Automatikusan generált értékek … Jegyezze fel a szükséges jelszavakat!" (Alkalmazás kulcs, Adatbázis jelszó — Megjelenítés) · Aldomain wiki · „Az aldomain telepítés után nem módosítható…"

PrivateBin — Telepítés · „Titkosított jegyzet és szöveg megosztás" · ~30M · Memória 1089 MB / 3712 MB (29%) · same data sentence · Aldomain paste

Observation, not yet a row: the drive page's „Meglévő meghajtó csatolása" candidate list (GET /api/disks/candidates) offers the guest's own system volume /dev/mapper/pve-vm--9201--disk--1 (mount_source /mnt/sys_drive, already_mounted: true, data_bearing: true) beside the two blank 50 G disks.

Step 5 (cont.) — deploy

Both through the deploy page's own call: every named input of #deploy-form → POST /api/stacks/<app>/deploy {"values":{…}}, then GET /api/stacks/<app> every 5 s (the page polls every 3 s). Generated secrets travelled on stdin and were shredded.

app fields sent POST state transitions running after
bookstack APP_KEY, DB_PASSWORD, SUBDOMAIN=wiki 13:34:38 → 202 „Telepítés elindítva – az állapot a kártyán követhető" +6 s deploying · +26 s degraded · +42 s starting · +68 s running 68 s
privatebin SUBDOMAIN=paste 13:35:47 → 202 +5 s starting · +21 s running 21 s

Observation: the deploy page's poll handles deploying / running / starting / unhealthy / exited / stopped and has no degraded branch (deploy.html ~1040–1095), so for those 16 s the page keeps showing its previous step. Harmless here; recorded.

App pages (/apps/bookstack, /apps/privatebin): „Fut" · „Naprakész" · „Megnyitás ↗". BookStack's „Első lépések": „Nyisd meg a wiki.DOMAIN címet a böngészőben · Jelentkezz be: admin@admin.com / password · Változtasd meg azonnal az admin jelszót és email címet …" and a box „Alapértelmezett belépés admin@admin.com / password — Az első bejelentkezés után azonnal változtasd meg!". PrivateBin: „Nyisd meg a paste.DOMAIN címet …" → R-498 (52 of 53 templates).

Step 6 — using both, through their front doors

Front doors from demo-hp: wiki.drill0242.felhom.eu → 302 /login; paste.drill0242.felhom.eu → 200, 22 896 B, PrivateBin UI (English).

BookStack (over HTTPS, so its secure cookies work — R-460's 419 was a plain-HTTP effect):

UTC act result
13:37:18 log in admin@admin.com / password (the page's own instruction) 302 → /; logged-in markers 3
13:37:19 change password via POST /users/1 405 — harness wrong route (BookStack ≥23 is /settings/users/1)
13:37:52 change admin e-mail + password via POST /settings/users/1 _method=PUT 302 → /settings/users; old default login now → /login (refused); new → / (accepted)
13:38:44 create book „Családi receptek DRILL0242" 302 → /books/csaladi-receptek-drill0242
13:38:4x save a draft with _method=PUT 405 — harness (the route is POST)
13:39:35 new draft 3 → POST name „Töltött káposzta", body DRILL0242-OLDAL Töltött káposzta: árvíztűrő tükörfúrógép 2026-09-14 302 → /books/csaladi-receptek-drill0242/page/toltott-kaposzta
13:39:3x upload attachment nagymama-recept.bin, 262 144 B random, sha256 2e252913844f8931… 200, attachment id 1
13:39:3x read back page 200, sentence present 2 (negative control 0); attachment listed 1; GET /attachments/1 200 262 144 B, sha256 equal: YES

PrivateBin — the browser's own v2 format (AES-256-GCM, PBKDF2-SHA256 100 000, zlib), key only in the fragment, as a browser does:

UTC act result
13:38:08 POST / a paste „DRILL-0242 privatebin sentinel … — árvíztűrő tükörfúrógép", expire 1 week 200 {"status":0,"id":"3963448c37a87005",…}
13:38:08 GET /?pasteid=3963448c37a87005, decrypt 200, 471 B, decrypted == sent: True (sha256 5920d11290cca367…); wrong key → InvalidTag

Step 7 — the backups pages, read as a first-timer (13:38:55)

/backups verbatim, in order: „Csak egy másolat készül (nincs második meghajtó) — a 3-2-1 mentéshez csatlakoztasson egy második meghajtót vagy offsite tárolót." · „A rendszermentés jelenleg ugyanazon a lemezen van, mint a rendszer — így hibás fájlok ellen véd, lemezhiba ellen nem. …" · Rendszer (/) 1.96 GB / 68.7 GB (3%) · DB mentések – · Utolsó teljes mentés 2026-09-14 15:28 (10 perce) 624.3 MB Helyi tároló (local) Naprakész · „Következő mentés — 0 órája — a mentési ablakon belül" · Visszaállítás ellenőrizve „Még nem futott" · Mentés most · window 02:30 / 03:30 / 04:15 / 04:30–08:30 · Távoli rendszermentés „nincs beállítva".

  • Honest: both warnings; the whole-box backup's date and target.
  • Confusing: „Következő mentés" (next backup) with „0 órája" ("0 hours ago") under it — the value is the age of the LAST backup (backups.html:101-102, .AgeHours).

/backups/apps: „Utolsó adatbázis mentés: Még nem futott"; BookStack „Konfig + DB + Adatok" and PrivateBin „Konfig + Adatok", each „1. mentés Auto helyi Utolsó: most" · „Nincs 2. (off-drive) másolat" · „3. mentés Nincs beállítva".

„Utolsó: most" was checked, not assumed: timeAgoStr renders „most" only for a parseable time under a minute old (an empty value renders nothing, funcmap.go:283-296), and GET /api/backup/snapshots?stack=… returned for both apps {"time":"2026-09-14T13:38:53Z","short_id":"helyi","tier":1,"drive_label":"Belső SSD (rendszer)"} — 3 s before the page was read. True. (That unit predates the BookStack content of 13:39:35.)

/stacks/bookstack/backup → R-499: „… már szerepelnek a teljes rendszermentésben (PBS) … ehhez az alkalmazáshoz nincs külön teendő." — this box has no PBS.

/backups/restore: app select BookStack / PrivateBin, „Pillanatkép: — Válasszon alkalmazást —", „Még nincs mentés felhasználói adattal."

Step 8 — „Mentés most" (the app-backups page's button, POST /api/backup/run)

UTC observable
13:41:21 pressed → {"ok":true,"message":"Mentés elindítva"}
+5 s {"enabled":true,"running":true}
+21 s {"db_dump":{"count":1,"duration":"15.752362463s","last_run":"2026-09-14T13:41:37.325510764Z","success":true},"enabled":true,"running":false}
13:41:42 both apps' restore points: {"time":"2026-09-14T13:41:37Z","short_id":"helyi","tier":1,"drive_label":"Belső SSD (rendszer)"} — one entry each; the 13:38:53 unit is gone (one local point kept)
13:41:4x /backups/apps: „Utolsó adatbázis mentés: 2026-09-14 15:41 (most)"; Adatbázisok: bookstack · MariaDB · 59.0 KB · 15:41 · 41 tábla · OK

The date moved, to the true time, on both pages (R-476 class: holds). 21 s end to end.

Step 9 — version labels and Update

Both app pages: „Fut · Naprakész". GET /api/stacks/<app> at 13:42:28:

bookstack   template_images {bookstack: lscr.io/linuxserver/bookstack:26.05.2, bookstack-db: mariadb:12.3}
            catalog_images  {bookstack: lscr.io/linuxserver/bookstack:26.05.2, bookstack-db: mariadb:12.3}
privatebin  template_images {privatebin: privatebin/pdo:2.0.5}   catalog_images {privatebin: privatebin/pdo:2.0.5}

The catalog offers no newer version of either app, so there was nothing to press. Skipped, per the brief. „Naprakész" is true on this evidence (installed == catalog).

Step 10 — removal: what the dialog shows for PrivateBin

The app page's delete opens removeStack() (layout.html:396), which loads /api/stacks/privatebin/hdd-data → {"hdd_paths":null,"has_hdd_data":false} and /backup-data → [{"path":"/mnt/sys_drive/felhom-data/backups/primary/privatebin","size_human":"40K","exists":true}]. Rendered text, in order:

Alkalmazás eltávolítása: privatebin · „Az alkalmazás visszaáll "Nincs telepítve" állapotba. A sablon megmarad, újratelepíthető." · „Ez a művelet nem visszavonható!" · Mindig törlődik: Docker kötetek (adatbázis, alkalmazás konfiguráció) · Telepítési konfiguráció (app.yaml) · Másodlagos mentés ütemezése · Mentési adatok: /mnt/sys_drive/felhom-data/backups/primary/privatebin (40K) · ☐ Mentési adatok törlése · „Az éjszakai restic pillanatképek nem törölhetők egyenként — a megőrzési szabályok szerint automatikusan elavulnak." · Mégsem · Eltávolítás

No „delete my data" box appears — PrivateBin has no drive data; its data is in a Docker volume, which the dialog says is always deleted. „Delete my data too" was therefore performed as every delete box ticked: remove_backups: true (the only box), remove_hdd_data: false (no box shown). Observation: the sentence about „éjszakai restic pillanatképek" appears on a box with no restic configured at all.

Step 11 prep — the household loses a recipe (13:43:07)

Through BookStack's own delete: GET …/page/toltott-kaposzta/delete → token; POST …/page/toltott-kaposzta _method=DELETE → 302 to the book. Then: page 404, /attachments/1 404, the book page lists the title 0 times. The last backup containing both is the 13:41:37 unit (step 8).

Step 11 — restore BookStack from its backup, through the restore page

The page's own submit: POST /backup/restore _csrf (from the page meta), stack_name=bookstack, snapshot_id=helyi (the only point, 13:41:37 — step 8).

UTC observable
13:44:13 302 → /backups/restore?flash=Visszaállítás elindult — az állapot itt frissül.
+6 s app stopped
+11 s starting
+26 s running (probe pending)
+32 s (13:44:45) running, health_probe.healthy: true
13:45:09 read back through the front door — login with the post-change password → /; page …/page/toltott-kaposzta 200; sentence present 2 (negative control 0); árvíztűrő tükörfúrógép present 2; /attachments/1 200, 262 144 B, sha256 equal to the pre-backup upload: YES

DATA: PASS. The recipe the household deleted at 13:43:07 is back, with its attachment byte-identical and its Hungarian text intact, 32 s after pressing restore.

Reading the page at 13:45:11, after completion: the one-shot flash is gone and the page shows the empty form again („Pillanatkép: — Válasszon alkalmazást — · Még nincs mentés felhasználói adattal."). CORRECTED 13:46 — the first reading here was wrong. The page's script polls /api/backup/restore-status, which at 13:46:03 returned "last":{"op":"restore","stack":"bookstack","ok":true, "message":"A(z) bookstack: 2 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult.", "finished_at":"2026-09-14T13:44:39Z"},"last_recent":true. So a Hungarian finish message exists and names what came back; whether the page shows it is script-rendered and was not observed (no browser here). The server-rendered HTML alone shows only the empty form.

Step 10 — removal, done the way the screens lead

Harness shortcut, withdrawn as a finding. At 13:43:36 the harness called POST /api/stacks/privatebin/remove on a running app and got 409 stack "privatebin" is still running — stop it first before removing (English). The screens never offer that: for an operational app stacks.html:99-102 shows Frissítés · Újraindítás · Leállítás and no „Eltávolítás"; the app page shows none either. Nothing changed on the box (before/after identical, step10-remove-observe.txt).

UTC act result
13:45:26 „Leállítás" → POST /api/stacks/privatebin/stop {"ok":true,"message":"Stack privatebin stop completed"} (English message; the UI shows its own text) · stopped at +6 s
13:45:32 observe before volume privatebin_privatebin_data ×1 · containers 0 · /opt/docker/stacks/privatebin/.felhom.yml · backup dir 40K
13:45:33 „Eltávolítás" with every box ticked → POST …/remove {"remove_hdd_data":false,"remove_backups":true} 200 {"removed":"privatebin","volumes_removed":["privatebin_privatebin_data"],"hdd_paths_removed":[],"hdd_paths_preserved":[],"backup_paths_removed":["/mnt/sys_drive/felhom-data/backups/primary/privatebin (40K)"]}
13:45:39 observe after volumes 0 · containers 0 · backup dir No such file or directory · only the catalog template .felhom.yml remains (the dialog: „A sablon megmarad")
13:45:4x front door / API paste. → 404 · restore points [] · state not_deployed, deployed=false

PASS — the drive is clean of the app's data and its backups, and the response names each thing it removed. (volumes_removed is populated here; R-489 recorded null over removed volumes on demo-hp — this box did not reproduce it.)

Step 12 — status pages, read as a customer would a week later (13:46)

  • /launcher: tiles BookStack, Filebrowser (PrivateBin gone — correct).
  • /dashboard: „3 Futó alkalmazás · 0 Leállítva · 55 Összes alkalmazás" · Memória 1.0 GB / 4.0 GB · Rendszer (/) 2.11 GB / 68.7 GB · Lemezek állapota: QEMU QEMU HARDDISK „Nincs adat" „0 °C" · „Utolsó mentés: 2026-09-14 13:41" while /backups/apps says 15:41 for the same run → R-500 (dashboard.html:154 formats without the box zone).
  • /monitoring: „Hub kapcsolat — Kapcsolódva … Utolsó sikeres jelentés: most"; graphs „Még nincsenek adatok…". The banner „A gazdagép metrikái jelenleg nem elérhetők" appeared in my text extraction, but it is display:none by default and shown only by script (monitoring.html:15) — not evidence; withdrawn. Whether the host card fills in was not observable without a browser.
  • Observation, not filed: „0 °C" beside „Nincs adat" on a virtual disk.

Evidence off the machine, end of Phase 1 (13:46)

box-logs-phase1/: controller.log (526 lines), agent-journal.log (2461), bootstrap-journal.log (173, pairing code redacted), box-state.txt. Secret sweep over the copies for the claim code, passphrase, dashboard password, BookStack password, break-glass credential and installer root password: 0 each; control (passphrase planted in a throwaway copy) 1.

Accident A1 — power cut (A1-power-cut.txt)

UTC observable
13:47:31 before: felhom-controller:0.242.0 · bookstack:26.05.2 · mariadb:12.3 · filebrowser:1.3.3-stable · traefik:v3.6.7, all healthy · agent 0.130.0
13:47:33 qm stop 330 (hard)
13:48:36 qm start 330 (60 s dark)
+37 s appliance answers SSH
+2 m 07 s (13:50:43) hub: Event from drill0242: controller_started (info) — Controller elindult (0.242.0)
+2 m 9 s dashboard /api/health 200
13:58:11 the dashboard session did not survive: /api/stacks/bookstack → 401 authentication required (the customer logs in again; the harness loop misread this as unparseable for 8 min — harness fault, stopped)
13:58:33 after re-login: bookstack running, healthy, template images unchanged; every container Up 7 minutes (healthy), same five image tags; agent 0.130.0
13:58:36 BookStack front door: page 200, sentence 2 (control 0), Hungarian 2, attachment 262 144 B, sha256 equal: YES

Hub log across the cut (non-routine lines only): DR-recipe host-half stored 15:49:04 CEST, controller_started 15:50:43, DR-recipe app-half stored 15:50:44. No alarm, no warning, no customer mail event.

A1: PASS — every app back on the same version (Slice 3 holds), data intact, no false alarm, ≈2 min to a working dashboard. Cost to the household: one re-login.

Accident A2 — a typo in the code

Where it can be tried. The setup code is single-use and this box is already claimed, so the first-claim screen cannot be re-entered. The login page's „Elfelejtett jelszó" opens the same /claim form and the same code gate, titled „Jelszó visszaállítása — Felhom · Add meg az e-mailben kapott beállító kódot, majd válassz új jelszót." A2 was run there, on the same box. The pairing-code typo on the hub's self-bind page was NOT exercised (H1: no link).

Code: generation 4, minted by H1 at 13:59:30. The typo is the real code with its last letter changed — exactly one character different, same length (checked, never printed). Limiter, from source (claim.go:32-33, :170-200): 5 failures → 15-minute lock, counted per source AND globally.

UTC attempt result
14:00:09 typo 200, form re-rendered (not accepted)
14:00:10 the right code, new 24-char password 302 → / — accepted after the typo
14:00:10 log in with the new password / the old one 302 / 200 (old refused)
14:00:10–12 four more wrong codes (the spent code) 200 each
14:00:12 the 5th failure box: [WARN] [web] claim: code lockout tripped (source 192.168.0.104) — 15 min · Event pushed: claim_lockout (warning) — Túl sok hibás beállító kód — a beállító oldal 15 percre zárolva
14:00:12 a 6th attempt, during the lock 200
14:00:16 hub Event from drill0242: claim_lockout (warning) … · Operator email sent for drill0242/claim_lockout

Harness note: the text filter used on these replies missed the refusal wording, so the on-screen messages are taken from the reply bodies below, and the plain „Hibás vagy lejárt kód" reading is repeated after the lock expires. | 14:15:26 | a wrong code after the 15-minute window | 200, error box „Hibás vagy lejárt kód" — the form accepts attempts again |

On-screen wording, read from the reply bodies: during the lock „Túl sok próbálkozás — próbáld újra 15 perc múlva."; after it „Hibás vagy lejárt kód". Both Hungarian, both true.

A2: PASS — a one-letter typo is refused without harm, the right code is accepted right after it, the lock is honest about its length and lifts on time. Two properties recorded, not filed: the lock is global as well as per source (claim.go:188-200) — five wrong guesses from anywhere lock the real household out for 15 minutes, by design against guessing; and the lockout e-mail went to the operator only (Operator email sent for drill0242/claim_lockout), while the screen tells the customer.