From 0073691231f32eb3f92ee60c5d8eb73aa37c49fe Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Tue, 21 Jul 2026 16:37:25 +0200 Subject: [PATCH] docs(report): TASK-F - lifecycle, HP USB byte-proof, R-55 live leg, Part 4 skip --- REPORT.md | 217 +++++++++++++++++++++++------------------------------- 1 file changed, 94 insertions(+), 123 deletions(-) diff --git a/REPORT.md b/REPORT.md index 16b64b5..f8cb364 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,147 +1,118 @@ -# REPORT — TASK-E: pre-invite sweep + HP mini ISO (2026-07-21) +# REPORT — TASK-F: app lifecycle + HP USB write + R-55 live leg (2026-07-21) -Baselines at start (all verified, all clean): controller `83f20c8` v0.156.0, agent `935904f` v0.92.1, -felhom.eu `d4ac52d` hub v0.68.1, app-catalog `34d50a3`. +Baselines at start (verified, all clean): controller `a96226a` v0.157.0, app-catalog `857ba53`, +felhom.eu `0b2c598`, agent `eba040d` v0.93.0 (source only — untouched this task). -## Per-part status +| Part | Status | +|---|---| +| 1 — app lifecycle states | **DONE + LIVE-PROVEN** — controller v0.158.0, plus **v0.158.1 fixing a defect I shipped** | +| 2 — HP USB write | **DONE — byte-proven** | +| 3 — R-55 live leg | **DONE — PROVEN LIVE** | +| 4 — resolvability table | **SKIPPED by operator ruling** (no Docker Hub login) | -| Part | Status | Ships as | -|---|---|---| -| 1 — escrow wordlist | **DONE** | agent **v0.93.0** (not built/deployed; rides the next train) | -| 2 — R-55 stop-honesty | **DONE — scope corrected by operator ruling** | controller **v0.157.0**, deployed to 9201 | -| 3 — catalog honesty | **DONE, with one ruling** | catalog: wanderer re-pinned, plant-it retired, R-41 slice 1 | -| 4 — operator one-pager | **DONE** | `documentation/PROMPT-TEMPLATE.md` | -| 5 — §6 leftovers | **DONE** | controller **v0.157.1** + `scripts/build-hub.sh` **v1.23.0** | -| 6 — HP mini ISO | **DONE, 2 artifacts not 3** (see below) | `v1.22.0` hp ISOs, both loaders | -| 7 — rulings + R-17 | **DONE** (R-17 execution is operator-console) | ROADMAP + capability map | +## Part 2 — the HP boot stick -Two questions were put to the operator rather than guessed; both are recorded where they landed. +Written on felhom-pve under a format-operation evidence bar. **Two USB-shaped devices were present, +and that turned out to matter:** -## Part 6 — the HP mini ISOs +- **`/dev/sdb` — ADATA HD710 PRO, 932G. EXCLUDED. It is the live customer data drive**, mounted at + `/mnt/hdd_1` and bind-mounted into guest 9201 (`mp8`) — immich and calibre-web data. Its by-id + path is `usb-ADATA_…`, but **`udevadm` reports `ID_BUS=ata` and sysfs `removable=0`**, so the two + required positive checks exclude it on their own. A bare-`/dev/sdX` workflow would have offered + `sdb` and `sdc` side by side with nothing but a letter between them. +- **`/dev/sdc` — Samsung Flash Drive, serial `0375123010001586`, 119.5 GiB. The target.** -**Version: rebuild at `v1.22.0`, no bump.** Phase-0 probe confirmed **no functional change to the ISO -tooling since the v1.22.0 artifacts** were built on 2026-07-19: the last content commit before that -build was `8feed6d` (08:57:51, build at 09:00), and everything since is docs or `chmod +x` mode bits. -`ISO_VERSION` tracks the Felhom release the ISO is tagged to; adding a build profile does not move it, -and the profile name already makes the artifacts unmistakable, so no date-stamping was needed. +Checks, all passed: `ID_BUS=usb`; `removable=1`; absent from `findmnt` (device and every partition); +absent from `/etc/fstab`; not an LVM PV (only `/dev/sda3`); no ZFS pools exist on the host; not +referenced by any guest config; 0 open handles. Destroyed content: an old `iso9660` "PVE" install +image. **Operator confirmed the printed device before any byte was written.** -New: `scripts/iso/profiles/hp.profile`, committed. It mirrors `n100.profile` with **one deliberate -difference — the loader default stays at the stock signed `shim`.** `mkimage` exists to work around -the N100's AMI firmware GRUB relocation defect; it is not a Felhom requirement, and it costs Secure -Boot (it is unsigned). HP business firmware should take shim. - -| ISO | sha256 | bytes | -|---|---|---| -| `felhom-pve-9.2-1-v1.22.0-hp-generic.iso` (**shim**) | `2b51d74c0eaf5155fd42a137dff803b9ab8692294cedcb727c9a80658c9f7823` | 1 705 338 880 | -| `felhom-pve-9.2-1-v1.22.0-hp-generic-mkimage.iso` (**mkimage**) | `09700442c297edcf81bdb9d6fddd14e79ba62091e7c2f183fa687c513f061142` | 1 704 482 816 | - -Both in `180:/mnt/5_hdd/felhom.eu/felhom-iso/out/` with `.sha256` + `.manifest.txt`. Build logs in -`felhom-iso/logs/build-hp-{shim,mkimage}-2026-07-21.log`. Input PVE ISO `proxmox-ve_9.2-1.iso`, -sha256 `4e88fe41…`. Both manifests: `secret-bearing : no`. - -**`validate-answer` was gated on OUTPUT text, not `$?`** — the documented trap (it exits 0 on -failure). Both logs show `The answer file was parsed successfully, no errors found!` through the -`gate_validate_answer` output-parse gate. - -### Two artifacts, not three — and why that is the right answer - -TASK-E asked for a shim pairing ISO, a mkimage pairing ISO, and a separate `hp-safety` match-nothing -ISO. **Both ISOs above ARE safety ISOs.** The `n100-safety` precedent is not a separate flag: it is -the committed profile built as-is, because that profile ships a match-nothing disk filter. A third -artifact would have been a byte-for-byte duplicate of one of these. - -Arming the real install requires the target SSD's `ID_SERIAL_SHORT`, which **cannot exist until the -board is in hand** — inventing it is exactly the input the conventions say to ask about rather than -guess. So all HP artifacts are deliberately inert, which satisfies "the first thing the new board ever -runs must be provably harmless" more strongly than the three-artifact split would have. - -**Verified from INSIDE both ISOs, not merely from the profile** — the embedded `answer.toml` extracted -with `osirrox` reads: +The identity was **re-asserted at write time**, not only at survey time, in case of re-enumeration +between the confirm and the write. ``` -[disk-setup] -filesystem = "ext4" -filter-match = "all" -filter.ID_SERIAL_SHORT = "REPLACE-WITH-TARGET-SSD-SERIAL" +1705338880 bytes (1.7 GB, 1.6 GiB) copied, 55.9 s, 30.5 MB/s +device sha256: 2b51d74c0eaf5155fd42a137dff803b9ab8692294cedcb727c9a80658c9f7823 +iso sha256: 2b51d74c0eaf5155fd42a137dff803b9ab8692294cedcb727c9a80658c9f7823 +RESULT: MATCH - byte-proven ``` -### Operator boot cheat-sheet — HP mini +`dd … oflag=direct conv=fsync`, then the ISO's exact byte-length read back **from the device** and +hashed — byte-proof, not exit-code-proof. The source ISO's sha256 was verified after transfer to +felhom-pve and matches the published hash. The customer data drive was re-checked after the write and +is still mounted at both its paths. -1. **Flash `…-hp-generic.iso` (shim) FIRST.** Leave Secure Boot **ON**. Expect: GRUB loads, the Felhom - boot screen appears, the installer starts and then **aborts having touched no disk**. That is - success — it proves the loader and the firmware setting before anything destructive exists. -2. **If it fails at GRUB** — a `relocation` error, "no bootable device", or a hang before the boot - screen — that is the N100's F1 defect on this board too. Switch to - `…-hp-generic-mkimage.iso` **and set Secure Boot OFF** in firmware. mkimage is unsigned; with - Secure Boot enforcing the firmware refuses it outright ("Access Denied" — proven, RUNBOOK-B leg 3). -3. **The safety ISO always precedes the real one.** Never boot an install-armed ISO on this board - until one of the two above has been observed to abort cleanly. -4. **To arm the real install**, tell CC which loader worked and give it the internal SSD's - `ID_SERIAL_SHORT` (`lsblk -o NAME,SERIAL` from any live boot). **Confirm it is the internal SSD and - not an attached backup drive** — that check is what protected the N100's external HDD on the - rehearsal run. CC copies `hp.profile` to an uncommitted working profile with the real serial (the - `n100-demo.profile` precedent — a real serial is never committed) and rebuilds. -5. **No remote install step was attempted.** The boot is physically yours. +### Boot cheat-sheet (unchanged, restated) -## Part 4 — the operator one-pager +1. **Boot the stick with Secure Boot ON.** This is the shim variant. Expect the Felhom boot screen, + then the installer **aborting having touched no disk** — that is success. +2. **On a GRUB failure** (`relocation`, "no bootable device", a hang before the boot screen): use + the **mkimage** ISO and set **Secure Boot OFF**. It is unsigned; SB-enforcing firmware refuses it. + That ISO is at `180:/mnt/5_hdd/felhom.eu/felhom-iso/out/…-hp-generic-mkimage.iso` and is not on + this stick. +3. **Both artifacts are inert** (match-nothing disk filter). The real install ISO does not exist yet + — it needs the HP's internal SSD serial, which needs the board in hand. Send me the serial and + which loader worked, and I will build it. +4. **The first boot is yours.** No remote install step was attempted. -`documentation/PROMPT-TEMPLATE.md` gains a standard section between the header and §0. Mandatory for -M+ tasks and anything carrying a STOP; guidance covers audience, the four things it must say in order, -and the rule that it is a summary and never the only place a requirement appears. TASK-B/C/D cited as -precedent. §10's seam-discipline row was **verified present** (line 229) and not duplicated. +## Part 1 — app lifecycle (summary; detail in `felhom-controller/REPORT.md`) -## Part 5 — both §6 leftovers +`available` / `hidden` / `abandoned` as a `.felhom.yml` field, because the `retired/` directory move +shipped hours earlier would have orphaned every customer running a withdrawn app. Deploy gate is +server-side and fail-closed; unknown values fail open; orphan detection never sees the field. -- **`.gitignore` anchor** → controller v0.157.1. Details and the both-directions verification are in - `felhom-controller/REPORT.md`. -- **The hub `build.sh` was not in any repo.** Adopted as `scripts/build-hub.sh` **v1.23.0**: build - logic byte-identical (verified by diff), plus a `SCRIPT_VERSION` and a header naming the split. The - operative path `/mnt/5_hdd/felhom.eu/build/felhom-hub/build.sh` is now a **symlink** to the repo - copy — one source of truth, repo edits live immediately (the `install_skills.py` pattern). Previous - copy retained as `build.sh.pre-adopt-2026-07-21`. +Live on 9201 through the real endpoints: plant-it renders **0 times** on the catalog page (control +app 10), a direct deploy POST returns **409 „Ez az alkalmazás jelenleg nem telepíthető."**, the app +page carries the permanent notice with no Telepítés button. -## Part 7 — rulings recorded +**v0.158.0 shipped a defect that I caught during live validation, not from the suite:** the lifecycle +predicates had pointer receivers, and html/template cannot call those on the non-addressable value +the handler passes — so **every `/apps/` returned 500**, for every app, while compiling cleanly +with a fully green test suite. Fixed in v0.158.1. The lesson is recorded in the controller report and +the R-57 row: **a template method call is only checked when that template actually runs**, so a +render test is the only coverage for template-facing methods. -R-25b (full-teardown cascade, three acks + typed name, **re-sized S → M**, spec to follow, no longer -blocks R-3) · R-11 (direct Messenger; the doc is the architect's) · R-42 (option (a)) · R-4 (**COMPLETE** -— freemail.hu operator-verified) · R-55 + R-41 slice 1 marked shipped · **new R-56** (app difficulty -classification — the constructive half of the glance ruling; glance stays). Capability map updated on -the boot-orphan row and the escrow-ceremony row. +## Part 3 — R-55 live leg, PROVEN -### R-17 — spike-lite result: it CANNOT be done through tooling +immich (drive-backed) stopped through the **real UI endpoint** → `compose down` → 0 containers; +calibre-web (drive-backed) and bookstack (not drive-backed) left running; `pct reboot 9201`. -Ruled DELETE. CC did **not** improvise credentialed API calls, and found there is no tooling path: +> `14:29:28 [gate] … live bind confirmed — recreating drive-backed app calibre-web … onto /mnt/felhom-drives/hdd_1` +> `14:29:29 [gate] … 1 drive-backed app(s) left stopped — zero containers means the customer stopped them on purpose` -- `felhom-tenantsync.sh` does not touch Hetzner at all — PBS namespaces/tokens only. -- The hub **can** delete sub-accounts (`hetznerapi.DeleteSubaccount`), but its only caller is - `offsite.Deprovision`, which selects by the `felhom-customer=` label as part of a customer RESET. - A legacy archive sub-account from the 2026-07-09/10 spikes carries no such label, so no verb targets it. -- The only in-repo references are **test fixtures** (`hub/internal/hetznerapi/fake.go` generates the - `u629193-sub%d` pattern; `dr_recipe_test.go` uses it as a literal). Nothing live points at it. +**immich came back STOPPED.** Hours earlier the identical fixture brought it back running — same +experiment, opposite result. calibre-web recreated, bookstack back, `[bootrecon] no boot-orphaned +apps`, **zero alerts**, reboot → steady state in **~15 s**. The `left stopped` line fired in +production for the first time, so the honoured path is observable rather than silent. -**Operator console action:** Hetzner Storage Box console → box `u629193` → Sub-accounts → -`u629193-sub1` → confirm the directory holds only the old-box archive → Delete. The 9/9 byte-identical -restores are the sign-off; deleting the sub-account destroys its data. +## Part 4 — skipped -## Observations (recorded, not acted on) +Operator ruled no Docker Hub login. The gate therefore still exits 2 (INCONCLUSIVE) on a full sweep +rather than producing anonymous throttled accusations — which is the designed behaviour, not a +failure. One `docker login` on DooPlex plus +`python3 scripts/check-image-resolvable.py` closes it whenever convenient. -1. **A gate I shipped nearly shipped a lie, and it is the most transferable thing here.** The first - full catalog resolvability sweep reported **24 of 65 pins dead**, including `postgres:16-alpine` - and `redis:7-alpine`. They are fine — Docker Hub had started throttling. Worse, `docker manifest - inspect` prints `toomanyrequests: …` and **still exits 0**, the identical exits-0-on-failure shape - as `validate-answer` in the ISO tooling. Two independent instances of the same trap now sit in two - different toolchains; it is worth treating "does this tool signal failure through its exit code?" - as a standing question rather than a per-tool footnote. The gate now resolves ambiguity to - INCONCLUSIVE, never to an accusation. -2. **The full 52-app resolvability table is still owed.** DooPlex is not logged in to Docker Hub, so - the sweep exits 2 (incomplete) rather than producing the table. One `docker login` + re-run closes it. -3. **R-55's live reboot leg is not run** — see `felhom-controller/REPORT.md`. Shipped and wired, not - yet proven live. -4. **wanderer is re-pinned but never deployed.** It went from 1 service to 3 with a second public - hostname; it deserves a real deploy on the demo box before anyone is invited to it. -5. `reuse_refs_check.py` resolves REUSE.md citations against the repo root and so cannot follow a - cross-repo pointer; the catalog's `healthprobe.go` citation still flags. Path corrected; the - checker limitation is pre-existing. -6. **The `felhom-testing` skill's "known flake" line was actively harmful** — it instructed every - future session to dismiss a true positive, for weeks. Removed, and generalised: "known flake, just - re-run it" is a diagnosis and needs evidence like any other. +## Remaining + +- **Agent v0.93.0 is source-only** — the recovery-code wordlist fix is committed but unbuilt and + unpublished; it rides the next agent train. Nothing on any host runs it yet. +- **R-17** — deleting the `u629193-sub1` archive is an operator **console** action; no tooling verb + targets it. Exact steps are in the previous session's report. +- **HP first boot** is physically yours (above). +- **The full catalog resolvability table** is still owed (Part 4). +- **wanderer** was re-pinned last session to a 3-service upstream shape and has never been deployed. +- **`hidden`** lifecycle state has no catalog user yet; only `abandoned` is exercised end to end. + +## Observations + +1. **I pushed a commit that does not build.** A `git stash`/`stash pop` used to compare + `reuse_refs_check.py` output silently discarded the staged index, so the first lifecycle commit + carried only the new test files. Repaired with a follow-up commit rather than a force-push — + rewriting already-pushed history is the worse trade. **Never `git stash` mid-staging.** +2. **The stale-credential trap bit again, and the memory note already described it.** The controller + password in `~/.config/credentials` is wrapped in **single** quotes; I stripped only double + quotes, sent the quotes as part of the password, and concluded the credential was stale. It was + not. Strip both quote styles. +3. **Two USB devices, one of them live customer data**, is the case the `ID_BUS=usb` + `removable=1` + rules are actually for — the dangerous device carried a `usb-…` by-id name and would have looked + plausible in any listing that showed model and size alone.