Files
felhom.eu/documentation/pilot/DRILL-GL6-2026-07-08.md
T
admin fb8e5111cf docs: GL-8 wrap — runbook F3/F6 notes, findings FIXED, REPORT, decision log
day0-install: F3 (no --allow-new-leaf on a populated BYO host) + F6 (:53 must
be free, refusal + remediation). GO-LIVE-PACKAGE + DRILL-GL6 ledger flip
F1/F3/F6 -> FIXED v1.12.0; GL-7 unblocked; F2/F7 stay open. CONTEXT + REPORT.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-08 19:40:57 +02:00

387 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DRILL GL-6 — supervised go-live drill (demo box, two full cycles) — 2026-07-08
> Go-live item **G7** + the live-validation landing zone for GL-1…GL-5b deferrals. Destructive-
> supervised: guest 9201 destroyed + rebuilt twice; Viktor present, gating every phase. This record
> is filled per phase as evidence lands; timings + deviations verbatim. No secrets.
**Inputs verified at start:** felhom.eu `bd97bc34` (host-install **v1.11.1**, operator pubkeys
pinned) · felhom-agent `7f073936` (v0.76.0) · hub Day-0 manifest vouches agent
`0.76.0`/`9828c5f7…f50b` + golden `0.103.0`/`8481e8a1…6026e` (fetched via the box-facing API).
## Phase 0 — preflight + key validation (nothing destroyed)
| Step | Result |
|---|---|
| 0a baselines | **PASS** — repo heads + `SCRIPT_VERSION="1.11.1"` + manifest all as stated above |
| 0b key validation leg 1 | **PASS** — all four fingerprints EQUAL (below) |
| 0c key validation leg 2 | [Viktor] — pending at Gate 0 |
| 0d abort-path backup + restore-test | **PASS** (local + PBS backups fresh; PBS restore-test parity ok, 5m35s) |
| 0e busy-drive prop | **STAGED**`gl6-busyprop` unit cwd-pinned into `/mnt/felhom-drives/felhom-usb` (4h) |
| 0f mechanism re-read | **DONE** — Phase-3 delivery pinned (below) |
### 0b — the four fingerprints, side by side
```
repo op (felhom-op-1): SHA256:7YqN4rXO08yixTeOO+UtQ8jHyIGycICuctQgRYVGnWw
box op (felhom-op-1): SHA256:7YqN4rXO08yixTeOO+UtQ8jHyIGycICuctQgRYVGnWw
repo rec (felhom-rec-1): SHA256:/ixgTesZqykAGJpFUUd4kLAiHFgKOkYFLNC3AQXWP+k
box rec (felhom-rec-1): SHA256:/ixgTesZqykAGJpFUUd4kLAiHFgKOkYFLNC3AQXWP+k
```
Box source: felhom-pve agent config (`systemctl cat``-config` path) `authz.signers[]`;
repo source: the v1.11.1 `OPERATOR_KEY_*_LINE` constants. Both match the runbook's stated values.
### 0d — abort-path backups (the session's safety net)
- **Local (agent pipeline):** `--selftest=backup -vmid 9201` → snapshot-mode, crash-consistent,
`local:backup/vzdump-lxc-9201-2026_07_08-11_43_42.tar.zst` (6.82 GB, 5m52s). Expected
`uncovered_volumes`: `/etc/felhom-bootstrap` + `/mnt/felhom-drives` (bind mounts — regenerated /
re-enrolled, per doctrine).
- **PBS (offsite tier, PVE-native vzdump per the S4-proven path):** `felhom-pbs:backup/ct/9201/2026-07-08T09:51:00Z` — 3m04s (dedup)
— NOTE: the live config deliberately pins `local_backup_target: local`; the PBS leg was driven
with `vzdump 9201 --storage felhom-pbs --mode snapshot` (tunnel healthy, storage active).
- **Restore-test vs the fresh PBS archive:** **PASS**`mount_parity: ok`, inventory
`mp0=/var/lib/docker (200G)`, `mp1=/mnt/sys_drive (50G)`, mp8/mp9 throwaways; scratch 990000
restored+booted+verified+torn-down in **5m35s** (first G12 PBS-tier runtime datum).
Observation (cosmetic): the record's `source_tier` says "local" — the tier label derives from the
CONFIG backup target, not the explicit `-archive`'s storage; display-only.
### 0e — busy prop + data-intact witness
Prop: transient unit `gl6-busyprop`, cwd inside `/mnt/felhom-drives/felhom-usb`.
Witness file (for the Phase-1/5 drive-data-intact checks):
`userdata/actualbudget/canary_hdd_actualbudget.txt` on `/dev/sdd1`
(UUID `da9e7089-cf8e-4617-adcb-a377743fae00`), sha256 prefix `7b00bc3550b10e3952e0e0965f5a2628`.
### 0f — Phase-3 signed-update mechanism (confirmed at source, NOT improvised)
1. [Viktor] sign: `felhom-opsign -op agent_update -host <host_id> -key-id felhom-op-1
-agent-version <v> -sha256 <hex> -key <operational private key> [-ttl 30m]` → signed-op blob.
2. Deliver: `POST /api/v1/admin/hosts/{host_id}/jobs` `{job_id, blob_b64}` — **hub GLOBAL key
required** (Viktor), or the v0.70.0-precedent direct `signed_jobs` sqlite insert on the hub pod
(CC via kubectl on 180). The agent's heartbeat sees `has_signed_ops`, fetches, verifies against
the PINNED operational key + durable nonce store, downloads the binary from Gitea, re-verifies
the SIGNED sha (the only integrity root), then `felhom-selfupdate-guarded apply` A/B-swaps with
crash-rollback armed; dwell → `commit`.
## Gate 0 — [Viktor]
- Input 1 (byo caps): **--cores 4 --memory 8192**
- Input 2 (Phase-6 repopulation): **fresh + redeploy apps**
- 0c fingerprint confirmation vs password manager: **CONFIRMED — match**
- Verdict: **GO** — with one pre-Phase-1 STOP + ruling (below)
### Gate-0 deviation — the demo customer's empty git credentials (Viktor ruling → v1.11.2)
Pre-destroy check found demo-felhom's `git.username`/`git.token` still EMPTY (the GL-2 finding);
the v1.11.1 installer would die at step 5/8. **Viktor ruled: the artifacts are world-readable by
design — anonymous fetch is the supported path.** All 12 installer fetch targets (2 packages + 10
raw configs) validated HTTP 200 anonymously, then **host-install v1.11.2** shipped mid-drill
(felhom.eu `0640aa06`): empty creds → loud WARN + anonymous fetch (conditional curl auth; the
hub-vouched sha256 stays the integrity root; configured creds still used when present). Harness
26/26 (new GL6-ANON case; GL4-C2 assertion updated for the pinned constants).
## Phase 1 — uninstall #1 (full mode, busy drive staged) — PASS with 2 findings
Real `--uninstall --vmid 9201` (typed confirm, v1.11.2 fetched from the served URL):
- **Busy-drive behavior (the staged prop on felhom-usb):** WARN + "Eject the drive via the
dashboard, or stop the apps using it and re-run" — NO `-l`/`-f`, teardown CONTINUED; the other
two drives unmounted ("data stays on the drive"); the root bind correctly left mounted with its
own guard warn. The KEPT-vs-WIPED statement listed felhom-usb as
"still mounted (busy — stop the apps and retry)" — i.e. the code takes the
statement-lists-it-as-retry path; after releasing the prop a plain manual `umount` completed the
job (no re-run needed).
- **KEPT-vs-WIPED statement:** printed verbatim as designed (guest+volumes / agent / self-update
artifacts / watchdog+OOB / pveum+pool / state = WIPED; drives+data / PBS backups / hub record /
escrow = KEPT). The recovery-credential KEPT line correctly keyed off the state file — which on
THIS box predates the 4b state marker, so the line was absent although a G1-vaulted credential
exists (observation: state-keyed conditionals can under-report on pre-v1.10.0 installs).
- **1b audit:** agent binary+baks, selfupdate wrapper+slots, rollback unit+drop-in, watchdog set,
mkfs wrapper, snippets, dnsmasq drop-ins, sudoers, state dirs, pveum user/roles/ACL/pool — ALL
GONE. PVE printed a benign "ignore invalid acl role 'FelhomAgentStore'" notice during ACL
removal (ordering noise, recorded).
- **Drive data intact:** witness file re-hashed on the still-mounted primary path —
`7b00bc3550b10e3952e0e0965f5a2628` == the Phase-0 value.
### Findings
- **GL6-F1 (MED, security-adjacent):** `/etc/felhom-agent/` SURVIVES the uninstall holding
`agent.json.bak-*` files (incl. the operator's same-day ceremony backup) — old configs with
secret material (pve token now dead; the per-host hub api_key is still live because the hub
record survives). The uninstall removes `agent.json` but not `.bak-*` siblings. Fix candidate:
purge `"$agent_cfg"*` / the dir in 4b (v1.11.3). Files left in place pending Viktor's call
(one is his own backup).
- **GL6-F2 (MED):** the agent-created per-drive systemd mount units
(`mnt-felhom-usb.mount` + flash + teszt_enroll) and their ACTIVE primary mounts at
`/mnt/<name>` survive — the statement's "physically removable now" overclaims (the binds are
gone but the partition is still mounted). Fix candidate: uninstall stops+removes the agent's
`mnt-*` units for enrolled namespaces (data untouched). Left as-is for Phase 2 (the re-enroll
behavior against pre-existing mounts is itself drill-relevant).
## Gate 1 — [Viktor]: **GO** (delete the .bak secrets now)
GL6-F1 residue resolved at the gate: `shred -u /etc/felhom-agent/agent.json.bak-*` + `rmdir`
(incl. Viktor's ceremony backup, confirmed disposable). F1/F2 kept as v1.11.3 fix candidates.
## Phase 2 — byo-mode day-0 (Peti's exact path, on real hardware) — PASS (happy path), 3 findings
Command (Viktor typed the `demo-felhom` hostname ack — interactive consent leg):
`./felhom-host-install.sh --customer-id demo-felhom --mode byo --vmid 9201 --cores 4 --memory 8192
--force-gitea-golden --acl-storages "local local-lvm" --passphrase-file <0600>` (+ `--allow-new-leaf
--resume` after F3/F4, see below).
**End state — all green (verify step, verbatim highlights):**
- disclosure printed; **Viktor's typed-hostname ack accepted** (`byo install acknowledged for host demo-felhom`);
- **NO 4b / chpasswd / recovery-credential** anywhere (grep of the full transcript = 0);
- **anonymous artifact fetch worked** (v1.11.2): agent v0.76.0 sha `9828c5f7…` verified, golden
0.103.0 sha `8481e8a1…` verified, both against the hub manifest;
- installed agent `--version` == **0.76.0**; daemon **active**;
- **`authz signers: 2 (operator-signed self-update armed)`** from first boot — **key validation leg 3 ✓** (config carries operational:felhom-op-1 + recovery:felhom-rec-1);
- pool member + all 4 scoped-ACL grants (user+token) present; **caps on the guest: cores 4 / memory 8192**;
- byo config asserts pass (lan_resolver/wg_tunnel/oob all off);
- **controller Up (healthy) 0.103.0** + traefik + cloudflared up; dashboard `HTTP 200` (Host-header probe); hub config-retrieve OK.
### Findings — the byo install's resume path is fragile (happy single-shot path is clean)
- **GL6-F3 (MED, GL-7-relevant):** on a BYO host carrying the OWNER's (non-Felhom) guests, a FRESH
install trips the populated-host leaf guard (`pct list | grep -q .` counts ANY guest) and dies
demanding `--preserve-state-from`/`--allow-new-leaf` — even though no Felhom controller pin exists
to protect on a fresh install. **Peti's box has his own guests → his day-0 command MUST include
`--allow-new-leaf`** (GL-7). Better fix candidate: narrow the guard to `felhom_guests` so a fresh
byo install on a populated non-Felhom host doesn't trip.
- **GL6-F4 (HIGH, resume-correctness):** `--resume` fully skips completed steps via
`should_skip X || step_X`, but later steps consume the skipped steps' IN-MEMORY outputs, which are
then empty → broken results: **(a)** `enroll` skipped → `HOST_ID`/`HOST_API_KEY` empty →
`step_agent_config` writes a config missing `hub.host_id` (+ empty `proxmox.token` when `token` is
skipped) → **daemon crash-loops** (`hub.host_id is required`); the mint-once token secret is
unrecoverable, so recovery = re-run `token`+`enroll`. **(b)** `grows` skipped →
`ROOTFS_GROW`/… empty → provision passes `-rootfs-grow ""` → **flag parse error**. Note
`step_token` even has an internal resume-guard (`[[ -n "$PVE_TOKEN" ]]`) that the `|| step_token`
dispatch DEFEATS — strong signal the dispatch is the bug (steps whose outputs feed later steps must
re-run, or re-derive from the written config, on resume). **Drill recovery:** dropped the affected
completed marks from `state.json` to force re-run — the install then completed cleanly. The
single-shot happy path (no interruption) is unaffected, which is Peti's path.
- **GL6-F2 (from Phase 1, MED):** the agent's per-drive `mnt-*.mount` units + primary `/mnt/<name>`
mounts survived uninstall #1; on this reinstall the parent bind `/mnt/felhom-drives` is back but the
drives are NOT yet bound under it — re-enrollment is the Phase-2b UI leg.
### F4 fixed mid-drill (Viktor: HOLD → fix now) — host-install v1.11.3 (`98bf5a43`)
Root cause confirmed: `should_skip X || step_X` fully skips a completed step on `--resume`, but
token/enroll/grows are PRODUCERS whose in-memory outputs later steps consume; `step_token` even had
an internal resume-guard the `|| step_token` dispatch DEFEATED. Fix: token/enroll/grows run every
pass (all idempotent — reuse-or-rotate / mint-once-reuse POST / pure recompute); golden's
`GOLDEN_VOLID` re-derived from the local archive in the resume block. Harness +GL6-F4 invariant,
27/27, shellcheck clean.
**Live proof (v1.11.3, crafted `--resume --dry-run` on felhom-pve, throwaway `FELHOM_INSTALL_STATE_DIR`,
zero mutations):** marks = the exact pre-crash set `[preflight,token,grows,enroll]` (agent_config
NOT done). Transcript: steps **2/8 token, 3/8 grows, 4/8 enroll all RE-RUN**; the agent_config write
shows **`host_id=<dry-run-host-id>` (non-empty)** and provision shows **`-rootfs-grow 0 -datavol-grow
184 -sysdata-grow 42`** — both the exact values that were empty (→ crash-loop / flag parse error)
pre-fix. Real 9201 + its state.json untouched (verified: daemon active, controller Up healthy).
**GL6-F4 CLOSED + live-validated.** (GL6-F3 stays a GL-7 command requirement `--allow-new-leaf`;
guard-narrowing to `felhom_guests` recorded as a candidate. GL6-F1/F2 remain v1.11.x candidates.)
### Phase 2b — bring-to-life: BLOCKED on public DNS (GL6-F5), browser bridge confirmed working
- **Browser bridge: WORKS** — created a tab group, navigated to the dashboard; the failure below is
the SITE, not the bridge.
- **GL6-F5 (onboarding, Viktor/Cloudflare):** after the byo reinstall, `felhom.demo-felhom.eu` does
NOT resolve publicly — `curl` from the box (outbound internet) returns `Could not resolve host`.
The cloudflared tunnel is UP (QUIC + HTTP/2 to argotunnel, precheck all pass, same tunnel token),
and the dashboard is healthy IN-GUEST (`HTTP 200`, "Beállítás" setup page) — so the controller +
tunnel are fine; the missing piece is the public DNS CNAME `felhom.demo-felhom.eu → <tunnel>.cfargotunnel.com`.
The controller does not auto-(re)create the dashboard's own DNS record on reinstall. **This blocks
the 2b real-pipeline rehearsal** (password set + drive re-enroll need the dashboard reachable in a
browser). NOT an install-correctness defect — the install is green; it is a Cloudflare-zone /
onboarding gap that also matters for Peti (GL-7 / G9).
### Phase 2b — bring-to-life (browser, CC-driven) — drive re-enroll + data-visible PASS
DNS unblock: Viktor added a Pi-hole split-horizon A record `felhom.demo-felhom.eu → 192.168.0.105`
(LAN path to the guest); dashboard then loaded in-browser. (The public Cloudflare path is ALSO
correct — forced-DoH resolution returned HTTP 200 with the real page; only the client-side resolver
via the box's broken dnsmasq (F6) was failing.)
- **Dashboard live:** "Demo Ügyfél", 3 running / 56 total apps, mem 1.1/8 GB, backup "Még nem futott".
- **Drive re-enrollment (CC-driven UI):** Storage → Meghajtók showed all 3 drives detected-but-
UNREGISTERED (the agent's registration state was wiped in uninstall #1; the drives + data survived
per F2, tagged **"Adatot tartalmaz"**). Clicked **Regisztrálás** on `felhom-usb`
(uuid `da9e7089…`, /dev/sdd1) — it moved to **Adattárolók → Aktív**, ext4 /dev/sdd1[/felhom-data]
HD710 PRO, additive (no format). **Data-visible PASS:** the witness file is now readable in the
guest at `/mnt/felhom-drives/felhom-usb/userdata/actualbudget/canary_hdd_actualbudget.txt`,
hash `7b00bc35…` == the Phase-0 value. Additive doctrine proven end-to-end through the real UI.
- **GL6-F6 (MED, byo coexistence):** the box runs a leftover **broken dnsmasq** (active, bound to
`0.0.0.0:53`, but queries time out — empty `/etc/dnsmasq.d/`, no upstream) on a byo host where
`lan_resolver` is OFF. It breaks DNS for LAN clients that use the box as a resolver (it broke the
drill's browser resolution). Fix pile with F1/F2: byo install/uninstall should stop+mask the
leftover dnsmasq. Immediate drill unblock was the Pi-hole entry.
- **G9/G10 — password is OPERATOR-set, currently UNSET → dashboard OPEN.** Settings → Biztonság:
*"A jelszavas védelem nincs beállítva. Kérd az üzemeltetőt a beállításhoz."* The dashboard password
comes through the real config pipeline (hub customer-config → controller.yaml bcrypt — the anti-F9
path), NOT a customer-set field here. Until the operator sets it the public-reachable dashboard is
UNAUTHENTICATED (the known G10 exposure, now confirmed live). Geo-restriction also OFF. **Operator
leg pending:** Viktor sets the dashboard password via the hub, then CC verifies login is enforced.
## Gate 2 — [Viktor]: **GO** — Peti's byo path live-proven end to end
Green: anonymous fetch, sha-verified 0.76.0 + 0.103.0, signers ARMED from first boot, caps applied,
no root@pam touched, controller healthy, drive re-enrolled additively with pre-drill data intact.
F4 found + fixed (v1.11.3) + live-proven. **Deferred to GL-7 with Peti:** the dashboard password
(operator-set via hub — G9) + geo-restriction; **G10 (open dashboard until the operator sets a
password) is now a GL-7 onboarding prerequisite.** Findings F1/F2/F3/F6 → post-drill installer pile.
## Phase 3 — armed key end-to-end (the only leg that needed real keys)
**PASS — the armed-key chain proven both directions with the REAL operational key.**
- 3a: drill agent **0.76.1** published (sha `ff18972f…aed16c`), version-only bump, throwaway.
- **Delivery mechanism (deviation from the [CC] tag, recorded):** the hub pod is a minimal image
(no sqlite3/python — only the felhom-hub binary) and the admin-enqueue API is GLOBAL-key gated, so
CC cannot inject the signed job. `felhom-opsign` has a built-in `-upload`+`-hub-key`, so the sign +
enqueue is ONE operator command (Viktor's secrets stay in it). The `-hub-key` is the hub's
**`report_api_key`** (live ConfigMap == committed `manifests/hub.yaml:77`; the operator/global key,
NOT the web-UI login) — a point of confusion worth a GL-7 note.
- 3b/3c FORWARD (0.76.0→0.76.1): `felhom-opsign -op agent_update -host demo-felhom-01 -key-id
felhom-op-1 -agent-version 0.76.1 -sha256 ff18972f… -key <op-priv> -upload https://hub.felhom.eu
-hub-key <report_api_key>`. Agent (restart-forced poll) → audit `gate decision class=agent_update
disposition=destructive allowed=true reason=signed key_id=felhom-op-1 nonce=4176e8e0…` → download
(anon Gitea) → sha re-verify → `felhom-selfupdate-guarded apply` A/B flip → `new version running
0.76.1 prev=0.76.0 dwell=1m0s` → `update committed version=0.76.1`. `.prev`=0.76.0 retained;
crash-guard silent.
- RETURN (0.76.1→0.76.0): same with the 0.76.0 published sha `9828c5f7…` + a FRESH nonce → committed
back to **0.76.0** (the manifest-vouched version; sha confirmed). Double round-trip = sign→verify→
nonce→swap proven twice with the real key.
- 3d: 0.76.1 package DELETED (HTTP 204 → 404 anon). Box ends at 0.76.0, daemon active.
**Key validation leg 4 (the whole point of GL-1's key-pin): the pinned operational key actually
authorizes a real signed op end-to-end.** Combined with legs 13 (box==repo==manager fingerprints,
armed-from-first-boot), the operator-signed self-update path is fully live-validated.
## Gate 3 — [Viktor]: **GO** — armed-key chain proven both directions.
## Phase 4 — drift-heal, live (the R2 fix earning its keep) — PASS, 1 finding
- **4a induce drift:** `pveum pool modify felhom --vms 9201 --delete 1` → pool members `[9201]` → `[]`;
the agent selftest immediately reflected it (`pool read: pool "felhom", 0 member(s)`) — the exact
silent drift R2 addresses.
- **GL6-F7 (MED, new):** the R2 `PoolAddVMID` re-assertion is **bring-up-only** (`bringup.go:498`,
after restore-over-existing) — there is NO continuous pool-membership reconcile (the stale-lock
reaper READS membership, `stalelock.go:183`, but never repairs). Confirmed live: 2 min after the
removal, no re-assert, pool stayed `[]`. So out-of-band membership drift does NOT auto-heal; it
needs a bring-up or `--adopt-pool`. (R2 itself DID fire correctly at this drill's provision —
`pool membership re-asserted vmid=9201` — so the fix works for its designed path.) Candidate:
a periodic membership reconcile; acceptable for the pilot (verify catches it, --adopt-pool fixes).
- **4b C7 — the verify assert firing on real drift (read-only):** installer `--resume` re-ran
`step_verify` → **`[ERROR] pool: guest 9201 NOT in pool felhom (the scoped token cannot manage it —
adopt with --adopt-pool)`** → `Day-0 provision completed WITH WARNINGS`. The ACL grants + `authz
signers: 2` stayed green (only membership was perturbed). C7 (GL-2's structural assert catching a
real silent drop) is now LIVE-VALIDATED — closes the GL-2 "not testable pre-drill" gap.
- **Repair:** `--adopt-pool --vmid 9201` re-added it (pool `[9201]`), re-verify → **SUCCESS**. Demo
healthy. (Also incidentally re-proved the v1.11.3 F4 fix: the `--resume` re-ran token/enroll/grows
cleanly and reached verify — no crash-loop, no flag error.)
## Gate 4 — [Viktor]: **GO** — drift-heal proven (C7 live, F7 recorded, repaired).
## Phase 5 — uninstall #2 (the byo box's clean exit) — PASS
Real `--uninstall --vmid 9201` (typed confirm), no busy prop this time.
- `felhom-usb` unmounted cleanly ("data stays on the drive"); statement reads **"the drives are
physically removable now"** (no busy exception this run) — the KEPT-vs-WIPED statement printed as
designed; UNINSTALL complete.
- **Host audit CLEAN:** no felhom-agent unit/user/binary/selfupdate-wrapper/A-B-slots/rollback-unit/
limits-drop-in/watchdog/mkfs-wrapper/hook-snippet/dnsmasq-drop-ins; pveum user 0, roles empty,
felhom pool gone; `/var/lib/felhom-agent`, `/var/lib/felhom-install`, `/etc/felhom-agent` all gone.
(F1 did NOT recur — this install's config had no `.bak` siblings.)
- **Drive DATA intact:** witness `7b00bc35…` == Phase-0 (the data-sovereignty exit, proven a 2nd time).
- Recurring known findings: **F2** (per-drive `mnt-*.mount` units survive), **F6** (leftover dnsmasq
still active). Trivial demo-box note: 6 ancient `felhom-agent.v0.1x.0.bak` binaries (Jun 1011,
legacy naming) survive the `.bak-*` glob — historical cruft, not on a real customer box; a fix
could widen the glob to `.v*.bak`.
## Gate 5 — [Viktor]: **GO** — clean byo exit, data intact 2nd time.
**6b — return to normal.**
- **Drive re-enrolled (CC-driven UI):** `felhom-usb` → Adattárolók **Aktív**, ext4 /dev/sdd1
[/felhom-data]; **witness `7b00bc35…` intact** in-guest (additive, 3rd data-intact proof).
(First click collided with the in-progress snapshot backup and no-op'd; retry after the backup
registered it — minor UI race worth a note. Only felhom-usb re-enrolled; flash/teszt left for
Viktor's demo curation.)
- **Manual backup + restore-test GREEN:** `selftest=backup OK (crash-consistent=true)` →
restore-test **`mount_parity: ok`** (29s — small: fresh install, no apps/drive-data yet).
- Controller + traefik + cloudflared + filebrowser all Up healthy; dashboard HTTP 200; hub reporting.
**Observation:** during the snapshot vzdump the dashboard briefly flashed "Protected container not
running: traefik/cloudflared/filebrowser" — a transient false-positive of the container monitor
during the backup freeze (cosmetic; cleared when the backup finished).
- **Viktor's ongoing return-to-normal (not drill blockers):** redeploy the demo apps (his Phase-0
choice: fresh redeploy); set the dashboard password (deferred to GL-7); update/align LAN DNS
(the re-provision changed the guest IP .105→.139 — a manual Pi-hole entry goes stale; rely on the
appliance lan_resolver or a reserved DHCP lease — GL-7 note).
## Phase 6 — appliance-mode reinstall (the demo returns to normal life)
**6a — appliance install: SUCCESS, single-shot.** `--mode appliance … --allow-new-leaf`.
- **4b/8 break-glass RAN** (appliance): `root@pam password set + vaulted to the hub` + break-glass
layers 1+2 installed — the recovery credential is in the hub (operator-retrievable). (SSH key
access unaffected.)
- verify green: pool member, all 4 scoped-ACL grants (user+token), **`authz signers: 2`**, controller
Up healthy 0.103.0, Day-0 provision SUCCESS.
- **Bonus: this ran in ONE shot** (vs the byo install's 4 resumes on v1.11.2) — the **v1.11.3 F4 fix
live-proven on a real end-to-end install**, not just the dry proof.
*(Phases 36 appended as they run.)*
## Gate 6 — [Viktor]: **GO — DRILL COMPLETE.**
Demo healthy on normal life. Two full destroy/rebuild cycles proven (byo #2, appliance #6), both
uninstalls clean, drive data intact ALL THREE checks, armed-key chain proven, C7 live-validated.
## Summary — what GL-6 validated
**Key-pin chain (GL-1's whole point) — legs 14 all green:** box==repo==password-manager fingerprints
(op `7YqN…`, rec `/ixg…`); signers ARMED from first boot on both byo + appliance installs; the pinned
operational key AUTHORIZED a real signed `agent_update` op end-to-end (round-trip 0.76.0→0.76.1→0.76.0
with the real key, verify vs pinned signer + nonce, A/B swap, crash-guard silent).
**Go-live gates now LIVE-VALIDATED on real hardware:** G2 (byo profile — disclosure+ack, no root@pam,
config asserts), G4 (preflight guards + verify pool/ACL asserts incl. **C7 firing on real drift**),
G5 (mandatory byo caps applied to the guest), G6 (uninstall gap-closure — KEPT/WIPED statement,
plain-umount busy-drive handling, self-update-artifact removal, **data intact ×3**), G7 (this drill).
**G1 DONE** (published chain consumed end-to-end via anonymous fetch — the v1.11.2 Gate-0 ruling —
+ armed keys proven). **G12** re-exercised (restore-test `mount_parity: ok` on PBS 5m35s + local tiers).
**G9** rehearsed (dashboard password is operator-set-via-hub; location confirmed) → **deferred to GL-7**;
**G10** (open dashboard until the operator sets a password) → GL-7 prerequisite.
## Findings ledger (post-drill installer pile unless noted)
| ID | Sev | What | Status |
|----|-----|------|--------|
| **F4** | HIGH | `--resume` skipped producer steps → config missing hub.host_id/proxmox.token (daemon crash-loop) + provision `-rootfs-grow ""` | **FIXED v1.11.3, live-proven** (dry + the single-shot appliance install) |
| F1 | MED | `/etc/felhom-agent/` + `agent.json.bak-*` (secret-bearing) survive uninstall | **FIXED v1.12.0 (GL-8)** — uninstall purges `${agent_cfg}.bak*` |
| F2 | MED | per-drive `mnt-*.mount` units + `/mnt/<name>` primary mounts survive uninstall ("physically removable" overclaims) | open — uninstall stop+remove the agent's `mnt-*` units |
| F3 | MED | byo FRESH install on a host with the OWNER's guests trips the populated-host leaf guard (`pct list`), needs `--allow-new-leaf` | **FIXED v1.12.0 (GL-8)** — guard narrowed to `felhom_guests` |
| F5 | — | initial "public dashboard DNS absent" — **MISDIAGNOSIS**: the `*.demo-felhom.eu` wildcard works (HTTP 200 via DoH); real cause was F6 | superseded by F6 |
| F6 | MED | leftover **broken dnsmasq** (active on `:53`) on a byo host breaks LAN-client DNS | **FIXED v1.12.0 (GL-8)** — byo preflight REFUSES a foreign `:53` + instructs (no mutation; Viktor ruling); live-proven |
| F7 | MED | R2 pool re-assertion is **bring-up-only** (`bringup.go:498`); no continuous membership reconcile → out-of-band drift doesn't auto-heal (repair via `--adopt-pool`) | open — candidate periodic membership reconcile |
| — | LOW | re-provision changes the guest DHCP IP (.105→.139) → manual LAN DNS entries go stale | GL-7 note (reserve lease / use lan_resolver) |
| — | LOW | legacy `felhom-agent.v*.bak` binaries survive the uninstall `.bak-*` glob | trivial (demo-box history only) |
| — | LOW | snapshot-mode backup briefly flashes "Protected container not running" (monitor false-positive during freeze) | cosmetic |
**Honest leftovers (unchanged, still open):** S5 customer-data DR drill; G12 parity-on-real-drift +
PBS-tier runtime characterization; CGNAT true-sim retest; Impl-3 shared-box format gate; OOB spike.
## Drill deviations (recorded)
1. **v1.11.2 shipped mid-drill (Gate-0 ruling):** empty customer git creds → anonymous artifact fetch
(the artifacts are world-readable by ruling; sha256 stays the integrity root). All 12 fetch targets
200-anon-validated first.
2. **F4 fixed mid-drill (v1.11.3, Viktor HOLD):** the resume-correctness bug, live-proven fixed.
3. **Phase-3 delivery is operator-driven, not [CC]:** the hub pod has no direct-insert tool and the
admin-enqueue API is global-key-gated, so Viktor signs+enqueues in one `felhom-opsign -upload` command
(`-hub-key` = the hub `report_api_key`, not the web login — a GL-7 clarity note).
4. **G9 password + full app redeploy deferred to GL-7** (operator/customer curation).