Files
felhom.eu/REPORT.md
T
admin 02d63ed070 GL-2: --mode appliance|byo install profile (host-install v1.10.0) + harness
Mandatory install profile (no default): byo = a host the operator does not
own. Break-glass gated OFF at its call site in byo (root@pam never touched),
mandatory --cores/--memory, argv-time refusals (--enable-oob/--rotate-recovery,
non-9.x PVE, missing --acl-storages), host-mutation disclosure + typed-hostname
ack, byo config asserts (lan_resolver/wg_tunnel/oob off; byo flips the
lan_resolver write default to off), pool+ACL verify asserts in BOTH modes (R2),
--preflight-only (no state, PASS/FAIL verdict), resume mode-mismatch refusal,
FELHOM_INSTALL_STATE_DIR harness override.

NEW scripts/hostinstall-mode-harness.sh: static refusal matrix C1-C4 + grep
invariants + PVE tier (C5 + A/B dry transcripts). 16/16 PASS on felhom-pve
(C5 live); red-proofs RP-1..RP-3 run->fail->revert. shellcheck clean at
severity=warning. Docs: day0-install SC.5 byo section + trust model; REUSE row;
CONTEXT + REPORT. Live drill = GL-6 (supervised); STOP honored (no non-dry run).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-07 20:26:32 +02:00

6.3 KiB
Raw Blame History

felhom.eu — task reports

Overwrite this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in hub/CHANGELOG.md; the scripts history lives in scripts/CHANGELOG.md.

TASK GL-2 — BYO-host install profile (--mode appliance|byo) — host-install v1.10.0 (2026-07-07)

Baseline confirmed: felhom.eu @ 1a1e42ad, SCRIPT_VERSION="1.9.1"1.10.0. Implementation + static/dry validation only per spec §0 — the live provision/uninstall runs are GL-6 (operator-supervised); the §13 STOP was honored (zero non-dry installer invocations anywhere).

Files created / modified (single repo, main)

  • scripts/felhom-host-install.sh — v1.10.0 (Parts 13; see scripts/CHANGELOG.md for the feature list)
  • scripts/hostinstall-mode-harness.sh — NEW (Part 4)
  • documentation/runbooks/day0-install.md--mode in every command, new §C.5 (byo command + trust-model paragraph + preflight-only), §C.6 (pre-v1.10.0 resume note), 4 new troubleshooting rows
  • scripts/CHANGELOG.md, REUSE.md (install-profile-gate pattern row), CONTEXT.md, this file

Spec deviations / judgment calls (each verified against the real code)

  1. --mode collision: v1.9.1 already used --mode provision|dr. Resolved: appliance|byo are the only install values, provision retired with a specific die, the dr stub dispatch kept verbatim (the documented 10D seam).
  2. Spec §2 was wrong about the lan-resolver: the config write did setdefault('lan_resolver', {"enable": True}) — default-ON, so the spec's byo assert would have died on every byo install. Fixed by flipping the byo DEFAULT to off (appliance untouched); the assert still dies if a --preserve-from config carries true, which is the intended refusal.
  3. The byo skip line reads break-glass (step 4b) SKIPPED (byo: …) — NOT the spec's literal "4b/8 …" text, because Scenario B / harness H-B forbid the string 4b/8 in the byo transcript (an internal spec contradiction; the greppable invariant won).
  4. --preflight-only skips the byo typed ack (prints the disclosure, logs the skip) — nothing will be mutated, and a mandatory prompt would break its re-runnable/scripted purpose.
  5. documentation/pilot/GO-LIVE-PACKAGE.md is not in the repo and no operator copy arrived with the task — the G2/G4/G5 → "implemented, awaiting GL-6" flip is recorded in CONTEXT.md; add the doc when supplied.

Harness results

Static tier (Windows Git Bash AND felhom-pve, identical): bash -n, C1/C1b/C1c, C2a/C2b, C3/C3b/C3c, C4, C6-static, INV-1 (one appliance-gated break-glass call site), INV-2 (chpasswd unreachable outside step_break_glass), INV-3 (--mode in usage) — all PASS.

PVE tier (felhom-pve, read-only, temp FELHOM_INSTALL_STATE_DIR): C5 PASS live (bogus --acl-storages entry → die naming it, pre-hub, pre-passphrase). H-A/H-B dry transcripts ran with the out-of-band demo passphrase (file→file, never printed) but die at step 5 with rc=1 — a customer-data blocker, not a code bug: demo-felhom's controller.yaml serves EMPTY git.username/git.token. The transcript span through the break-glass gate was validated manually instead: appliance transcript contains 4b/8; byo transcript contains no 4b/8/chpasswd/recovery-credential and does contain the disclosure block, the dry ack line, acl storages all present, and the host-DNS :53 line (it correctly showed the live dnsmasq). --preflight-only proven live both ways: PASS verdict + rc 0 + zero state entries; bogus-storage run → specific die + PRE-FLIGHT FAIL (exit 1) verdict.

Red-proofs (run → fail → revert, against mutated scratch copies — the repo file was never mutated):

  • RP-1 un-gated the break-glass call site → INV-1 FAIL (13/16). Reverted (scratch deleted).
  • RP-2 dropped the byo caps requirement → C1/C1b/C1c FAIL (11/16). Reverted.
  • RP-3 dropped the resume mode-mismatch check → C4 FAIL (13/16). Reverted.

§9 rule 9 — the _state_mark dry-run finding

Verified in code: both _state_mark and _state_put begin with $DRY_RUN && return 0 — dry runs write nothing. Belt on top: the harness points FELHOM_INSTALL_STATE_DIR (new, harness-only env override — none existed) at a throwaway temp dir for EVERY invocation, so even non-dry refusal cases can never touch a live state.json. Confirmed live: felhom-pve's real state.json was byte-untouched (it only ever saw the temp dir) and the preflight-only runs left 0 entries.

Shellcheck

v0.10.0 (build server 180 + felhom-pve; not installed locally). Clean at --severity=warning on both scripts. Full-severity: 2 pre-existing SC2015 info notes on untouched v1.9.1 lines (A&&B||C in step_verify) — triaged, not refactored (spec rule 2); 1 new SC2154 false positive on the EXIT trap (assignment inside the trap string) — targeted disable comment.

NOT yet live-validated — awaiting supervised GL-6

  • The full appliance AND byo installs end-to-end (rc=0 transcripts blocked today by the demo-felhom git-creds gap below).
  • The uninstall → re-provision drill under both modes.
  • C7: the verify pool/ACL asserts firing on real drift (needs a deliberately-broken live box).
  • The byo typed-hostname ack answered interactively (only its dry/preflight-only branches ran).

Observations (documented, not acted on)

  • demo-felhom has EMPTY git.username/git.token in its served controller.yaml — any step-5 (re)install for that customer dies until real Gitea read creds are set (operator; ties into the standing "scope down + rotate the package-WRITE git token" security follow-up).
  • felhom-pve's live install predates _state_put — its state.json has only completed (no customer_id/provisioned_vmid/mode); a future --uninstall there needs an explicit --vmid, and §C.6 of the runbook covers the missing-mode resume.
  • A --resume that skips preflight never records mode into an old state file (it only writes during a full preflight) — harmless (C4 only fires when a mode IS recorded), noted for GL-6.
  • The live host-DNS line on felhom-pve shows the agent's own dnsmasq (lan_resolver is ON there — correct for an appliance; it is exactly what byo now refuses).