docs: GL-7 closeout — tester agreement + hub 0.36.0 CHANGELOG/REPORT/CONTEXT + GO-LIVE G7 done
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -2,66 +2,85 @@
|
||||
|
||||
> **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-8 — BYO coexistence hardening (host-install v1.12.0) — 2026-07-08
|
||||
## TASK GL-7 — customer page: passphrase hardening + install-command generator (hub v0.36.0) — 2026-07-09
|
||||
|
||||
**Baseline confirmed:** felhom.eu `1dc86523`, `SCRIPT_VERSION="1.11.3"` → **`1.12.0`** (`18a556a5`).
|
||||
Three independent fixes from the GL-6 drill findings, one file, one theme (BYO = Felhom is a guest
|
||||
on a host it doesn't own). felhom.eu only; agent untouched. §13 STOP honored — no live install/
|
||||
uninstall; the F6 refuse path is a read-only preflight die (safe to exercise), proven on felhom-pve.
|
||||
**Shipped:** felhom.eu `844fbfa7` (code) + `02c748eb` (manifest bump); **hub v0.36.0 LIVE** on k3s
|
||||
(ArgoCD app `felhom` Synced/Healthy, rollout confirmed, live image `felhom-hub:0.36.0`,
|
||||
`Listening on :8080`). felhom.eu only — agent + host-install untouched. Two coupled, security-first
|
||||
changes to the operator customer page, plus the Peti tester agreement.
|
||||
|
||||
### The three fixes
|
||||
### Part 1 — passphrase hardening (the security win, ships first)
|
||||
|
||||
- **F3 — leaf guard narrowed.** The populated-host leaf-regeneration guard tested `pct list` (ANY
|
||||
guest) and so over-fired on a BYO host's own non-Felhom guests, forcing `--allow-new-leaf` on
|
||||
every fresh byo install. Now it tests `[[ -n "$(felhom_guests)" ]]` (the `/etc/felhom-bootstrap`
|
||||
detector) — it fires only when a real Felhom guest exists (whose pinned fingerprint a leaf regen
|
||||
would orphan — the 2026-06-28 incident). `--preserve-state-from` / `--allow-new-leaf` unchanged.
|
||||
- **F6 — foreign `:53` refuse (byo), Viktor's §8 option 1.** In byo the agent's lan_resolver stays
|
||||
off, so any `:53` bind is the owner's. GL-2's informational `:53` line is promoted to an actionable
|
||||
gate that **`die`s with remediation and mutates nothing** — Felhom does not stop/mask/kill a DNS
|
||||
service on a host it doesn't own (the break-glass analog). No stop/mask path, no consent-ack leg
|
||||
(nothing to consent to). `ss` failure/absence degrades to a warn. Appliance mode untouched.
|
||||
- **F1 — uninstall config purge.** The teardown removed `agent.json` but left `${agent_cfg}.bak*`
|
||||
siblings — a GL-6 residue held a **live hub api_key**. Now it removes the config AND every `.bak*`
|
||||
sibling, then the dir; the KEPT/WIPED statement + summary say "config (+ its .bak backups)". Paths
|
||||
logged, contents never. GL-4 Scenario-D parity still green (the GL4-D token set unchanged — the
|
||||
config removal was already covered).
|
||||
The per-customer retrieval passphrase was in cleartext **twice**: the visible `#retrieval-pw` node
|
||||
and baked into the Option-3 debug curl's `X-Retrieval-Password:` header (a copyable command carrying
|
||||
the secret). Now:
|
||||
- `#retrieval-pw` renders a **masked bullet run by default**, with a **Reveal** (`toggleSecret`) and
|
||||
a **copy** (`copySecret`) control; the value lives in `data-secret` (the page's existing reveal
|
||||
model — the value still ships in the DOM, unchanged from before).
|
||||
- The Option-3 command carries a **`<YOUR-RETRIEVAL-PASSWORD>` placeholder** — the secret is never in
|
||||
a copyable command block.
|
||||
- A **zero-secret-in-DOM** reveal-on-demand fetch is a deliberate future follow-up, explicitly NOT
|
||||
scoped here (kept the change tight and reviewable).
|
||||
|
||||
### Tests (harness `hostinstall-mode-harness.sh`, extended)
|
||||
### Part 2 — interactive install-command generator
|
||||
|
||||
Static (anywhere): **GL8-F3** (guard uses `felhom_guests`, not `pct list`), **GL8-F6** (byo `:53`
|
||||
gate refuses+instructs; no `systemctl stop/mask` / `kill` of any `:53`/dnsmasq/resolv service
|
||||
anywhere), **GL8-F1** (`${agent_cfg}.bak*` removal present) + **GL8-F1b** (behavioural: the exact
|
||||
glob-removal clears `agent.json` + three `.bak*` + the emptied dir). PVE tier on felhom-pve:
|
||||
**GL8-F6 live** — the byo preflight **actually refused at `:53`** (felhom-pve's leftover dnsmasq is
|
||||
bound — the exact GL-6 F6 scenario) with the remediation + `PRE-FLIGHT FAIL (exit 1)`; and the
|
||||
free-`:53` arm (ss shimmed empty) passes the gate. **36/36 on felhom-pve, 0 failed**; shellcheck
|
||||
clean; GL-2 Scenario-A + GL-4 Scenario-D regression green.
|
||||
The three hard-coded install `<code>` blocks became a **client-side builder** (vanilla JS — no
|
||||
framework, CDN, or network call) that assembles a live-updating command from form controls:
|
||||
- Emits **only real host-install v1.12.0 flags** in a **download-then-run** shape (never `curl | bash`).
|
||||
- CustomerID prefilled from the server (`pageData.ScriptVersion` + `data-customer-id`); a **byo**
|
||||
selection **requires** `--cores`/`--memory` (client-side enforcement via `.gen-req` + a `gen-msg`
|
||||
prompt); caps/mode are placeholders, never silent defaults.
|
||||
- **Graceful JS-off static fallback**: the Option-1/2 code nodes retain a
|
||||
`--customer-id … --mode <appliance|byo>` command.
|
||||
- **Curated control surface** (mode/cores/memory/vmid/node/acl-storages/operator-pubkey-file/
|
||||
preserve-state-from + skip-provision/dry-run/preflight-only/allow-new-leaf). The **seven
|
||||
dangerous/operator-only flags** (`--force`, `--rotate-recovery`, `--enable-oob`, `--remove-golden`,
|
||||
`--uninstall`, `--adopt-pool`, `--rescope-acl`) are **never offered as controls**.
|
||||
|
||||
**Red-proofs (mutate scratch copy → invariant fails → discard):** RP-F3 (revert to `pct list`) →
|
||||
GL8-F3 FAILS; RP-F6 (drop the `:53` die) → GL8-F6 FAILS; RP-F1 (narrow to `agent.json` only) →
|
||||
GL8-F1 FAILS. All confirmed.
|
||||
`configs.go`: `const hostInstallVersion = "1.12.0"`; `pageData.ScriptVersion` added + populated.
|
||||
`style.css`: `.gen-controls`/`.gen-radios`/`.gen-radio`/`.gen-check(s)`/number-inputs/`.gen-msg`
|
||||
(dark palette, 2px radius).
|
||||
|
||||
### Judgment calls
|
||||
### Part 3 — tests (green gate passed on committed source)
|
||||
|
||||
- F3 behavioural runtime is impractical to isolate (the guard is inline in step 5, gated on the real
|
||||
state dir's leaf; felhom-pve has BOTH a leaf and a Felhom guest) — validated grep-level + red-proof;
|
||||
the true behavioural proof (fresh byo install on a populated non-Felhom host proceeds without
|
||||
`--allow-new-leaf`) folds into GL-7's Peti install.
|
||||
- F6 uses the existing `ss -H -ltnup` (UDP+TCP) invocation — catches a UDP-only resolver (dnsmasq)
|
||||
too, not just TCP.
|
||||
- F1 is surgical (`${agent_cfg}` + `${agent_cfg}.bak*`, then `rmdir`), not `rm -rf` the dir — respects
|
||||
a non-default `-config` path that might share a directory.
|
||||
- **`TestTemplates_PassphraseHardened`** — the secret is NOT in the Option-3 command (placeholder is),
|
||||
`#retrieval-pw` is masked-by-default (bullet-entity run), `data-secret` populated, reveal+copy
|
||||
controls present. **Red-proof:** revert the Option-3 block to `{{.Config.RetrievalPassword}}` → the
|
||||
"not-in-command" / masked assertions fail.
|
||||
- **`TestTemplates_InstallGenerator`** — every curated control id renders, script version +
|
||||
`data-customer-id` present, the JS-off static fallback command present, and **none of the seven
|
||||
excluded flags appear anywhere on the page**.
|
||||
- `go build ./... && go vet ./... && go test ./internal/web/` → **ok** on the committed source (`844fbfa7`).
|
||||
|
||||
### NOT live-validated (folds into GL-7 / a demo re-run)
|
||||
### Part 4 — deploy (GitOps)
|
||||
|
||||
The real populated-host byo install proceeding without `--allow-new-leaf` (F3); the real foreign-`:53`
|
||||
refusal on Peti's box (F6 — proven read-only on felhom-pve here); the real uninstall purging a `.bak`
|
||||
(F1 — behaviourally unit-tested + grep-verified). All fold into GL-7's Peti day-0 as the live proof.
|
||||
Built `felhom-hub:0.36.0` on 180 from the pushed source, bumped `manifests/hub.yaml` (0.35.0→0.36.0),
|
||||
ArgoCD hard-refresh + sync → Synced/Healthy, `deploy/hub` rolled out, live image + startup log
|
||||
confirmed. **Gotcha caught:** the first build produced a stale image because the Part-1/Part-2 commits
|
||||
were committed locally but not pushed before building (`build.sh` pulls origin) — fixed by pushing
|
||||
first, then rebuilding.
|
||||
|
||||
### Findings status + what's next
|
||||
### Part 5 — tester agreement + closeout
|
||||
|
||||
F1/F3/F6 → **FIXED (v1.12.0)** in the GL-6 ledger + GO-LIVE-PACKAGE. **Still open (non-blocking):**
|
||||
F2 (per-drive `mnt-*.mount` units survive uninstall), F7 (pool re-assert bring-up-only, no continuous
|
||||
heal — `--adopt-pool` repairs). **GL-7 is unblocked** — Peti's day-0 command needs no `--allow-new-leaf`
|
||||
workaround. **Next: GL-7** (Peti day-0 runbook + tester agreement).
|
||||
`documentation/pilot/PETI-tester-agreement.md` written: the BYO trust boundary (break-glass/OOB/WG
|
||||
all OFF, non-root agent, pool-scoped token, TLS+per-guest-token local API), the honest limitations
|
||||
(no offsite backup yet; physically-removable-drive caveat F2; pool-reassert-bring-up-only F7;
|
||||
`:53`-must-be-free F6), exit rights (uninstall keeps drives/data/hub-record), Peti's box facts
|
||||
(80c/128 GB → caps **12/32768**, `sajatfelhom.hu` tunnel re-point), the byo install command, and the
|
||||
onboarding sequence (preflight → install → **G9 password-set** → tunnel → **backup-green**).
|
||||
GO-LIVE-PACKAGE updated: GL-7 ✔; **G9/G10 → done**, **G11 → done** (with the one open pilot question
|
||||
folded into the agreement's pre-install checklist); decision log + open-questions updated.
|
||||
|
||||
### The one remaining pilot question (owned by the agreement)
|
||||
|
||||
Peti's **local backup TARGET** — `felhom-pbs` is unreachable from his LAN, so the default
|
||||
`--acl-storages "local local-lvm felhom-pbs"` is wrong for him. Confirm at onboarding **whether his
|
||||
box has a second disk/pool**: if yes it becomes the `--acl-storages` backup target (real local
|
||||
resilience); if no, backups share the guest's pool (degraded — one drive failure loses both, must be
|
||||
stated to him). Changes only the `--acl-storages` value, nothing structural.
|
||||
|
||||
### Verification note
|
||||
|
||||
The hub UI is operator-password-gated — CC cannot log in, so per the repo policy UI changes are
|
||||
verified via the render tests (both green) plus the deploy checks (image/rollout/startup log), not a
|
||||
live browser session.
|
||||
|
||||
Reference in New Issue
Block a user