02d63ed070
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
94 lines
6.3 KiB
Markdown
94 lines
6.3 KiB
Markdown
# 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](hub/CHANGELOG.md); the scripts history lives in [scripts/CHANGELOG.md](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 1–3; 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).
|