ISO 1.29.0 PUBLISHED — bilingual console, proven on both menu entries (R-559)
gates / gates (push) Successful in 22s

Live at iso.felhom.eu, sha256 dceacae5da247d76cad065bf6c0d3bbefac8d8a5f8e571
db2fd8449a97e94829, and both download pages now name it.

Every gate criterion is recorded with its OBSERVED value in
documentation/tests/iso-release-1.29.0-2026-09-18/ — including two proof
installs from the published bytes, one per boot-menu entry, each with a first
boot AND one reboot: /etc/issue bilingual with zero hits for 8006, pvebanner
masked, package 1.29.0 installed, unit enabled and fired, pairing code present,
and the installed script byte-identical to repo HEAD. G11: the downloaded bytes
hash to the published checksum.

A defect was caught BETWEEN builds by looking at the screen rather than at the
config: the second menu entry read "Felhom telepítés (szöveges mód) / Install
Felhom (text mode)" — 58 characters — and the GRUB menu box cut it at "Instal".
The English half was unreadable on the boot screen. Shortened to "… / text" and
rebuilt; the published image is the rebuilt one. The Hungarian half is the part
that may not change, so the English half is the part that gave.

Teardown: VMs 323/324/325 destroyed, the two unclaimed appliance registrations
discarded (zero left in `registered`), guest 9201 untouched — 23 containers
before and after. The two stale *.rootpw.txt files were shredded from the
publish source directory before the upload ran from it (R-587, files gone; the
guard that would stop it recurring is still open).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-18 21:16:57 +02:00
parent 8d539f971c
commit 31eeb36e88
14 changed files with 132 additions and 53 deletions
+22 -35
View File
@@ -1,52 +1,39 @@
# STATUS — what works, what's broken, what's next
**Updated 2026-09-18 (late evening) — the box's own screen now speaks English too.**
**Updated 2026-09-18 (night) — the new installer is published, and the box's screen speaks English too.**
> **Ready for a volunteer: yes.**
**What changed.** Two screens a person meets before they ever see a dashboard were Hungarian only:
the text on the box's own monitor while it waits to be paired, and the download page where the
installer comes from.
the text on the box's own monitor while it waits to be paired, and the download page. Both are now
bilingual — Hungarian exactly as before, then the same thing in English under it. The boot menu too.
**The box's screen cannot know a language** — when it shows the pairing code, nobody has told it who
owns it yet. So it shows **both**: the Hungarian exactly as before, then the same thing in English
underneath. Same for the "your box is connected" screen and the login text. The boot menu gets an
English half too.
**`felhom.eu/en/download` is live**, and both download pages now point at the new installer.
**The download page has an English twin, and it is live now:** `felhom.eu/en/download`. Each page
links to the other. The rest of the website stays Hungarian, as you ruled.
**The new installer is published: version 1.29.0.** I installed from the finished file **twice** —
once from each boot-menu choice — on throwaway machines, let each one boot and then rebooted it, and
checked the screen every time. Both showed the bilingual text, no Proxmox address, and the pairing
code readable in both languages. Then I downloaded the published file back and checked it is
byte-for-byte the one I tested.
**What I measured rather than eyeballed.** Every Hungarian line was captured before a single English
word was added, and the test compares them letter by letter. The English blocks are checked for
stray Hungarian letters. And the whole screen was counted: the paired-up text is **24 lines on a
25-line screen** — it fits with one line to spare, so the test now refuses anything taller. Two more
lines and the Hungarian code would scroll off the top.
**Something was wrong and I caught it by looking.** The first build's second menu line was too long
for the box GRUB draws, so its English half was chopped off mid-word on the boot screen. I shortened
it and built again; the published image is the fixed one. The Hungarian is what may never change, so
the English is the half that gave way.
**Two things were already broken when I started.** The installer's own test suite had been failing
for two days and nobody saw it, because it is not part of any automatic check — it only runs when
someone runs it. And the release checklist said every screen must be Hungarian, which would have
blocked exactly what you asked for; your September ruling replaced that, so I rewrote the checklist
item rather than skipping it.
**Two things were already broken before I started.** The installer's own test suite had been failing
for two days and nobody saw — it is not part of any automatic check. And the release checklist still
said every screen must be Hungarian, which would have blocked exactly what you asked for; your
September ruling replaced that, so I rewrote the checklist item rather than skipping it.
**Rows.** Three opened.
**Rows.** Three opened, one closed, one half-done.
**Needs you — the installer image is built but NOT published, and that is deliberate.**
**Needs you — nothing for the installer.** It is done and live. The old version stays online, so
going back is one edit if anything looks wrong.
Publishing to the download site is public and cannot be undone, and the checklist requires a real
install from the finished file on **both** boot-menu choices, plus a reboot, on a real machine. That
is a supervised job, so I stopped there. The file is ready on this machine:
**Still open from earlier today:** raise the box floor to 0.256.1 (so English households get English
alerts everywhere), and whether to change the shared demo password.
- `felhom-installer-1.29.0-pve9.2-1.iso`, checksum starts `c67ceaa3`
- Everything checkable without installing has been checked and passes.
**If you do nothing:** nothing breaks. The download pages still point at the current published
installer (1.28.0), which is correct and working — I deliberately did not point them at a file that
is not there yet. The English page and the bilingual screens simply wait for the next image.
**Also still open from earlier today:** raise the box floor to 0.256.1, and whether to change the
shared demo password.
---
## Previous note
+2 -2
View File
@@ -735,7 +735,7 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-555** | **[P3-LOW] The wire-contract gate counts a field as received when its name appears in a Go COMMENT on the receiving side.** FOUND 2026-09-17 by CC adding the report's `language` field (controller v0.247.0): `scripts/wire_contract_gate.py` passed WITHOUT an allowlist entry, because `receiver_tokens()` tokenises whole files and the word „language" occurs hub-side only in a comment (`hub/internal/web/configs.go:558`, „this page's existing language"). The shape is the gate's own named failure class (name-for-fact, R-421): any English tag name that also appears in hub prose passes unread. The field was allowlisted by hand with this row named. **Fix shape:** strip `//` and `/* */` comments (and template `{{/* */}}`) before tokenising; add the decoy „a tag whose name appears only in a receiver comment must convict"; expect a handful of currently-passing tags to surface — each is a finding, not noise. | **READY - rank P3-LOW; owner: CC** |
| **R-557** | **[P3-LOW] Localisation slice 2 — Go-side customer strings follow the language.** PLAN 2026-09-17 (10 §10). Inventory §2.2: 947 shown + 184 error literals in 84 files; 237 format strings, 111 concatenations, 42 numeric `%d` (English plurals). Flash messages travel inside the redirect URL (`?flash=<Hungarian>`) and must become keys; `cloudflare/countries.go` (113 country names); alert texts; handler errors printed with `err.Error()`. **After R-553** (the four compare-not-show sites). Cost 16–20 CC-hours. **DEPENDENCY added 2026-09-17 (R-553 shipped, v0.251.0):** the behaviour-by-wording sites are fixed, so this slice is unblocked — EXCEPT one producer: `"Sikeres — nincs mentésre jelölt alkalmazás"` (controller/internal/backup/offbox.go) must stay Hungarian until **R-570** closes, because the page's legacy fallback still reads it on boxes that have not run off-site since 0.251.0. Everything else this slice touches is now decided by a kind, not by its words. **RELEASE A SHIPPED 2026-09-18 (controller v0.252.0):** 226 Go literals converted -- flash-as-key (8 writers, 8 readers, legacy prose still shown verbatim), page data and view-model text, the `internal/api` JSON answers, the alert banners (`Alert.MessageKey`), 237 country names at DISPLAY (the cloudflare table is untouched -- only CODES are on the wire, the task claimed otherwise), and the four app-named page titles (R-566 CLOSED). New gate `i18n_go_parity.py` + `i18n_go_base.json` (7 467 base-commit literals, frozen) refuses any key whose Hungarian is not byte-identical; three decoys. Wire goldens freeze `internal/monitor` and `internal/notify` -- the hub MAILS the event message when it has no `customerMessages` entry, so those stay Hungarian until R-558. `HU_FORMAL_CEILING` 16 -> 18 (measured, no word changed). **RELEASE B SHIPPED 2026-09-18 (controller v0.253.0): all 179 error literals carry a key** via `util.MsgError` -- `Error()` is still the Hungarian byte for byte, `errors.Is` answers for the kind AND a wrapped cause, an error ARGUMENT renders recursively, and a foreign error (restic/docker/ssh/stdlib) prints verbatim. 76 display sites go through `errText`, pinned by `TestNoErrErrorInPageOutput`. `memoryVerdict` returns an error; `UpdateRefusal` gained a `Cause`. Plurals are a BUNDLE rule (a key with `.one`/`.other` takes its count first), not a call-site flag. **ZERO Hungarian error literals remain** (ASCII search with both controls, case-insensitive). Two tooling defects found and filed: R-576 (the converter dropped concatenations and the gate could not see it) and the case-sensitive counter. New gap: R-575. **RELEASE C SHIPPED 2026-09-18 (controller v0.254.0) -- SLICE 2 CLOSED.** ~70 saved-note producers write in the BOX language at write time (s16 option 1; a household that switches sees the previous run's note in the old language until the next run). `EndRestoreOp` takes no Hungarian literal from anywhere. The switch became a GLOBE on the dashboard and on the pages outside the dashboard chrome; a visitor with no session gets a display-only `felhom_lang` cookie that `langFor` reads ONLY when there is no session, and a successful CLAIM carries it into the setting (s16). Two parity blocks, measured with a real diff: 3 change shapes across 106 fixtures (footer; the recovery globe posting to the household switch; the login/claim globe posting to /lang), 5 byte-identical. **A DEADLOCK was introduced and caught by the suite hanging (R-578).** Gaps filed: R-572..R-578. | **CLOSED 2026-09-18 - controller v0.252.0 + v0.253.0 + v0.254.0** |
| **R-558** | **[P3-LOW] Localisation slice 3 — the hub's customer e-mails follow the household's language; the operator sets it at customer creation.** PLAN 2026-09-17 (10 §3, §10), operator decision 2. The box already reports `"language"` (controller v0.247.0); nothing hub-side reads it. Build: a per-customer language on the hub (default hu) set at creation and rendered into `controller.yaml` beside `customer.*` (`hub/internal/configgen/configgen.go`), used by the box only while `settings.json` has no choice; the dispatcher picks the language the box REPORTS; English for the 39 `customerMessages`, the 4 severity labels, the body wrapper, the claim/re-enroll/reset/claimed/self-bind mails and the public bind page (inventory §2.3). Delete the `language` allowlist entry in `wire_contract_gate.py` when the field is read. Cost 6-8 CC-hours, one hub + one controller release. **THE WIRE SURVEY (R-557 release A, 2026-09-18) HANDS THIS ROW ITS INPUT LIST:** (1) `internal/notify/notifier.go` -- 31 event messages. The task assumed the hub composes its own mail from the event kind; it does NOT. `FormatCustomerEmail` uses `customerMessages[eventType]` when it has one and **falls back to the controller's message when it has none**, and appends it as „- Üzenet: %s” whenever the two differ -- several types carry no entry precisely so the controller's dynamic sentence IS the mail (hub `api/handler.go` L2034/L2045/L2066). So an English household's mail is Hungarian until this row ships. (2) `internal/monitor/healthcheck.go` -- 5 storage sentences in report `health.warnings`/`health.issues`, operator-facing. Both are frozen by wire goldens in those packages, so no later slice can translate them by accident; this row is what unfreezes them. **SLICE 3 SHIPPED AND CLOSED 2026-09-18 — hub v0.118.0/v0.118.1 + controller v0.256.0/v0.256.1.** The hub reads the language the box reports and writes the household's e-mail in it; nothing an operator reads moved. 56 mail goldens per language captured from hub v0.117.0 BEFORE any string moved, and all 56 Hungarian ones pass unchanged. Order: last reported -> created-with -> hu. `message_customer` carries the sentences the box composes and the hub cannot translate; 19 producers render both from ONE key, and their Hungarian is byte-identical by the Go parity gate. A Hungarian household sends no second copy, so the fleet's payload is unchanged. The bind page is per-language with `expired` pinned to Hungarian (the language would otherwise be the oracle the text refuses to be). Proven live twice: a real English e-mail read in the inbox and the Hungarian one 74 s later (Part A), and the box's push log showing `[hu-only]` then `[+household(en)]` for the same event 57 s apart (Part B). Gaps filed: R-581 (received_at ties), R-582 (the English guard), R-583 (the test mail), R-584 (credential litter), R-585 (six unconverted producers). R-555 closed with it. | **CLOSED 2026-09-18 - hub v0.118.1 + controller v0.256.1** |
| **R-559** | **[P3-LOW] Localisation slice 4 — the console banner and the download page in English.** IN SCOPE — operator ruling 2026-09-17 evening („yes", 10 §11 1b). PLAN 2026-09-17 (10 §10, open decision 1b): the starter listed them; the operator's scope ruling names „the controller, emails, guide, app catalog" and not them. Inventory §2.4: 34 banner lines (`scripts/iso/felhom-bootstrap.sh`, printf to the console; `/etc/issue` byte-coupled to `iso/pkg/debian/postinst`, console font avoids ő/ű) and 32 strings on `website/letoltes.html`. Cost 4–6 CC-hours plus an ISO release train. **SLICE 4 SOURCE SHIPPED 2026-09-18 (ISO 1.29.0 source + the English download page); the IMAGE IS NOT PUBLISHED — that is the operator's step.** The three console texts are bilingual: Hungarian block byte-for-byte as before, then English, inside the same frame; the GRUB entries gain an English half; `postinst`'s byte-coupled copy of the issue text changed identically. The Hungarian is a GOLDEN captured before any English existed, and the harness also pins no-Hungarian-letter-in-English (Hungarian block as the positive control), ≤ 80 columns, and ≤ 25 ROWS — the pairing banner is 24 on a 25-row console, one row of margin. `felhom.eu/en/download` is LIVE, linked both ways, and a site gate refuses the two download pages naming different installer files or checksums. **The release gate's G16 required every Felhom string to be Hungarian and would have stopped this**; ruling 1b (2026-09-17) supersedes that scope, so G16 was REWRITTEN, not waived. Built and gate-checked mechanically: `felhom-installer-1.29.0-pve9.2-1.iso`, sha256 `c67ceaa3…fb02`. Publishing needs a proof install on BOTH menu entries plus a reboot. Gaps filed: R-586, R-587, R-588. | **SOURCE SHIPPED 2026-09-18 — PUBLICATION PENDING (operator: proof install on both menu entries, then publish 1.29.0)** |
| **R-559** | **[P3-LOW] Localisation slice 4 — the console banner and the download page in English.** IN SCOPE — operator ruling 2026-09-17 evening („yes", 10 §11 1b). PLAN 2026-09-17 (10 §10, open decision 1b): the starter listed them; the operator's scope ruling names „the controller, emails, guide, app catalog" and not them. Inventory §2.4: 34 banner lines (`scripts/iso/felhom-bootstrap.sh`, printf to the console; `/etc/issue` byte-coupled to `iso/pkg/debian/postinst`, console font avoids ő/ű) and 32 strings on `website/letoltes.html`. Cost 4–6 CC-hours plus an ISO release train. **SLICE 4 SOURCE SHIPPED 2026-09-18 (ISO 1.29.0 source + the English download page); the IMAGE IS NOT PUBLISHED — that is the operator's step.** The three console texts are bilingual: Hungarian block byte-for-byte as before, then English, inside the same frame; the GRUB entries gain an English half; `postinst`'s byte-coupled copy of the issue text changed identically. The Hungarian is a GOLDEN captured before any English existed, and the harness also pins no-Hungarian-letter-in-English (Hungarian block as the positive control), ≤ 80 columns, and ≤ 25 ROWS — the pairing banner is 24 on a 25-row console, one row of margin. `felhom.eu/en/download` is LIVE, linked both ways, and a site gate refuses the two download pages naming different installer files or checksums. **The release gate's G16 required every Felhom string to be Hungarian and would have stopped this**; ruling 1b (2026-09-17) supersedes that scope, so G16 was REWRITTEN, not waived. Built and gate-checked mechanically: `felhom-installer-1.29.0-pve9.2-1.iso`, sha256 `c67ceaa3…fb02`. Publishing needs a proof install on BOTH menu entries plus a reboot. Gaps filed: R-586, R-587, R-588. **PUBLISHED 2026-09-18: ISO 1.29.0, sha256 `dceacae5…e94829`, live at iso.felhom.eu, and both download pages now name it.** Proof installs on BOTH menu entries (graphical VM 323, text VM 325), each with first boot AND one reboot: `/etc/issue` bilingual with 0 hits for 8006, `pvebanner` masked, package 1.29.0 installed, unit enabled and fired, pairing code present, and the installed script byte-identical to repo HEAD. G11 round trip: the downloaded bytes hash to the published checksum. **A defect was caught between builds by looking at the screen**: the second menu entry's English half was cut off at `Instal` by the GRUB box width, so it was shortened and the image rebuilt. VMs destroyed, the two unclaimed appliance registrations discarded, guest 9201 untouched (23 containers before and after). | **CLOSED 2026-09-18 — ISO 1.29.0 published** |
| **R-560** | **[P3-LOW] Localisation slice 5 — catalog cards, settings and first steps in English.** PLAN 2026-09-17 (10 §7, §10). Inventory §2.5: 835 strings, ~4 984 words across all 53 `.felhom.yml` (use_cases 262, first_steps 233, deploy_fields descriptions 79 / labels 68, prerequisites 60, description 56, tagline 53). Proposed format: an `i18n: {en: {...}}` block inside each `.felhom.yml` (controller's `yaml.Unmarshal` ignores unknown keys, so older controllers are unaffected), field-by-field fallback. Needs a controller change to read it and catalog copy gates with an English rule. Cost 12–16 CC-hours. | **READY - rank P3-LOW; owner: CC (catalog + controller)** |
| **R-561** | **[P3-LOW] Localisation slice 6 — the volunteer guide in English, then a stranger's first hour in English; closes R-516.** PLAN 2026-09-17 (10 §10). `runbooks/VOLUNTEER-first-hour.md`: 44 paragraphs, ~1 579 words. Then the 2026-09-14 walk repeated by an English speaker on a fresh box, R-516 closed against `audits/I18N-INVENTORY-2026-09-17.md`. Depends on R-556..R-560. Cost 6–8 CC-hours plus the attended walk. | **READY - rank P3-LOW; owner: CC** |
| **R-562** | **[P3-LOW] Dates and sizes are not formatted for any locale — and the Hungarian pages disagree with themselves.** FOUND 2026-09-17 by the i18n inventory §2.8: the two template date layouts differ (`2006. 01. 02. 15:04` Hungarian vs `2006-01-02 15:04` ISO); 10 layout literals in `internal/web` Go and 25 elsewhere pick formats ad hoc; sizes print a decimal POINT (`%.1f GB`, 4 helpers) where Hungarian uses a comma; `timeAgo`/`nextRunLabel`/`pruneLabel` produce Hungarian words outside the three converted pages. Not changed by v0.247.0 (Hungarian bytes are frozen by the parity rule). **Fix shape:** one date and one size formatter per language in `internal/i18n`, the Hungarian output deliberately changed in ONE reviewed release with the parity fixtures re-captured for that release only and the change named in its CHANGELOG. Needs an operator word on the Hungarian format (comma, date style). | **READY - rank P3-LOW; owner: CC** |
@@ -762,7 +762,7 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-584** | **[P2-MED] Credential-bearing probe scripts were left in a live guest's `/tmp` for hours, across three releases.** FOUND 2026-09-18 while cleaning up after slice 3's live proof: `probeB.sh`, `probeB2.sh`, `probeB3.sh`, `probeC.sh` and `probeD.sh` were still in demo-hp guest 9201's `/tmp` from slice 2's releases B, C and D earlier the same session, and all five carried the controller password INLINE (`-d "password=$PW"` with the value substituted). The standing rule in `.claude/rules/ui-hungarian.md` says exactly this: *delete any credential-bearing helper from `/tmp` (host AND guest) when done*. They were not deleted at the end of each phase; they were found by grepping my own litter for secret-shaped strings before removing it, which is a check that happened only because a later phase went looking. All five are shredded. **Why it is a row and not a note: the rule existed, was loaded, and was still not followed three times running — so the rule is not the mechanism.** The password is the shared demo one (Tier-0 boxes, `~/.config/credentials`), unchanged; rotating it is cheap and is the operator's call. **Fix shape:** never inline a credential in a helper — write it to a mode-0600 file and read it with curl's `@file` form, which this run did and which is why this run's own scripts were clean; and make the last act of a phase that pushed a script to a box the `shred -u` of it, the same way R-320 made evidence-copying the last act of a phase. | **READY - rank P2-MED; owner: CC (the mechanism), operator (whether to rotate)** |
| **R-585** | **[P3-LOW] Six event producers still send Hungarian only, so an English household can see one Hungarian line in some mails.** FOUND 2026-09-18 by localisation slice 3 Part B (R-558, controller v0.256.0), which converted 19 of the customer-facing producers and left these: `backup_failed`, `db_dump_failed`, `backup_integrity_ok`, `backup_integrity_failed`, `offbox_enlarge_blocked` and `local_api_endpoint_drift`. **Why they were left:** each receives its sentence already FINISHED from another package, so the key and its arguments no longer exist by the time the notifier sees it — converting them means changing their callers, not the notifier. The 15 operator-tier types are deliberately excluded and are NOT part of this row: the operator reads Hungarian. **Why it matters more than it looks: `offbox_enlarge_blocked` has no `customerMessages` entry on the hub**, so its raw sentence IS the household's mail rather than an extra line under a translated headline — for that one type an English household gets a wholly Hungarian mail, not a mostly-English one. **Fix shape:** push the key and its arguments down from each caller (the shape slice 2 release B already used for errors, `util.MsgError`), then add each to the `convertedProducers` table in `internal/notify/message_customer_test.go`, which is the list both language tests walk. | **READY - rank P3-LOW; owner: CC** |
| **R-586** | **[P2-MED] The ISO bootstrap harness captured the console to a FILE, so each banner erased the one before it — two checks were RED for two days and nobody saw.** FOUND 2026-09-18 starting slice 4 (R-559): running `scripts/iso/test/bootstrap-modes.sh` unchanged at `183727db9c44` reported `FAIL: R-496: banner painted to the console seam` and `FAIL: R-496: banner names the Tulajdonosi jelmondat`. **Cause:** the script paints each banner with `> "$CONSOLE_DEV"`. On a real console that is a character device and truncation is a no-op, so every paint appears; pointed at a plain file, as the harness did, each paint TRUNCATES. Commit `c033b3b` (ISO 1.28.0, R-535, 2026-09-16) added `print_bound_banner`, which paints immediately after the pairing banner in the same invocation — from that commit the pairing banner was wiped before the check read it. `c033b3b` did not touch the harness. **Why it survived: the harness is in NO gate and NO CI run** — not in `repo_gates.py`, not in `.gitea/`; it runs only when a person runs it, and between 09-16 and 09-18 nobody did. FIXED in the same session: the harness points `FELHOM_CONSOLE_DEV` at a FIFO with a background reader, which restores device semantics (opening a FIFO with `>` truncates nothing) and lets a test see EVERY paint — which slice 4's golden checks then needed anyway. Production code untouched. **What is still open: the harness remains outside every gate.** It needs a container, so it cannot join `repo_gates.py --fast`, which is what both the pre-push hook and CI run — meaning a non-fast entry would still never execute. **Fix shape:** either give CI a container-capable job that runs it, or make the ISO release gate's G16 the place it is required (done for G16 this session — so it now runs at least once per ISO release, which is better than never but later than a push). | **READY - rank P2-MED; owner: CC** |
| **R-587** | **[P3-LOW] Two root-password files sit in the directory the public ISO is published FROM.** FOUND 2026-09-18 running the ISO release gate's credential criteria for slice 4: `/mnt/5_hdd/felhom.eu/felhom-iso/out/` holds `felhom-pve-9.2-1-v1.24.0-nested-probe-generic.iso.rootpw.txt` and `felhom-pve-9.2-1-v1.25.0-nested-vm-generic-mkimage.iso.rootpw.txt` from 2026-07-22/23, mode 0600, left by two appliance-mode builds. **They are NOT on the bucket** — both `https://iso.felhom.eu/<name>` return **404**, against a control (`felhom-installer-1.28.0-pve9.2-1.iso.sha256`) that returns 200, so the check is real and not a dead probe. **The only thing keeping them off a PUBLIC bucket is the `--include "felhom-installer-<VER>*"` pattern in the publish command**, and they are named `felhom-pve-*`, so the pattern misses them by an accident of naming rather than by design. A publish typed without the include, or an include widened to `felhom-*`, uploads root passwords to a world-readable bucket. **Fix shape:** shred the two files (they are three months old and their VMs are long gone), and make the release build refuse to run — or the publish step refuse to start — while any `*.rootpw.txt` exists in the out directory. A pattern that protects by coincidence is not a control. | **READY - rank P3-LOW; owner: CC** |
| **R-587** | **[P3-LOW] Two root-password files sit in the directory the public ISO is published FROM.** FOUND 2026-09-18 running the ISO release gate's credential criteria for slice 4: `/mnt/5_hdd/felhom.eu/felhom-iso/out/` holds `felhom-pve-9.2-1-v1.24.0-nested-probe-generic.iso.rootpw.txt` and `felhom-pve-9.2-1-v1.25.0-nested-vm-generic-mkimage.iso.rootpw.txt` from 2026-07-22/23, mode 0600, left by two appliance-mode builds. **They are NOT on the bucket** — both `https://iso.felhom.eu/<name>` return **404**, against a control (`felhom-installer-1.28.0-pve9.2-1.iso.sha256`) that returns 200, so the check is real and not a dead probe. **The only thing keeping them off a PUBLIC bucket is the `--include "felhom-installer-<VER>*"` pattern in the publish command**, and they are named `felhom-pve-*`, so the pattern misses them by an accident of naming rather than by design. A publish typed without the include, or an include widened to `felhom-*`, uploads root passwords to a world-readable bucket. **Fix shape:** shred the two files (they are three months old and their VMs are long gone), and make the release build refuse to run — or the publish step refuse to start — while any `*.rootpw.txt` exists in the out directory. A pattern that protects by coincidence is not a control. **The two files were SHREDDED 2026-09-18** from the publish source directory, immediately before the 1.29.0 upload ran from it. The directory now holds no `*.rootpw.txt`. **The mechanism half is still open**: nothing stops the next appliance build leaving one there, and nothing refuses a publish while one exists — the `--include` pattern still protects by coincidence. | **PARTLY DONE 2026-09-18 (files gone; the guard is not built) - rank P3-LOW; owner: CC** |
| **R-588** | **[P3-LOW] ISO release records live in two different places, so "was the gate run for this image?" cannot be answered by looking.** FOUND 2026-09-18: the release records for 1.27.0 and 1.27.1 are directories under `documentation/tests/iso-release-<ver>-<date>/`, and there is none for **1.28.0** — which led me to conclude its gate had not been run. It had: the record is `documentation/audits/evidence-backup-promise-2026-09-16/phaseD-iso-gate.txt`, inside an audit about something else. **The gate's own rule is "a criterion with no recorded observation is a criterion that was not run"**, and that rule is unenforceable while the records have no single home — the question it answers has to be settled by a full-text search for a checksum, which is what it took here. **Fix shape:** one home, `documentation/tests/iso-release-<ver>-<date>/`, and a line in `iso-release-gate.md`'s Result-recording section naming it; optionally a check that every published ISO version has a record directory. | **READY - rank P3-LOW; owner: CC** |
| **R-537** | **[P1-HIGH] The app-backup page labels the tier-1 backup „DB + Konfig + Adatok" and prints the app's data-drive size next to it — but the tier-1 unit contains NO drive-side app data at all.** MEASURED 2026-09-16 on the drill box (fresh install, controller 0.243.0, one drive, tier 2 and tier 3 both „Nincs beállítva"): five photos (3 000 000 B) were uploaded into Nextcloud through its own WebDAV interface, then the customer-visible „Mentés most" was pressed (`POST /api/backup/run` → 200, the unit grew 25 337 B → 978 MB). The resulting unit's `manifest.json` lists `db-dumps` + three **docker volume** dumps and nothing else; listing the 781 MB `nextcloud_nextcloud_html.tar` (29 346 entries, positive control `version.php` = 3 hits) gives **`Fotok` = 0 and `nyaralas` = 0**, and `./data/` is the empty bind-mount point. A `find` over the whole `backups/` tree for `*appdata*` / `*Fotok*` returns nothing. The page nevertheless renders „1. mentés … DB + Konfig + Adatok" and „Nextcloud Adatlemez 65.1 MB" — a size measured on exactly the data it does not copy (`internal/web/handlers.go:1176-1178`, `BackupContents`). **This is a truth defect, not a design defect:** `07-backup-architecture.md` §6.2 places nextcloud's file leg at **Tier 2 and Tier 3 only**, and its „[FACT] What the whole-guest tiers do NOT carry" says `mp8 /mnt/felhom-drives` is out of vzdump scope (confirmed live: „excluding bind mount point mp8 … (not a volume)"). So on a one-drive box with no off-site tier — the state every fresh install starts in — the household's files are in **no backup**, while the page says „Adatok". Same family as R-517/R-518. **Fix shape:** render tier-1 contents from the capture set actually written (`ComputeCaptureSet`), so a unit with no file leg reads „DB + Konfig" and the drive size is not shown beside it; and say on the page that the app's files need tier 2 or tier 3. Evidence: `audits/evidence-drill-0243-2026-09-16/phase2-f10.txt`. **CLOSED 2026-09-16 — controller v0.244.0, proven live.** The contents label is computed PER TIER from what that tier captures: Tier 1 says „Adatok" only when the app's data really is in the volumes the unit captured, and a class-A app carries one sentence saying where its files ARE protected. Proven on demo-hp through the page the customer opens: Paperless-ngx reads „1. mentés … DB + Konfig" with „Az alkalmazás fájljait a távoli másolat (és a második meghajtó) védi …", while its „2. mentés" row still reads „DB + Konfig + Adatok". Red-proof: restoring the old app-shaped label fails `TestAppBackupRows_Tier1LabelDoesNotClaimFilesItCannotHold`. **RE-PROVEN 2026-09-16 on a FRESH box** (installed from the built ISO 1.28.0, controller 0.244.0, off-site on by default): the Nextcloud row read „1. mentés … DB + Konfig" with the new sentence, „2. mentés … Nincs 2. (off-drive) másolat", „3. mentés Sikeres restic → …your-storagebox.de"; „DB + Konfig + Adatok" appeared ZERO times while the local unit held no file leg. | **CLOSED 2026-09-16 — controller v0.244.0 (proven live on demo-hp)** |
| **R-538** | **[P1-HIGH] A tier-1 app restore reports plain success and leaves Nextcloud listing files whose bytes were never in the backup — and it destroys the app's own trash, the customer's last copy.** MEASURED 2026-09-16 on the drill box, F10 („a child deletes the photo folder"): the five photos were deleted through Nextcloud (DELETE 204, PROPFIND 404), then restored through the page exactly as a customer would (`POST /backup/restore` `stack_name=nextcloud` `snapshot_id=helyi` → 302, finished in **35 s**, „A(z) nextcloud: 3 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult."). Afterwards the folder is back and **lists all five photos**, and **none of them opens**: `GET nyaralas-1..5` = 404 / 503×4 with `Sabre\DAV\Exception\NotFound`, while the positive controls at the same moment pass (`status.php` 200, WebDAV PUT 201, GET 200). Cause: the replayed MariaDB dump (11:01:45Z) knows the photos, the bytes live on `mp8` and were never captured (R-537). **Worse:** the bytes were still on the drive in Nextcloud's own trash (`appdata/nextcloud/admin/files_trashbin/files/Fotok.d1789556707/nyaralas-1..5.jpg`, all five present) and the restored database no longer references them — the trash listing comes back **empty**, so „restore from trash", the one route that would have worked, is gone. The customer is left with five unopenable photos, a success message, and no warning. **Fix shape:** before replaying a database whose app has an uncaptured file leg, refuse or warn („ennek az alkalmazásnak a fájljai nincsenek ebben a mentésben — a visszaállítás után a fájlok hiányozni fognak"); and never present a DB-only restore of a class-A app as a complete one. Evidence: `audits/evidence-drill-0243-2026-09-16/phase2-f10.txt`. **CLOSED 2026-09-16 — controller v0.244.0, proven live.** A unit restore refuses before anything is touched when the unit cannot return the app's drive-side files, and names the route that can. Fired live on demo-hp: `POST /backup/restore` for paperless-ngx → 302 with „Ez a mentés nem tartalmazza az alkalmazás fájljait, ezért nem állítjuk vissza az adatbázist föléjük — a fájlok így a helyükön maradnak. A fájlok a távoli másolatból állíthatók vissza …", and the app read `running` before AND after, so nothing was stopped and no trash was made unreachable. The database-and-settings-only path exists as a separately worded second step. Red-proof: disabling the guard fails `TestUnitRestore_RefusesWhenTheUnitCannotHoldTheFiles`. **RE-PROVEN 2026-09-16 on a FRESH box, and this time the refusal had somewhere to point:** after five photos were deleted, `POST /backup/restore` was refused with „…a fájlok így a helyükön maradnak. A fájlok a távoli másolatból állíthatók vissza: … „Teljes visszaállítás (fájlok + adatbázis)"", the app read `running` before AND after, and the wastebasket was untouched. The off-site route then returned all five photos — 200 with the exact uploaded sizes and sha256 IDENTICAL to the originals, 5/5, with a negative control. Evidence: `audits/evidence-backup-promise-2026-09-16/phaseE-photos.txt`. | **CLOSED 2026-09-16 — controller v0.244.0 (proven live on demo-hp)** |
@@ -0,0 +1,59 @@
# ISO release 1.29.0 — PUBLISHED 2026-09-18 (R-559, bilingual console)
| | |
|---|---|
| file | `felhom-installer-1.29.0-pve9.2-1.iso` |
| sha256 | `dceacae5da247d76cad065bf6c0d3bbefac8d8a5f8e571db2fd8449a97e94829` |
| size | 1 705 324 544 bytes |
| base | Proxmox VE 9.2-1 (`4e88fe41…c2c6c`) |
| source | felhom.eu `eb1ae37` + the menu-width fix (this commit) |
| published | `https://iso.felhom.eu/felhom-installer-1.29.0-pve9.2-1.iso` |
| rollback | `felhom-installer-1.28.0-pve9.2-1.iso` (sha `a4cd9b6d…`) stays online; rolling back is one edit on the two download pages |
**What changed:** the console is bilingual — the pairing banner, the bound banner and `/etc/issue`
carry the Hungarian block byte-for-byte as before, then an English block inside the same frame. Both
GRUB entries gained an English half.
## The criteria, each with its observed value
| | verdict | observed |
|---|---|---|
| G1 no `answer.toml` | **PASS** | `osirrox -find / -name answer.toml` → 0 hits |
| G2 no root password | **PASS** | no `.rootpw.txt` emitted for 1.29.0; the build says `root-pw: NONE` |
| G5 no credential literal | **PASS** | scanned the shipped script for `root_password`, `ssh-rsa`, `ssh-ed25519`, `PRIVATE KEY`, `password =`, `$6$`, `api_key` → all 0. One `Bearer` hit is `Bearer $token`, a variable |
| G6 boot menu | **PASS** | 2 menuentries, `timeout=15`, `timeout_style=menu` (underscore), `default=0`, banned entries 0 |
| G7 package present | **PASS** | `felhom-bootstrap 1.29.0` in `/proxmox/packages/`, and `dpkg -l` on both installs |
| G9 payload = repo HEAD | **PASS** | `cmp` of `felhom-bootstrap.sh` and `postinst` extracted from the shipped `.deb` → **IDENTICAL**; the installed copy's sha256 `00d4eb55…` equals the repo file's |
| G11 checksum + round trip | **PASS** | downloaded the published bytes: 1 705 324 544 B, sha256 `dceacae5…e94829` = built file = published `.sha256` |
| G14 a person chooses the disk | **PASS** | observed on BOTH entries: graphical `Target Harddisk: /dev/sda (32.00GiB, QEMU HARDDISK)` with a selector; text `Target harddisk: < /dev/sda (QEMU HARDDISK) (32.00 GiB) >` |
| G15 console after first boot AND one reboot | **PASS** | both installs: `grep -c 8006 /etc/issue` → **0**; `Felhom otthoni szerver` → 1; `Felhom home server` → 1; `pvebanner.service` → **masked**. Screendumps after the reboot are identical to first boot |
| G16 Hungarian first, secrets named once per language | **PASS** | harness green on every R-559 golden/English/width/height check; `jelszavad` 0, `Tulajdonosi jelmondat` 1, `Owner passphrase` 1 |
## The proof installs — BOTH menu entries, on the published bytes
Nested VMs on demo-hp, disks on `nvme-scratch` (`/mnt/hdd_1`), destroyed afterwards.
| | entry | VM | result |
|---|---|---|---|
| 1 | `Felhom telepítés / Install Felhom` (graphical) | 323 | installed, first boot + reboot, all checks pass |
| 2 | `Felhom telepítés (szöveges mód) / text` | 325 | installed, first boot + reboot, all checks pass |
Both first boots showed the bilingual `/etc/issue` above the login prompt and the bilingual pairing
banner, with the accented characters rendering correctly (R-63's console font). Screendumps in this
directory; the pairing codes in them are non-secret (a bind also needs the owner passphrase).
## A defect found and fixed BETWEEN builds
The first build's second menu entry read `Felhom telepítés (szöveges mód) / Install Felhom (text
mode)` — 58 characters. **The GRUB menu box cut it at `Instal`**, so its English half was
unreadable on the boot screen. Seen on the screendump, not reasoned about. Shortened to
`… / text` (38 chars) and the image rebuilt; the published image is the rebuilt one. The Hungarian
half is unchanged, which is why the English half was the part that had to give.
## Teardown
VMs 323, 324 and 325 created and **destroyed** (`qm destroy --purge`); `qm list` empty. The two
unclaimed appliance registrations the proof installs made (ids 32, 33) were **discarded** through
the operator endpoint — `status='discarded'`, and zero rows remain in `registered`. demo-hp guest
9201: the same 23 containers before and after, untouched. Two stale `*.rootpw.txt` files from July
were shredded from the publish source directory before the upload (R-587).
Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

@@ -0,0 +1,26 @@
Felhom bare-metal ISO build manifest (R-21 slice A+B+C)
built : 2026-09-18T20:41:45+02:00
iso-version-tag : v1.29.0
pve-version : 9.2-1
source-iso : proxmox-ve_9.2-1.iso
source-iso-sha256 : 4e88fe416df9b527624a175f24c9aa07c714d3332afb1ee3dbf3879573ef2c6c
assistant-version : proxmox-installer-common v9.2.7
profile : none (a release image bakes no disk selection)
fqdn : (none — interactive install)
mode : release (PUBLIC image — NO answer.toml, NO baked credential, interactive disk selection)
loader : shim (stock MS-signed chain; Secure Boot OK on compliant firmware)
grub-mkimage : unknown
boot-menu : FELHOM release menu — TWO INTERACTIVE entries ('Felhom telepítés' graphical = default, 'Felhom telepítés (szöveges mód)' = Terminal UI), timeout 15s
menu-entries : 2 (graphical default + Terminal UI; timeout 15s)
menu-removed : debug variants, Rescue Boot, memtest86+, UEFI Firmware Settings
automated-entry : NOT PRESENT — no auto-installer-mode.toml, so the stock grub.cfg does
not emit it. Disk selection is INTERACTIVE by construction.
host-install-url : https://felhom.eu/scripts/felhom-host-install.sh (default)
secret-bearing : no (PUBLIC image — carries NO credential of any kind)
root-password : NONE — not baked. The installer prompts the person installing (release gate G2).
answer-file : NONE — no answer.toml, no auto-installer-mode.toml (release gate G1)
felhom-package : felhom-bootstrap_1.29.0_all.deb sha256=2c9ff754589d771d8926e68f443e17c7249246f9fca6e43f0050479692f431e8
repo-commit : 8d539f971cf67aae3a69599905ab78b72ca2f889
output : felhom-installer-1.29.0-pve9.2-1.iso
output-sha256 : dceacae5da247d76cad065bf6c0d3bbefac8d8a5f8e571db2fd8449a97e94829
output-size-bytes : 1705324544
@@ -0,0 +1 @@
dceacae5da247d76cad065bf6c0d3bbefac8d8a5f8e571db2fd8449a97e94829 felhom-installer-1.29.0-pve9.2-1.iso
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+11 -5
View File
@@ -40,11 +40,17 @@ this publication**. Operator ruling **1b of 2026-09-17** supersedes that scope (
and the download page ARE in scope"). G16 is rewritten: Hungarian FIRST, pinned by the golden, each
secret named once per language. What it protects is now stronger, not weaker.
**RELEASE STATE: the image is not built and not published.** `ISO_VERSION` is `1.29.0` in the build
script and the source is ready, but publishing to `iso.felhom.eu` is public and irreversible and its
runbook requires a proof install on **both** menu entries plus the 16-criterion gate against the exact
uploaded bytes. That is the operator's step. The download pages therefore still name **1.28.0** — the
image that is actually published — and a new gate refuses them naming different things.
**RELEASE STATE: PUBLISHED 2026-09-18** — `felhom-installer-1.29.0-pve9.2-1.iso`, sha256
`dceacae5da247d76cad065bf6c0d3bbefac8d8a5f8e571db2fd8449a97e94829`, live at `iso.felhom.eu`, with
both download pages updated to it. Every gate criterion recorded with its observed value in
`documentation/tests/iso-release-1.29.0-2026-09-18/`, including proof installs on **both** menu
entries (first boot AND one reboot each) and a verified round trip.
**One defect was caught between builds, by looking at the screen rather than at the config:** the
second menu entry read `Felhom telepítés (szöveges mód) / Install Felhom (text mode)` — 58 characters,
and the GRUB menu box **cut it at `Instal`**. The English half was unreadable on the boot screen. It
was shortened to `… / text`; the Hungarian half is what may not change, so the English half is the
part that gave. The published image is the rebuilt one.
## i18n inventory + wire-contract allowlist entry (2026-09-17, localisation starter)
+1 -1
View File
@@ -72,7 +72,7 @@ menuentry 'Felhom telepítés / Install Felhom' --class felhom --class os {
@@INITRD@@
}
menuentry 'Felhom telepítés (szöveges mód) / Install Felhom (text mode)' --class felhom --class os {
menuentry 'Felhom telepítés (szöveges mód) / text' --class felhom --class os {
echo 'A Felhom telepítése indul szöveges módban...'
@@LINUX_TUI@@
echo 'Rendszerbetöltő betöltése...'
+5 -5
View File
@@ -72,16 +72,16 @@
<div class="container">
<div class="section-header">
<h2>Download</h2>
<p>Felhom installer 1.28.0 (based on Proxmox VE 9.2)</p>
<p>Felhom installer 1.29.0 (based on Proxmox VE 9.2)</p>
</div>
<div class="info-card">
<p><a href="https://iso.felhom.eu/felhom-installer-1.28.0-pve9.2-1.iso"><strong>felhom-installer-1.28.0-pve9.2-1.iso</strong></a> — 1.6 GB (1,705,322,496 bytes)</p>
<p><a href="https://iso.felhom.eu/felhom-installer-1.29.0-pve9.2-1.iso"><strong>felhom-installer-1.29.0-pve9.2-1.iso</strong></a> — 1.6 GB (1,705,324,544 bytes)</p>
<p>Checksum (SHA-256):</p>
<div class="highlight-box">
<p><code>a4cd9b6ddcb55bae3700ab307084d2b699330cc20710688d5816318f04f6d635</code></p>
<p><code>dceacae5da247d76cad065bf6c0d3bbefac8d8a5f8e571db2fd8449a97e94829</code></p>
</div>
<p>The same checksum as a file: <a href="https://iso.felhom.eu/felhom-installer-1.28.0-pve9.2-1.iso.sha256">felhom-installer-1.28.0-pve9.2-1.iso.sha256</a></p>
<p>To check it on Linux or macOS: <code>sha256sum felhom-installer-1.28.0-pve9.2-1.iso</code>. On Windows, in PowerShell: <code>Get-FileHash felhom-installer-1.28.0-pve9.2-1.iso</code>. The string it prints must match the one above. If it does not match, do not use the file — download it again.</p>
<p>The same checksum as a file: <a href="https://iso.felhom.eu/felhom-installer-1.29.0-pve9.2-1.iso.sha256">felhom-installer-1.29.0-pve9.2-1.iso.sha256</a></p>
<p>To check it on Linux or macOS: <code>sha256sum felhom-installer-1.29.0-pve9.2-1.iso</code>. On Windows, in PowerShell: <code>Get-FileHash felhom-installer-1.29.0-pve9.2-1.iso</code>. The string it prints must match the one above. If it does not match, do not use the file — download it again.</p>
<p>The downloaded file has to be written to a USB stick of at least 4 GB (with Balena Etcher or Rufus, for example). The guide covers that too.</p>
</div>
</div>
+5 -5
View File
@@ -78,16 +78,16 @@
<div class="container">
<div class="section-header">
<h2>Letöltés</h2>
<p>Felhom telepítő 1.28.0 (Proxmox VE 9.2 alapon)</p>
<p>Felhom telepítő 1.29.0 (Proxmox VE 9.2 alapon)</p>
</div>
<div class="info-card">
<p><a href="https://iso.felhom.eu/felhom-installer-1.28.0-pve9.2-1.iso"><strong>felhom-installer-1.28.0-pve9.2-1.iso</strong></a> — 1,6 GB (1 705 322 496 bájt)</p>
<p><a href="https://iso.felhom.eu/felhom-installer-1.29.0-pve9.2-1.iso"><strong>felhom-installer-1.29.0-pve9.2-1.iso</strong></a> — 1,6 GB (1 705 324 544 bájt)</p>
<p>Ellenőrző összeg (SHA-256):</p>
<div class="highlight-box">
<p><code>a4cd9b6ddcb55bae3700ab307084d2b699330cc20710688d5816318f04f6d635</code></p>
<p><code>dceacae5da247d76cad065bf6c0d3bbefac8d8a5f8e571db2fd8449a97e94829</code></p>
</div>
<p>Ugyanez letölthető fájlként is: <a href="https://iso.felhom.eu/felhom-installer-1.28.0-pve9.2-1.iso.sha256">felhom-installer-1.28.0-pve9.2-1.iso.sha256</a></p>
<p>Ellenőrzés Linuxon vagy Macen: <code>sha256sum felhom-installer-1.28.0-pve9.2-1.iso</code>. Windowson PowerShellben: <code>Get-FileHash felhom-installer-1.28.0-pve9.2-1.iso</code>. A kiírt számsornak meg kell egyeznie a fentivel. Ha nem egyezik, ne használd a fájlt, és töltsd le újra.</p>
<p>Ugyanez letölthető fájlként is: <a href="https://iso.felhom.eu/felhom-installer-1.29.0-pve9.2-1.iso.sha256">felhom-installer-1.29.0-pve9.2-1.iso.sha256</a></p>
<p>Ellenőrzés Linuxon vagy Macen: <code>sha256sum felhom-installer-1.29.0-pve9.2-1.iso</code>. Windowson PowerShellben: <code>Get-FileHash felhom-installer-1.29.0-pve9.2-1.iso</code>. A kiírt számsornak meg kell egyeznie a fentivel. Ha nem egyezik, ne használd a fájlt, és töltsd le újra.</p>
<p>A letöltött fájlt egy legalább 4 GB-os USB kulcsra kell írni (például a Balena Etcher vagy a Rufus programmal). Az útmutató ezt is leírja.</p>
</div>
</div>