From 83c4f7f7944519aedd156a65d3402be234769b81 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Wed, 1 Jul 2026 06:55:17 +0200 Subject: [PATCH] docs: REPORT for hub v0.28.0 + host-install v1.2.0 (live-validated) Co-Authored-By: Claude Opus 4.8 (1M context) --- REPORT.md | 139 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 61 deletions(-) diff --git a/REPORT.md b/REPORT.md index 51fe549..aeaaaa8 100644 --- a/REPORT.md +++ b/REPORT.md @@ -4,84 +4,101 @@ --- -# REPORT — Hosts page: read-only fleet view (hub v0.27.0) +# REPORT — Configuration-tab move + online setup command + vmid auto-detect (hub v0.28.0 / host-install v1.2.0) -**Date:** 2026-07-01 · **Repo:** `felhom.eu` (`hub/`) · **Audit finding:** F-M1 (resolved) +**Date:** 2026-07-01 · **Repo:** `felhom.eu` (`hub/` + `scripts/` + `manifests/`) -## What & why +Three operator-requested improvements. -The agent enrolls as a **host** and the hub stores rich host state (identity, agent version, guests, -storage targets with SMART, DR/escrow, staleness) and **alerts** on it — but the entire host domain was -**invisible in the GUI** (email-only). This adds a **Hosts** nav section: a fleet **list** + a per-host -**detail** page. **Read-only** (GET only, no host actions/mutation routes) — this surfaces state the -pull/desired-state model demands; it does not reintroduce the inbound control retired in v0.26.0. +## Baselines -## Baseline +hub `v0.27.0` @ `54daaf9` → **`v0.28.0`**; `scripts/felhom-host-install.sh` `v1.1.0` → **`v1.2.0`**. +Trunk-based `main`. Commits: `562c0df` (Parts A/B/C code), `629196c` (hub image manifest), `b77df3c` +(script header version). -hub `v0.26.0` @ `73b3f6a` (clean tree) → **`v0.27.0`**. Trunk-based `main`; commits -`2289fc9` (code) + `f8b8730` (manifest bump). +## Part A — global settings moved to the Configuration tab -## Files +The two **global** cards ("Managed updates — global floor", "Day-0 artifacts — agent & golden") were on +the Customers page; they now live on Configuration. -**Created** -- `hub/internal/web/hosts.go` — `handleHostsList`, `handleHostDetail`, `hostStatus`/`hostStatusClass`/ - `hostStatusLabel`, `parseHostVitals`, `parseHostStorageTargets`, `customerName`. -- `hub/internal/web/hosts_test.go` — 5 tests (incl. the no-secret assertion). -- `hub/internal/web/templates/hosts.html`, `hub/internal/web/templates/host_detail.html`. +- `internal/web/server.go` — `handleConfiguration` supplies `GlobalFloor` + `Artifacts` + `CSRFField`; + the two POST routes moved to `/configuration/global-floor` + `/configuration/artifacts`. +- `internal/web/configs.go` — `handleSetGlobalFloor`/`handleSetArtifacts` redirects `/configs?flash=…` + → `/configuration?flash=…` (5 redirects); `handleConfigList` data trimmed to `{Customers, ActiveNav, + Flash}` (dropped the now-unused `GlobalFloor`/`Artifacts`/`CSRFField`; `globalFloor` still computed for + per-customer effective-floor). +- `templates/configuration.html` — the two cards + their 5 flash banners added. `templates/configs.html` + — cards + flashes removed; back to customer list + "Add Customer". +- `internal/web/render_test.go` — updated: asserts the global cards are **gone** from configs.html and + **present** (with `/configuration/*` actions + a flash) on configuration.html. -**Modified** -- `hub/internal/store/store.go` — **new** `ListGuestsForHost(hostID) ([]Guest, error)` + `scanGuest` - helper over `guestRealitySelectCols` (reality columns only; **omits** `api_key`, `desired_spec_json`). -- `hub/internal/store/host_test.go` — `TestListGuestsForHost`. -- `hub/internal/web/server.go` — `GET /hosts` (+`/hosts/`) and `GET /hosts/{id}` routes (modelled on - the `/apps` pair); new `timeAgoPtr` template helper for `*time.Time`. -- 7 existing templates (`dashboard`, `configs`, `apps`, `config_form`, `configuration`, - `customer_unified`, `app_detail`) — added the `Hosts` nav link (nav is duplicated per page, not a - shared partial). -- `hub/CHANGELOG.md` (newest-on-top), `manifests/hub.yaml` (`:0.26.0` → `:0.27.0`). +## Part B — online setup command + serving fix + passphrase hardening -## Design decisions +- **B1 (`scripts/…`):** `read_passphrase` now reads `read -rsp … < /dev/tty` — the no-echo prompt works + whether the script is a local file or piped to bash. `--passphrase-file` unchanged; passphrase still + never on argv/logs/state. +- **B2 (serving):** `https://felhom.eu/scripts/felhom-host-install.sh` was **404** — the webpage nginx + doc-root is `.../current/website` and git-sync only synced `/website/`. Fixed in + `manifests/webpage.yaml`: added `/scripts/` to the git-sync sparse-checkout + an nginx + `location /scripts/ { root .../current; default_type text/plain; }`. Single source of truth (the repo's + `/scripts`), no duplicated copy. **Now 200** (verified live, serves `SCRIPT_VERSION="1.2.0"`). +- **B3 (`customer_unified.html`):** new **"Option 1: Online install (recommended)"** — download-then-run + (`curl -fsSL … -o … && sudo bash … --customer-id {{.CustomerID}}`) with a copy button; the passphrase is + **not** templated (entered at the prompt). Former local-file command demoted to Option 2, debug curl to + Option 3. Rationale note: fetched over TLS, inspect-before-run — not `curl | sudo bash`. -- **Status badge reuses the alerting threshold.** `hostStatus()` reuses `s.staleThreshold` and the - `HostStalenessChecker`'s bands (stale after the threshold, down at 2×) — one definition, so the GUI - badge agrees with the emails. `nil` last-report → "waiting for first report". -- **No new ingestion path.** Vitals + rich storage (role/state/fill/thin-pool/SMART) are parsed from the - already-stored latest `report_json` (`GetLatestHostReportJSON`); worst-fill from `GetHostStorageTargets`. - Guest counts from `ListGuestsForHost` (per-host accurate). -- **No secrets.** The guests reader never selects `api_key`/`desired_spec_json`; DR/escrow are shown as - presence booleans only — never the opaque blobs. A test asserts the host `api_key` is absent from the - rendered detail body. -- **Nil-safe.** No report / no guests / no storage / no DR all render empty states, never a panic. +## Part C — vmid auto-detect (`scripts/…`) -## Tests — `go build ./... && go vet ./... && go test ./...` all `ok` +- New `VMID_EXPLICIT` flag (set by `--vmid`). The pre-flight guard now determines "in use" against the + **`pct list` + `qm list`** id-set (LXC + VMs share the space) via new helpers `used_vmids` / + `_vmid_in_use` / `next_free_vmid`. +- **explicit `--vmid` + in use** → deterministic: die unless `--force`. +- **default 9201 + in use + no `--force`** → auto-pick the next free id (scan upward) + **confirm** from + `/dev/tty` (`[y/N]`); `die "no free vmid confirmed"` on decline. Never silent. +- **default 9201 + `--force`** → destructive over-provision, no prompt. +- `--vmid` help + `usage()` range updated (and usage now reaches `-h, --help`). -- `TestListGuestsForHost` — none→empty; multiple→vmid-ordered; a foreign-host guest excluded; the - `api_key` column is not surfaced. **PASS** -- `TestHostStatus` — nil→pending, fresh→ok, 45m→stale, 3h→down. **PASS** -- `TestHandleHostsList` — 2 host rows, ONLINE + NO REPORT badges, worst-fill (73%) rendered, no `