doorstep: console is Felhom's (ISO 1.27.0 source), passphrase hand-over copy (hub 0.113.0 source), rulings
gates / gates (push) Successful in 19s
gates / gates (push) Successful in 19s
Phase 0: the public ISO never auto-installs by construction (no answer.toml, G1); the operator re-affirmed the interactive installer 2026-09-14. - felhom-bootstrap.sh: mask pvebanner.service, write a Hungarian /etc/issue (no :8006 admin URL); pairing banner names the Tulajdonosi jelmondat and paints through the CONSOLE_DEV seam (R-496). Harness: 8 checks, red first; fake hub now sends a pairing code (the banner was never tested, R-502). - hub: created flash + Credentials block tell the operator to hand the phrase over; the self-bind mail names the operator (R-497). Tests red first. - iso-release-gate G14-G16; domain ruling in 01-topology + CONTEXT; R-494 narrowed to P3; R-502..R-504 filed; volunteer guide and day-0 A.2 aligned. ISO_VERSION 1.27.0 (not built, not published). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
+21
@@ -14,6 +14,27 @@
|
||||
> language, one screen, no identifiers in the prose. Same subjects, different readers; merging them
|
||||
> would make one of the two audiences stop reading. `STATUS.md` is also a **view of `OPEN-ITEMS.md`**
|
||||
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
|
||||
## A customer's domain is their own, and the public installer stays interactive (2026-09-14, R-494 / R-495)
|
||||
|
||||
**Two operator rulings, both 2026-09-14.**
|
||||
|
||||
1. **Every customer has their own domain, never a name under `felhom.eu`.** Free Cloudflare certificates
|
||||
do not cover nested names; the domain (a few thousand forints a year) is included in the price. The
|
||||
operator creates the tunnel per day-0 A.1; R-494 is narrowed to "the hub could create it" (P3).
|
||||
Home: `architecture/01-topology-and-trust.md` §networking.
|
||||
2. **The public ISO keeps the interactive Proxmox installer.** Offered an install-time rule ("use the only
|
||||
internal disk, otherwise stop"), the operator kept the 2026-07-31 ruling: a person chooses the disk.
|
||||
Why it was offered and not built: the auto-installer has no local chooser or stop page; a runtime rule
|
||||
needs either an HTTP answer service or surgery inside the Proxmox installer, and USB-drive detection
|
||||
was measured unsafe by udev property (SPIKE-universal-iso-1). Consequence, stated so it is not
|
||||
re-litigated: **the installer's own screens stay English**; the Felhom-written text around them
|
||||
(menu, console, first-boot screens) is Hungarian, and the volunteer guide answers each installer
|
||||
screen. Release gate G14–G16. The spike that would inform a reversal is R-503.
|
||||
|
||||
**Decisions a later session must not re-derive:** the ISO's "never engaged" auto-install is not a
|
||||
defect — the release build has no answer file by construction (G1); `pvebanner.service` rewrites
|
||||
`/etc/issue` on every boot, so a console fix must mask it (ISO v1.27.0).
|
||||
|
||||
## The Update button is a guarded job, and the route back is the restore (2026-09-13, update arc slice 4 — R-448 / R-443 / R-439)
|
||||
|
||||
**Shipped** in controller v0.237.0 (job) + v0.238.0 (page) + v0.238.1 (nightly-leg fix), **proven live**
|
||||
|
||||
@@ -148,6 +148,15 @@ credentials.
|
||||
DNS/routing stay intact through an outage.
|
||||
- **Outbound only** for control/report/backup (poll to hub, push to PBS). No inbound control
|
||||
endpoint exists in the chosen model.
|
||||
- **Every customer has their OWN domain — never a name under `felhom.eu`** (operator ruling
|
||||
2026-09-14). The free Cloudflare tier's certificate covers one level below a zone, so nested names
|
||||
such as `felhom.<customer>.felhom.eu` are not covered; the customer's own domain is a few thousand
|
||||
forints a year and is **included in the customer's price**. The dashboard is `felhom.<customer
|
||||
domain>`, each app `<sub>.<customer domain>`. **The tunnel is created by the operator** per
|
||||
`runbooks/day0-install.md` A.1 today; the hub creating it at customer creation (for a domain already
|
||||
on Cloudflare) is register row R-494, P3, not blocking. Measured reason this was ruled now: the
|
||||
2026-09-14 first-hour drill used a `*.felhom.eu` customer domain with no tunnel, and the dashboard link
|
||||
in the setup-code mail did not resolve.
|
||||
- **Tunnel placement: host** (resolved, Part 3 §3/§5). `cloudflared` runs on the Proxmox host
|
||||
as its own **agent-managed systemd service** — not inside the guest — so the data path
|
||||
survives control-plane death by construction. Geo-restriction WAF is **hub-enforced** (the
|
||||
|
||||
@@ -697,7 +697,7 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
|
||||
| **R-489** | **[P3-LOW] `POST /api/stacks/{name}/remove` reports `volumes_removed: null` over named volumes it DID remove.** MEASURED 2026-09-13 on demo-hp five times (gokapi, actualbudget, adventurelog ×2, glance): `docker compose down --volumes` removed the app's named volumes (`docker volume ls` count 2 → 0) and the response carried `"volumes_removed":null`. The customer's confirmation dialog therefore cannot say what it deleted. Split out of R-474 (closed in v0.240.0 for the backups half). **Fix shape:** list the volumes before `down --volumes`, diff after, and report the difference (`[]` when none, never `null`). **PARTLY SHIPPED in v0.242.0 (`d698ce3`), measured live on 9202 the same night:** the difference is computed and a fresh compose-created volume IS reported (`["opengist_opengist_data"]`), but the listing filters on the compose project LABEL and a volume recreated by a unit restore (`docker volume create <name>`, `restore.go:154`) carries no labels — compose still removes it and the response says `[]` (`audits/v0242-2026-09-14/19-R489-cause.txt`). **Remaining fix:** list by the `<project>_` name prefix as well (union), or label the recreated volume as compose would. | **READY — rank P3-LOW; owner: CC (residual)** |
|
||||
| **R-492** | **[P3-LOW] `cfg.Paths.HDDPath` is empty on every box and still has readers; delete it.** R-465 audited its six readers and found every one falling back; R-490 (v0.242.0) gave the last one, `systemInfo`, the same fallback. The global now carries no information on any box and its deletion was deferred twice. **Fix shape:** remove the field, its env binding and the readers' fallback branches; a build proves nothing reads it. Next controller release. | **READY — rank P3-LOW; owner: CC** |
|
||||
| **R-493** | **[P1-HIGH] There are NO customer-facing install instructions, so a volunteer cannot begin — this blocks inviting anyone.** MEASURED 2026-09-14 (drill `audits/DRILL-fresh-install-0242-2026-09-14.md`, Phase 0.2), each source with what was searched: the website's 9 pages carry no mention of the installer, of `iso.felhom.eu`, or of any install step (`iso`, `letolt`, `telepit`); `https://iso.felhom.eu/` and `/index.html` both return **404** — only the exact object name `felhom-installer-1.26.1-pve9.2-1.iso` answers, so the file cannot be found without being told its name; `RUNBOOK-onboarding-draft-v4.md` is an operator-attended script; the R-11 tester one-pager was ruled 2026-07-21 and never written; no `email.md` exists in the workspace; the two hub mails a new customer receives (`Kösd össze a Felhom dobozodat`, `Elindult a Felhom szervered — beállító kód`) assume the box is already installed. **Stopgap written by the drill:** `documentation/runbooks/VOLUNTEER-first-hour.md` (Hungarian, the steps the product really needs, each addition over today listed at its top). **What it needs:** the operator to choose the channel and approve the text. | **WAITING-ON-OPERATOR — rank P1-HIGH; owner: operator (channel + text), CC (draft)** |
|
||||
| **R-494** | **[P1-HIGH] A new customer's dashboard has NO reachable address unless the operator hand-makes a Cloudflare tunnel — the link in the setup-code mail is dead.** MEASURED 2026-09-14 on a fresh install from the public ISO (drill intervention **I1**): the claim mail points at `https://felhom.drill0242.felhom.eu`; that name has **no A and no AAAA** record (`dig @1.1.1.1`, control `felhom.enkisfelhom.hu` resolves); the hub has **no tunnel- or DNS-creation code** (`hub/internal/cloudflare/` holds only geo-rule removal; `cf_tunnel_token` is a pasted, optional form field, `configs.go:1478`) — day-0 runbook A.1 makes it a manual Cloudflare-dashboard step that nothing on the customer-create page asks for; the box's own split-horizon resolver on the appliance LAN IP answered `google.com` but not the dashboard name at 13:27:39Z; the agent applied the record at **13:27:44Z** (`lanresolver: applied split-horizon record … ip=192.168.0.158`, 3 m 46 s after the controller started), so the box CAN answer the name — **but only to a device that uses the box as its DNS server, and no document, screen or mail tells a household to do that**; the router and the installer-offered DNS answer nothing. The page was reachable only at the guest's LAN address with the name forced (`curl --resolve …:443:192.168.0.158`). **A volunteer could not have done that.** **What it needs:** an operator ruling — the hub creates the tunnel and DNS at customer creation, or the product gives a household a LAN address that works with no DNS change. | **WAITING-ON-OPERATOR — rank P1-HIGH; owner: operator (ruling), CC (build)** |
|
||||
| **R-494** | **NARROWED 2026-09-14 by operator ruling → [P3-LOW] the hub COULD create the tunnel at customer creation, for a domain already on Cloudflare. Not blocking: every customer has their own domain and the operator creates the tunnel per day-0 A.1 (`architecture/01-topology-and-trust.md`).** *Original finding, kept:* **[P1-HIGH] A new customer's dashboard has NO reachable address unless the operator hand-makes a Cloudflare tunnel — the link in the setup-code mail is dead.** MEASURED 2026-09-14 on a fresh install from the public ISO (drill intervention **I1**): the claim mail points at `https://felhom.drill0242.felhom.eu`; that name has **no A and no AAAA** record (`dig @1.1.1.1`, control `felhom.enkisfelhom.hu` resolves); the hub has **no tunnel- or DNS-creation code** (`hub/internal/cloudflare/` holds only geo-rule removal; `cf_tunnel_token` is a pasted, optional form field, `configs.go:1478`) — day-0 runbook A.1 makes it a manual Cloudflare-dashboard step that nothing on the customer-create page asks for; the box's own split-horizon resolver on the appliance LAN IP answered `google.com` but not the dashboard name at 13:27:39Z; the agent applied the record at **13:27:44Z** (`lanresolver: applied split-horizon record … ip=192.168.0.158`, 3 m 46 s after the controller started), so the box CAN answer the name — **but only to a device that uses the box as its DNS server, and no document, screen or mail tells a household to do that**; the router and the installer-offered DNS answer nothing. The page was reachable only at the guest's LAN address with the name forced (`curl --resolve …:443:192.168.0.158`). **A volunteer could not have done that.** **What it needs:** an operator ruling — the hub creates the tunnel and DNS at customer creation, or the product gives a household a LAN address that works with no DNS change. | **READY — rank P3-LOW; owner: CC** |
|
||||
| **R-495** | **[P2-MEDIUM] The public installer asks a stranger four questions nothing answers, and REFUSES its own default on one of them.** MEASURED 2026-09-14 on `felhom-installer-1.26.1` (drill screens `s04`–`s22`): every screen after the GRUB menu is **English** Proxmox (EULA, disk, locale, password, network, summary); **three disks are offered with no guidance which is the system disk** (the default happened to be right); the administrator e-mail is prefilled `mail@example.invalid`; the hostname is prefilled `pve.example.invalid` and pressing Next on it returns **„Invalid values: hostname does not look valid”** — a volunteer who accepts the defaults cannot continue; at the end, with the stick still in, the default-ticked auto-reboot boots back into the installer. None of this is wrong for Proxmox; all of it is unanswered for a Felhom household. **Fix shape (cheap first):** the volunteer instructions answer each question (done in `runbooks/VOLUNTEER-first-hour.md`); later, prefill hostname and e-mail from the ISO build. | **READY — rank P2-MEDIUM; owner: CC (ISO prefill), operator (instruction text via R-493)** |
|
||||
| **R-496** | **[P2-MEDIUM] The box's console tells a stranger, in English and FIRST, to open the Proxmox admin page — and calls the owner passphrase „a jelszavadat”.** MEASURED 2026-09-14 (drill screen `s29`): above the Hungarian pairing banner the console prints Proxmox's own `Welcome to the Proxmox Virtual Environment. Please use your web browser to configure this server - connect to: https://192.168.0.134:8006/` — the operator admin UI, which a household must never be sent to; the Felhom banner below says „add meg ezt a kódot és **a jelszavadat**”, while the self-bind mail and page name the same secret **„Tulajdonosi jelmondat”** (R-323 renamed the mail; `scripts/iso/felhom-bootstrap.sh:71-72` was not renamed). **Fix shape:** replace the Proxmox `/etc/issue` block on appliance installs; rename the console word. Both live in `felhom-bootstrap.sh`, a frozen ISO payload (G9), so it ships with the next ISO. | **READY — rank P2-MEDIUM; owner: CC** |
|
||||
| **R-497** | **[P2-MEDIUM] No product channel ever gives the customer the „Tulajdonosi jelmondat”, yet the self-bind mail says they received it at setup.** MEASURED 2026-09-14: the 5-word phrase is minted at customer creation (`hub/internal/web/configs.go`, `RandomPassphrase(5)`) and shown **only on the operator's customer page**; the self-bind mail (`FormatSelfBindEmail`) says „amelyet a beállításkor kaptál” and the console asks for it; nothing sends it. Day-0 runbook A.2 says the operator must dictate or hand it over — a step that exists only in an operator document. A volunteer whose operator forgets cannot bind the box and is told they already have the phrase. **Fix shape:** the operator's customer-create confirmation says, in one line, to hand this phrase to the customer now; the volunteer instructions say where it comes from (done in `VOLUNTEER-first-hour.md`). | **READY — rank P2-MEDIUM; owner: operator (process), CC (hub copy)** |
|
||||
@@ -705,6 +705,9 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
|
||||
| **R-499** | **[P2-MEDIUM] Every app without a data drive is told its data is „already in the full system backup (PBS)" and there is „nothing to do" — on a box with no PBS, whose only whole-box copy sits on the same disk.** MEASURED 2026-09-14 on a fresh 0.242.0 box with the DR tier off (drill step 7): `GET /stacks/bookstack/backup` renders „Ennek az alkalmazásnak az adatai a belső rendszerlemezen vannak, amelyek **már szerepelnek a teljes rendszermentésben (PBS)** … ehhez az alkalmazáshoz nincs külön teendő." The sentence sits under `{{if not .IsHDDApp}}` in `controller/internal/web/templates/tier2_config.html:20-26` and consults nothing about where the whole-guest backup goes. On the same box `/backups` says, correctly, „Helyi tároló (local)" and „A rendszermentés jelenleg ugyanazon a lemezen van, mint a rendszer — így hibás fájlok ellen véd, lemezhiba ellen nem." **Two pages of one product contradict each other, and the reassuring one is the false one.** **Fix shape:** branch the sentence on the box's actual whole-guest target (PBS vs local, same-disk flag the overview already computes); a render test per branch. | **READY — rank P2-MEDIUM; owner: CC** |
|
||||
| **R-500** | **[P3-LOW] The dashboard shows the last backup in UTC while every backup page shows it in local time — two different clock times for one backup.** MEASURED 2026-09-14 on a fresh 0.242.0 box (drill step 12): the same backup (`last_run 2026-09-14T13:41:37Z`) reads „Utolsó mentés: **2026-09-14 13:41**" on `/dashboard` and „Utolsó adatbázis mentés: **2026-09-14 15:41** (most)" on `/backups/apps`. Cause, from source: `controller/internal/web/templates/dashboard.html:154` renders `{{.BackupStatus.LastRun.Format "2006-01-02 15:04"}}` with no conversion to the box's zone, while the backup pages go through the zone-aware helpers in `funcmap.go`. A household comparing the two screens sees a backup two hours apart from itself. **Fix shape:** format through the same zone-aware helper; a render test that pins a non-UTC zone and asserts the local hour. | **READY — rank P3-LOW; owner: CC** |
|
||||
| **R-501** | **[P3-LOW] The documented "confirm your CI run" recipe reads only the LAST page of the jobs list, and that list is not in id order — so it can report a run as missing that exists and passed.** MEASURED 2026-09-14 for felhom.eu commit `38848ff`: `actions/jobs?limit=1` → `total_count 335`; the recipe's page `T/50+1` = page 7 held ids 473…570 and **no match for 8 minutes**; a scan of all seven pages found the job on **page 6** (ids 380…581): `job id=581 name=gates status=completed conclusion=success completed_at 2026-09-14T14:18:23Z`. Pages are not sorted (page 1 ids 1…273, page 2 84…208). `actions/tasks` listed the same run first (`id 582, run_number 335, success`). **Fix shape:** the recipe in `felhom.eu/CLAUDE.md` (end-of-session checklist) scans every page and matches `head_sha`; say so in the same sentence that warns about the id offset (R-417). | **READY — rank P3-LOW; owner: CC** |
|
||||
| **R-502** | **[P3-LOW] The bootstrap regression harness is run by NO gate and NO CI — and it had never exercised the pairing banner.** MEASURED 2026-09-14: `grep -rn bootstrap-modes scripts/*.py .gitea/workflows` → nothing; the harness is run by hand in `felhom-iso-assistant:trixie`. While adding the R-496 checks, its fake hub's `register` reply turned out to carry no `pairing_code`, so `print_pairing_banner` returned early in every run: the banner that a household reads first had no test at all until ISO v1.27.0. **Fix shape:** register the harness in `repo_gates.py` behind a container-available check that reports INCONCLUSIVE (never skip-as-pass) when docker is absent, with a decoy. | **READY — rank P3-LOW; owner: CC** |
|
||||
| **R-503** | **[P3-LOW] SPIKE (not built): an install-time disk rule for the public ISO — "exactly one internal disk → install; otherwise stop in Hungarian".** Offered to the operator 2026-09-14 and **not chosen**: the 2026-07-31 ruling (a person chooses the disk) stands. Recorded so a reversal starts from measurements, not from the offer. **What must be measured first:** (1) whether the Proxmox auto-installer's HTTP answer mode can serve a per-machine answer from posted system info without network being a precondition a volunteer can miss; (2) whether USB transport is reliably visible in sysfs (`/sys/block/*/device` path, `removable`) where udev properties were measured blind (SPIKE-universal-iso-1 §3.2); (3) whether any refusal can be shown in Hungarian without modifying the Proxmox installer squashfs. **Reverses two rulings if built — needs an operator word.** | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (ruling), CC (spike)** |
|
||||
| **R-504** | **[P3-LOW] `iso.felhom.eu` cannot show an index page on its own — its root returns 404, and the download page lives on the website instead.** MEASURED 2026-09-14: `https://iso.felhom.eu/` and `/index.html` → 404; only named objects answer. The host is an R2 bucket behind a custom domain; whether R2 would serve an uploaded `index.html` at `/` was **not measured** (uploading anything to the public bucket is a publication). The ISO v1.27.0 task puts the Hungarian download page at `felhom.eu/letoltes` (published with the ISO, after the operator's yes). **Remaining:** a redirect from `iso.felhom.eu/` to that page needs a Cloudflare rule the session has no credential for. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (Cloudflare rule)** |
|
||||
|
||||
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
|
||||
One row per dated check. The R-number must have a row above. Dates are UTC.
|
||||
|
||||
@@ -8,18 +8,18 @@
|
||||
> **Every step this document adds beyond what exists today** (none of these is written anywhere a
|
||||
> volunteer can see):
|
||||
>
|
||||
> 1. **Where the installer is, by exact file name** — `iso.felhom.eu` has no index page (R-493).
|
||||
> 1. **Where the installer is** — the Hungarian download page `felhom.eu/letoltes` (published with ISO
|
||||
> v1.27.0 after the operator's yes; `iso.felhom.eu/` itself still has no index — R-504).
|
||||
> 2. **That the installer screens are English Proxmox screens**, and what to type on each.
|
||||
> 3. **A hostname to type** — the installer refuses its own default `pve.example.invalid`.
|
||||
> 4. **An e-mail to type** — the installer prefills `mail@example.invalid`.
|
||||
> 5. **To remove the USB stick** at „Installation finished — reboot now?".
|
||||
> 6. **To ignore the English „Please use your web browser … https://…:8006/" block** on the console —
|
||||
> that is the Proxmox admin page, not Felhom.
|
||||
> 7. **That „a jelszavadat" on the console means the 5-word „Tulajdonosi jelmondat"**, and that it
|
||||
> comes from the operator, not by mail.
|
||||
> 8. **How to reach the dashboard** — **UNRESOLVED**: the setup-code mail's link does not resolve for
|
||||
> a new customer unless the operator made a Cloudflare tunnel by hand (R-494). Step 5 below says
|
||||
> what the operator must do before sending this document.
|
||||
> 6. ~~To ignore the English Proxmox console block~~ — **removed in ISO v1.27.0** (R-496): the console
|
||||
> shows Felhom's Hungarian text.
|
||||
> 7. ~~That „a jelszavadat" means the Tulajdonosi jelmondat~~ — **removed in ISO v1.27.0 / hub v0.113.0**
|
||||
> (R-496, R-497): console, mail and page use one name; the mail says the operator hands it over.
|
||||
> 8. **How to reach the dashboard** — **ruled 2026-09-14**: every customer has their own domain and the
|
||||
> operator creates the tunnel (day-0 A.1) before sending this document. R-494 is narrowed to P3.
|
||||
>
|
||||
> Operator prerequisites this document silently depends on (§ „Az üzemeltető előtte"):
|
||||
> create the customer; **hand over the Tulajdonosi jelmondat out of band**; **create the Cloudflare
|
||||
@@ -45,12 +45,8 @@
|
||||
|
||||
## 1. A telepítő letöltése (~1 perc)
|
||||
|
||||
Töltsd le ezt a fájlt (kb. 1,7 GB):
|
||||
|
||||
`https://iso.felhom.eu/felhom-installer-1.26.1-pve9.2-1.iso`
|
||||
|
||||
Ellenőrző összeg (SHA-256), ha szeretnéd ellenőrizni:
|
||||
`f3cc86d5f0ec68bba4155c994b4fa84e208d50209bb6e815636c99e5441059a6`
|
||||
Nyisd meg a **https://felhom.eu/letoltes** oldalt, és töltsd le az ott megadott telepítőt
|
||||
(kb. 1,7 GB). Az oldal a fájl ellenőrző összegét (SHA-256) is megadja.
|
||||
|
||||
Írd ki USB-kulcsra (Windows: Rufus, **„DD" módban**; Mac/Linux: balenaEtcher).
|
||||
|
||||
@@ -62,7 +58,7 @@ Indítsd a gépet az USB-kulcsról. **A telepítő képernyői angolul vannak**
|
||||
|---|---|
|
||||
| Felhom logó, két sor | Válaszd a **„Felhom telepítés"** sort (vagy várj, magától elindul) |
|
||||
| END USER LICENSE AGREEMENT | **I agree** |
|
||||
| Target harddisk | Válaszd azt a lemezt, **amelyre a rendszer kerüljön**. Ha több lemez van, a legnagyobb nem feltétlenül a jó — a külső adatlemezeket ne válaszd. |
|
||||
| Target harddisk | **A telepítő soha nem választ helyetted.** Felsorolja a gép lemezeit méretükkel és típusukkal; válaszd azt, **amelyre a rendszer kerüljön — ez a lemez törlődik.** A külső mentőlemezt a telepítés idejére húzd ki. Ha több belső lemez van és nem tudod, melyik a jó, állj meg és szólj az üzemeltetőnek. |
|
||||
| Country / Timezone / Keyboard | Hagyd így: Hungary, Europe/Budapest, Hungarian |
|
||||
| Root password | Adj meg egy legalább 8 karakteres jelszót kétszer. **Nem kell megjegyezned** — a Felhom az első indulás után lecseréli. |
|
||||
| Administrator email | Írd be a saját e-mail címedet (a `mail@example.invalid` helyett) |
|
||||
@@ -73,10 +69,8 @@ Indítsd a gépet az USB-kulcsról. **A telepítő képernyői angolul vannak**
|
||||
|
||||
## 3. Az első indulás (~2 perc)
|
||||
|
||||
A képernyőn **először egy angol szöveg** jelenik meg („Welcome to the Proxmox Virtual Environment…
|
||||
connect to: https://…:8006/"). **Ezzel nincs teendőd — ne nyisd meg.**
|
||||
|
||||
Alatta megjelenik a Felhom:
|
||||
A képernyőn ez jelenik meg: „Felhom otthoni szerver — Ezen a képernyőn nincs teendőd, bejelentkezni
|
||||
sem kell." Alatta, kb. 2 perc múlva:
|
||||
|
||||
> Felhom — a doboz készen áll, és a párosításra vár.
|
||||
> Párosító kód: XXX-XXX
|
||||
@@ -89,7 +83,7 @@ Alatta megjelenik a Felhom:
|
||||
hivatkozásra (7 napig érvényes).
|
||||
2. Add meg:
|
||||
- a **Párosító kódot** a doboz képernyőjéről;
|
||||
- a **Tulajdonosi jelmondatot** (5 szó). A doboz képernyője ezt „jelszónak" hívja — ugyanaz.
|
||||
- a **Tulajdonosi jelmondatot** (5 szó), amelyet a Felhom üzemeltetőjétől kaptál.
|
||||
3. Hagyd bekapcsolva a dobozt. **Kb. 3 perc** múlva megérkezik a következő e-mail.
|
||||
|
||||
*(Megjegyzés: a doboz képernyője az összekötés után is a párosító kódot mutatja — ez ismert hiba,
|
||||
@@ -99,7 +93,7 @@ nem kell újra összekötni.)*
|
||||
|
||||
1. Nyisd meg a **„[Felhom] Elindult a Felhom szervered — beállító kód"** tárgyú e-mailt.
|
||||
2. Kattints a benne lévő címre (`https://felhom.<a-te-domained>`).
|
||||
**⚠ Ha a cím nem nyílik meg, szólj az üzemeltetőnek** — ez most ismert akadály (R-494).
|
||||
Ha a cím nem nyílik meg, szólj az üzemeltetőnek.
|
||||
3. „A szerver beállítása" oldalon add meg a **Beállító kódot** (3 szó, pl. `szó-szó-szó`), és válassz
|
||||
**legalább 12 karakteres** jelszót. Ez lesz a vezérlőpult jelszava.
|
||||
4. Ha nem jött meg a kód: **„Új kód kérése"** — mindig ugyanarra az e-mail címre érkezik.
|
||||
|
||||
@@ -69,7 +69,10 @@ Hub UI (`https://hub.felhom.eu`, operator password) → **Customers → New**:
|
||||
On save the hub generates two credentials:
|
||||
|
||||
- **Retrieval passphrase** (5 Hungarian words) — the ONE secret the installer carries to the box.
|
||||
Dictate or hand it to whoever runs Part C. Treat it like a password.
|
||||
The customer knows it as **„Tulajdonosi jelmondat”**. **Hand this phrase to the customer now** — in
|
||||
person or by phone; it is never e-mailed, and the self-bind page asks for it. (The hub's
|
||||
customer-created flash and Credentials block say the same sentence since hub v0.113.0, R-497; the
|
||||
self-bind mail tells the customer they received it from the Felhom operator.) Treat it like a password.
|
||||
- **Customer API key** — internal (baked into the generated controller.yaml); never handled manually.
|
||||
|
||||
### A.3 Verify the Day-0 artifact manifest
|
||||
|
||||
@@ -244,6 +244,53 @@ it. The installed box registered at the hub, failed to persist the token, and th
|
||||
Any future criterion of the form "the correct file is present" should be paired with one of the form
|
||||
"and everything it needs at run time is too". `build-deb.sh` asserts this itself and is red-proofed.
|
||||
|
||||
### G14 — a PERSON chooses the disk; the image never guesses (operator rulings 2026-07-31, re-affirmed 2026-09-14)
|
||||
|
||||
```bash
|
||||
osirrox -indev "$ISO" -find / -maxdepth 1 2>/dev/null | grep -cE "'/answer\.toml'|'/auto-installer-mode\.toml'" # 0 (= G1)
|
||||
grep -c 'filter\.' <every pipeline-added text file> # 0
|
||||
```
|
||||
**PASS =** both `0`, **and** the proof install shows the installer's own disk screen and `Bootdisk(s)`
|
||||
summary before erasing, **and** `runbooks/VOLUNTEER-first-hour.md` tells a volunteer, in Hungarian, how
|
||||
to recognise the system disk on that screen.
|
||||
|
||||
*The rule, in one paragraph a volunteer could read:* **the installer never picks a disk for you.** It
|
||||
lists every disk it sees, with its size and model; you choose the one the system goes on, and that
|
||||
disk is erased. If the machine has more than one disk, the guide tells you how to tell them apart
|
||||
before you press Next — and the external backup drive should not be plugged in during the install.
|
||||
|
||||
*Why:* SPIKE-universal-iso-1 §3.2–3.3 measured that no udev property separates an internal disk from a
|
||||
USB-caddy backup drive and that a filter matching two disks silently wipes one. On 2026-09-14 the
|
||||
operator was offered an install-time "use the only disk" rule and chose to keep the person as the
|
||||
safety mechanism. Changing this criterion reverses two rulings; the spike that would inform it is its
|
||||
own register row.
|
||||
|
||||
### G15 — the console is Felhom's after first boot, with no admin URL
|
||||
|
||||
On the proof install, after the first boot and after **one reboot**:
|
||||
```bash
|
||||
grep -c 8006 /etc/issue # 0
|
||||
grep -c 'Felhom otthoni szerver' /etc/issue # 1
|
||||
systemctl is-enabled pvebanner.service # masked
|
||||
```
|
||||
**PASS =** all three, **and** a console screendump shows no `https://…:8006/` line. *Why:* Proxmox's
|
||||
`pvebanner.service` rewrites `/etc/issue` on every boot; the 2026-09-14 drill (screen s29) found that
|
||||
text — the operator admin UI, in English — was the first thing a household read. The reboot is part of
|
||||
the criterion because an overwrite without the mask passes once and fails on the next boot.
|
||||
|
||||
### G16 — every Felhom-authored string on the volunteer's path is Hungarian, and names each secret once
|
||||
|
||||
```bash
|
||||
grep -n "printf ' " <extracted felhom-bootstrap.sh> | grep -viE 'á|é|í|ó|ö|ő|ú|ü|ű|Felhom|%s' # review every hit
|
||||
grep -c 'jelszavad' <extracted felhom-bootstrap.sh> # 0
|
||||
grep -c 'Tulajdonosi jelmondat' <extracted felhom-bootstrap.sh> # >= 1
|
||||
```
|
||||
**PASS =** no English sentence in the review list, `jelszavad` absent, the R-323 name present. Search
|
||||
with ASCII fragments where possible and keep one positive and one negative control in the record.
|
||||
*Scope, stated so it is not read wider:* the **Proxmox installer's own screens are English** and stay so
|
||||
under the 2026-07-31 ruling; G14 requires the guide to answer each of them. This criterion covers what
|
||||
Felhom writes: the GRUB menu, the console banner and `/etc/issue`, the first-boot screens.
|
||||
|
||||
---
|
||||
|
||||
## Result recording
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
## v0.113.0 — the operator is told to hand over the passphrase, and the mail says who hands it (2026-09-14, R-497)
|
||||
|
||||
**What was wrong, measured in the 2026-09-14 first-hour drill.** The five-word „Tulajdonosi jelmondat" is
|
||||
minted at customer creation and shown only on the operator's customer page — nothing delivers it, and
|
||||
nothing told the operator to. The self-bind mail then told the customer they had received it
|
||||
„a beállításkor", which was true only if the operator had remembered.
|
||||
|
||||
**What changed — copy only, no acceptance logic.**
|
||||
- The customer-created flash: *„Hand this phrase to the customer now — … Give it in person or by phone;
|
||||
it is never e-mailed, and the self-bind page asks for it."*
|
||||
- The Credentials block repeats the same sentence beside the Retrieval Password.
|
||||
- The self-bind mail's item 2 now reads: *„A tulajdonosi jelmondatodat (az 5 szóból álló kifejezést),
|
||||
amelyet a Felhom üzemeltetőjétől kaptál — személyesen vagy telefonon, e-mailben soha."*
|
||||
- **The passphrase is still never e-mailed** — the operator has not ruled that e-mail may carry it.
|
||||
|
||||
**Tests (red first):** `passphrase_handover_test.go` — the created flash and the page carry the
|
||||
hand-over sentence (failed before), the mail names the operator and no longer says „beállításkor"
|
||||
(failed before), and the mail never carries a passphrase word (a guard, green before and after).
|
||||
|
||||
## v0.112.0 — the floor carries a release without a golden (2026-09-13, R-472)
|
||||
|
||||
**Operator ruling 2026-09-13.** Goldens are baked weekly (R-468), but the hub held every floor above
|
||||
|
||||
@@ -347,7 +347,8 @@ A hivatkozás megnyitása után két adatot kell megadnod:
|
||||
|
||||
1. A párosító kódot, amely a doboz képernyőjén (a monitoron) látható.
|
||||
2. A tulajdonosi jelmondatodat (az 5 szóból álló kifejezést), amelyet a
|
||||
beállításkor kaptál. Ez igazolja, hogy a fiók a tiéd.
|
||||
Felhom üzemeltetőjétől kaptál — személyesen vagy telefonon, e-mailben
|
||||
soha. Ez igazolja, hogy a fiók a tiéd.
|
||||
|
||||
A hivatkozás 7 napig érvényes. Biztonsági okból 5 sikertelen próbálkozás után
|
||||
zárolódik — ilyenkor vedd fel a kapcsolatot az ügyfélszolgálattal.
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package web
|
||||
|
||||
// R-497 — the „Tulajdonosi jelmondat" is delivered by NOTHING in the product: it is minted at customer
|
||||
// creation, shown only on the operator's customer page, and never e-mailed (the operator has not ruled
|
||||
// that e-mail may carry it). Measured 2026-09-14 in the first-hour drill: the self-bind mail told the
|
||||
// customer they had received it „a beállításkor" — true only if the operator remembered to hand it over,
|
||||
// and nothing told the operator to.
|
||||
//
|
||||
// These pin the CONSEQUENCE, not a string for its own sake: the operator is told at the moment the
|
||||
// phrase exists, the page repeats it where the phrase is shown, and the customer's mail names the person
|
||||
// they got it from. The last test pins what must NOT change — the mail still carries no phrase.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/notify"
|
||||
)
|
||||
|
||||
// handoverMarker is the operator-facing instruction. One sentence, rendered in two places.
|
||||
const handoverMarker = "Hand this phrase to the customer now"
|
||||
|
||||
func TestCustomerPage_CreatedFlashTellsTheOperatorToHandOverThePhrase(t *testing.T) {
|
||||
s, st := newTestServer(t)
|
||||
seedCustomer(t, st, "acme", "")
|
||||
|
||||
created := renderCustomerPageWithQuery(t, s, "acme", "?flash=created")
|
||||
if !strings.Contains(created, handoverMarker) {
|
||||
t.Errorf("the customer-created flash does not tell the operator to hand over the phrase:\n%s", created)
|
||||
}
|
||||
if !strings.Contains(created, "never e-mailed") {
|
||||
t.Error("the created flash must say the phrase is never e-mailed — otherwise the operator waits for a mail")
|
||||
}
|
||||
|
||||
// Nominal page (no flash): the instruction stays beside the phrase, where the operator reveals it.
|
||||
plain := renderCustomerPage(t, s, "acme")
|
||||
if !strings.Contains(plain, handoverMarker) {
|
||||
t.Error("the Credentials block does not repeat the hand-over sentence beside the phrase")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfBindEmail_SaysWhoHandedOverThePhrase(t *testing.T) {
|
||||
_, body := notify.FormatSelfBindEmail("acme", "https://hub.example/bind/tok")
|
||||
|
||||
if strings.Contains(body, "beállításkor kaptál") {
|
||||
t.Error("the mail still says the customer received the phrase „a beállításkor” — nothing delivers it then")
|
||||
}
|
||||
if !strings.Contains(body, "üzemeltetőjétől") {
|
||||
t.Errorf("the mail must name who hands over the phrase (the Felhom operator):\n%s", body)
|
||||
}
|
||||
// Unchanged contract: one name, and what the thing is.
|
||||
if !strings.Contains(body, "tulajdonosi jelmondatodat") || !strings.Contains(body, "5 szóból álló kifejezést") {
|
||||
t.Error("the mail lost the secret's name or its description")
|
||||
}
|
||||
}
|
||||
|
||||
// The phrase itself must never ride the mail. A customer with a seeded phrase gets a mail that does not
|
||||
// contain it — the builder takes no phrase argument today; this pins that no future edit adds one via
|
||||
// the link text or a helper.
|
||||
func TestSelfBindEmail_NeverCarriesThePhrase(t *testing.T) {
|
||||
const phrase = "alma korte szilva barack meggy"
|
||||
_, body := notify.FormatSelfBindEmail("acme", "https://hub.example/bind/tok")
|
||||
for _, w := range strings.Fields(phrase) {
|
||||
if strings.Contains(body, w) {
|
||||
t.Errorf("the self-bind mail contains a passphrase word %q", w)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
{{if .Flash}}
|
||||
<div class="flash flash-success">
|
||||
{{if eq .Flash "created"}}Configuration created successfully.
|
||||
{{if eq .Flash "created"}}Configuration created successfully. <strong>Hand this phrase to the customer now</strong> — the Retrieval Password below is the customer's „Tulajdonosi jelmondat”. Give it in person or by phone; it is never e-mailed, and the self-bind page asks for it.
|
||||
{{else if eq .Flash "updated"}}Configuration updated.
|
||||
{{else if eq .Flash "password_regenerated"}}Retrieval password regenerated.
|
||||
{{else if eq .Flash "offsite_reissued"}}Offsite credentials re-issued — a fresh one-time password is staged; the controller picks it up on its next config refresh.
|
||||
@@ -464,6 +464,7 @@
|
||||
<button type="button" class="copy-btn" onclick="copySecret('retrieval-pw')" title="Copy">⎘</button>
|
||||
</div>
|
||||
<span class="form-hint">The per-customer secret that fetches the whole config — masked by default; never place it on a command line (the installer reads it at a no-echo prompt).</span>
|
||||
<span class="form-hint"><strong>Hand this phrase to the customer now</strong> — in person or by phone. The customer knows it as „Tulajdonosi jelmondat”; it is never e-mailed, and the self-bind page asks for it (R-497).</span>
|
||||
</div>
|
||||
<form method="POST" action="/configs/{{.CustomerID}}/regen-password" style="margin-top: 0.5rem;">
|
||||
{{.CSRFField}}
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
## ISO v1.27.0 — the console is Felhom's, and names the passphrase once (2026-09-14, R-496)
|
||||
|
||||
**Measured in the 2026-09-14 first-hour drill (screen s29):** the first text a household read on a fresh
|
||||
box was Proxmox's `Welcome to the Proxmox Virtual Environment … connect to https://<ip>:8006/` — the
|
||||
operator admin UI, in English — and the Felhom banner below it asked for „a jelszavadat", while the
|
||||
self-bind mail and page call the same secret „Tulajdonosi jelmondat".
|
||||
|
||||
**`felhom-bootstrap.sh` (the frozen ISO payload):**
|
||||
- `install_felhom_issue` runs first, before the network gate: masks `pvebanner.service` (it rewrites
|
||||
`/etc/issue` on every boot — overwriting alone would last one boot), writes a Hungarian Felhom text
|
||||
to `/etc/issue` with no admin URL, reloads agetty. Every step best-effort; never blocks the boot.
|
||||
- The pairing banner asks for „a Tulajdonosi jelmondatodat (az 5 szót a Felhom üzemeltetőjétől
|
||||
kaptad)", and paints through the `CONSOLE_DEV` seam instead of a hard-coded `/dev/console`.
|
||||
|
||||
**Harness (`scripts/iso/test/bootstrap-modes.sh`):** five R-496 checks, **red before the change**. Found
|
||||
on the way: the fake hub's register reply carried no `pairing_code`, so the pairing banner had **never
|
||||
been exercised by any test**; it now carries one. Still not run by any gate or CI (recorded as a row).
|
||||
|
||||
**Not changed, by operator ruling 2026-09-14:** the public ISO keeps the interactive Proxmox installer
|
||||
(no answer file). The 2026-07-31 ruling — disk selection is always a person's choice — stands.
|
||||
|
||||
## observations_gate.py reads EVERY observations section (2026-09-13, R-471)
|
||||
|
||||
`observation_items` returned the first `Observations` heading it found and stopped, so a report
|
||||
|
||||
@@ -48,7 +48,7 @@ set -euo pipefail
|
||||
# does not exist: the ISO is a frozen artifact, while felhom-host-install.sh is fetched at RUN TIME
|
||||
# from the website's git-sync of `main` (R-94/R-110), so whatever version an ISO carries, the script a
|
||||
# box runs is always current. Coupling them would invent a constraint. The claim is corrected instead.
|
||||
ISO_VERSION="1.26.1" # the ISO's own version. INDEPENDENT of felhom-host-install.sh's SCRIPT_VERSION,
|
||||
ISO_VERSION="1.27.0" # the ISO's own version. INDEPENDENT of felhom-host-install.sh's SCRIPT_VERSION,
|
||||
# which is fetched at run time from main and is not frozen into the image.
|
||||
IMAGE="${FELHOM_ISO_ASSISTANT_IMAGE:-felhom-iso-assistant:trixie}"
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -69,11 +69,42 @@ print_pairing_banner() {
|
||||
printf ' Felhom — a doboz készen áll, és a párosításra vár.\n\n'
|
||||
printf ' Párosító kód: %s\n\n' "$code"
|
||||
printf ' Nyisd meg az e-mailben kapott linket, és add meg\n'
|
||||
printf ' ezt a kódot és a jelszavadat.\n\n'
|
||||
printf ' ezt a kódot és a Tulajdonosi jelmondatodat\n'
|
||||
printf ' (az 5 szót a Felhom üzemeltetőjétől kaptad).\n\n'
|
||||
printf ' Ez a képernyő magától frissül — nincs teendő a\n'
|
||||
printf ' doboznál, és nyugodtan itt hagyhatod bekapcsolva.\n'
|
||||
printf '================================================\n\n'
|
||||
} > /dev/console 2>/dev/null || printf 'Párosító kód: %s\n' "$code"
|
||||
} > "$CONSOLE_DEV" 2>/dev/null || printf 'Párosító kód: %s\n' "$code"
|
||||
}
|
||||
|
||||
# install_felhom_issue (R-496, v1.27.0) — the text above the console login prompt is Felhom's, not
|
||||
# Proxmox's. Measured 2026-09-14 (drill screen s29): the first thing a household read on a fresh box was
|
||||
# `Welcome to the Proxmox Virtual Environment … connect to https://<ip>:8006/` — the operator admin UI,
|
||||
# in English. pvebanner.service REWRITES /etc/issue on every boot (/usr/bin/pvebanner opens it '>'), so
|
||||
# overwriting the file alone would last one boot: the unit is MASKED, then the file is written.
|
||||
# Best-effort in every step — a banner must never block the boot or the pairing.
|
||||
ISSUE_FILE="${FELHOM_ISSUE_FILE:-/etc/issue}"
|
||||
install_felhom_issue() {
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
if systemctl mask pvebanner.service >/dev/null 2>&1; then
|
||||
log "console: pvebanner.service masked (it would rewrite $ISSUE_FILE with the Proxmox admin URL on every boot)"
|
||||
else
|
||||
log "console: could not mask pvebanner.service — the Proxmox banner may return on the next boot"
|
||||
fi
|
||||
fi
|
||||
local tmp="${ISSUE_FILE}.felhom-tmp"
|
||||
if { printf '\n'
|
||||
printf ' Felhom otthoni szerver\n\n'
|
||||
printf ' Ezen a képernyőn nincs teendőd, bejelentkezni sem kell.\n'
|
||||
printf ' A beállításhoz kövesd a Felhomtól kapott útmutatót.\n\n'
|
||||
} > "$tmp" 2>/dev/null && mv -f "$tmp" "$ISSUE_FILE" 2>/dev/null; then
|
||||
log "console: $ISSUE_FILE is the Felhom text (no admin URL)"
|
||||
else
|
||||
rm -f "$tmp" 2>/dev/null
|
||||
log "console: could not write $ISSUE_FILE — continuing"
|
||||
fi
|
||||
command -v agetty >/dev/null 2>&1 && agetty --reload >/dev/null 2>&1
|
||||
return 0
|
||||
}
|
||||
|
||||
cleanup_pass() { [[ -e "$PASS_FILE" ]] && { shred -u "$PASS_FILE" 2>/dev/null || rm -f "$PASS_FILE"; }; return 0; }
|
||||
@@ -528,6 +559,9 @@ emit("FELHOM_EXTRA_ARGS", d.get("extra_args"))
|
||||
done
|
||||
}
|
||||
|
||||
# --- R-496: Felhom's text above the console login, before anything can wait on the network --------
|
||||
install_felhom_issue
|
||||
|
||||
# --- R-59/R-60 first-boot network gate: never proceed silently into a hub-unreachable install ------
|
||||
network_gate
|
||||
|
||||
|
||||
@@ -97,7 +97,9 @@ exit 0
|
||||
HI
|
||||
exit 0 ;;
|
||||
*"/appliance/register")
|
||||
echo '{"appliance_token":"TESTTOKEN123456","poll_interval_sec":30}'
|
||||
# pairing_code (R-27): without it print_pairing_banner returns early and the banner is never
|
||||
# painted — which is how the banner went untested until v1.27.0 (R-496).
|
||||
echo '{"appliance_token":"TESTTOKEN123456","poll_interval_sec":30,"pairing_code":"TST-CDE"}'
|
||||
exit 0 ;;
|
||||
*"/appliance/poll")
|
||||
if [ "$mode" = "200" ]; then
|
||||
@@ -113,15 +115,15 @@ exit 0
|
||||
CURL
|
||||
chmod +x "$FAKE/curl"
|
||||
|
||||
# fake systemctl (disable is a no-op)
|
||||
printf '#!/bin/bash\nexit 0\n' > "$FAKE/systemctl"; chmod +x "$FAKE/systemctl"
|
||||
# fake systemctl (disable is a no-op); logs every call so R-496's pvebanner mask is observable
|
||||
printf '#!/bin/bash\necho "$*" >> /work/systemctl.log\nexit 0\n' > "$FAKE/systemctl"; chmod +x "$FAKE/systemctl"
|
||||
|
||||
reset_state() {
|
||||
rm -rf /etc/felhom /run/felhom-bootstrap-pass /var/lib/felhom-install "$CALLS" /work/hostinstall.log \
|
||||
/work/poll-mode /work/sleep.count /work/sleep.flip /work/sleep.abort \
|
||||
/work/ip.log /work/ifreload.log /work/dhclient.log /work/hub-mode /work/dhcp-mode \
|
||||
/work/sys /work/interfaces /work/interfaces.felhom-bak /work/console.out \
|
||||
/run/felhom-interfaces.orig /work/run.log
|
||||
/run/felhom-interfaces.orig /work/run.log /work/issue /work/systemctl.log
|
||||
mkdir -p /etc/felhom
|
||||
}
|
||||
|
||||
@@ -150,7 +152,7 @@ iface nicB inet manual
|
||||
source /etc/network/interfaces.d/*
|
||||
IFACES
|
||||
}
|
||||
GATE_ENV="FELHOM_NET_SYS=/work/sys FELHOM_INTERFACES_FILE=/work/interfaces FELHOM_CONSOLE_DEV=/work/console.out"
|
||||
GATE_ENV="FELHOM_NET_SYS=/work/sys FELHOM_INTERFACES_FILE=/work/interfaces FELHOM_CONSOLE_DEV=/work/console.out FELHOM_ISSUE_FILE=/work/issue"
|
||||
|
||||
# ============================ Scenario D — direct mode, zero appliance calls =========================
|
||||
# Runs WITH the gate fixture (NICs + fallback-shaped interfaces) and the hub reachable: the G1
|
||||
@@ -179,6 +181,13 @@ check "G1: gate made zero ifreload calls" "[ ! -f /work/ifreload.log ]"
|
||||
check "G1: gate made zero dhclient calls" "[ ! -f /work/dhclient.log ]"
|
||||
check "G1: gate consumed zero sleeps" "[ ! -f /work/sleep.count ]"
|
||||
check "G1: interfaces fixture untouched" "grep -q 'bridge-ports nicA' /work/interfaces && grep -q '192.168.100.2' /work/interfaces"
|
||||
# R-496 (v1.27.0): the console's login banner is Felhom's, not Proxmox's — and it survives a reboot,
|
||||
# because pvebanner.service (which rewrites /etc/issue on EVERY boot) is masked, not merely overwritten.
|
||||
check "R-496: /etc/issue written" "[ -s /work/issue ]"
|
||||
check "R-496: /etc/issue is the Felhom text" "grep -q 'Felhom otthoni szerver' /work/issue"
|
||||
check "R-496: /etc/issue carries no admin URL (:8006)" "! grep -q '8006' /work/issue"
|
||||
check "R-496: /etc/issue does not say Proxmox" "! grep -qi 'proxmox' /work/issue"
|
||||
check "R-496: pvebanner.service masked" "grep -q 'mask pvebanner.service' /work/systemctl.log"
|
||||
|
||||
# ============ P: pairing loop (v1.21.0) — register, wait unbound INSIDE one invocation, deliver =====
|
||||
say "P: pairing env -> register + in-script 204 wait -> 200 delivery -> host-install, ONE invocation"
|
||||
@@ -188,7 +197,12 @@ FELHOM_HUB_URL=https://hub.example
|
||||
ENV
|
||||
echo 204 > /work/poll-mode
|
||||
echo 3 > /work/sleep.flip # after 3 in-script waits the hub "binds" (poll flips to 200)
|
||||
bash "$BSTRAP"; rc=$?
|
||||
rm -f /work/console.out
|
||||
env FELHOM_CONSOLE_DEV=/work/console.out FELHOM_ISSUE_FILE=/work/issue bash "$BSTRAP"; rc=$?
|
||||
# R-496: the pairing banner names the secret the way the self-bind mail and page do (R-323).
|
||||
check "R-496: banner painted to the console seam" "grep -q 'Párosító kód' /work/console.out"
|
||||
check "R-496: banner names the Tulajdonosi jelmondat" "grep -q 'Tulajdonosi jelmondat' /work/console.out"
|
||||
check "R-496: banner no longer says 'jelszavad'" "! grep -q 'jelszavad' /work/console.out"
|
||||
check "single invocation ran to done (exit 0)" "[ $rc -eq 0 ]"
|
||||
check "POSTed /appliance/register" "grep -q '/appliance/register' $CALLS"
|
||||
check "appliance token persisted 0600" "[ -f /etc/felhom/.bootstrap-done ] || { [ -f /etc/felhom/appliance-token ] && [ \"\$(stat -c %a /etc/felhom/appliance-token)\" = 600 ]; }"
|
||||
|
||||
Reference in New Issue
Block a user