felhom-host-install.sh v1.9.0 — Pool.Audit in FelhomAgentGuest (audit A1)

Companion to felhom-agent v0.62.0: the stale-lock reaper reads GET /pools/felhom
as its ownership registry. Pool.Allocate does NOT satisfy the read (spike T2).
Idempotent upgrade via --rescope-acl (_ensure_role modifies to the exact set).
Rescope FIRST, agent second. + REUSE §2 errata: gitea-creds is NOT out-of-band
(committed in felhom.secret.yaml, live-consumed — rotation pending).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-03 13:26:26 +02:00
parent 5e3dfcb714
commit 639a57ee8b
4 changed files with 26 additions and 11 deletions
+6 -7
View File
@@ -3,13 +3,12 @@
> Created with the REUSE.md rollout (2026-07-03). Authoritative history: `hub/CHANGELOG.md` (hub),
> `website/CHANGELOG.md`, `scripts/CHANGELOG.md`; end-of-task detail in `REPORT.md`.
- **2026-07-03 — SPIKE A1 (pool-membership read) COMPLETE** —
`documentation/audits/SPIKE-a1-pool-membership-read-2026-07-03.md`. **The A1 impl spec is now
unblocked** and must cite it: winner = `Pool.Audit` added to `FelhomAgentGuest`
(`felhom-host-install.sh` L183) + `GET /pools/felhom` cross-check in
`staleLockController.Guests()` (fail-safe skip). Enumeration proven pool-filtered under the
production token; fix is defense-in-depth for the broad-token precondition. Appendix: operator
follow-up to rotate + de-git the 3 committed secrets in `manifests/felhom.secret.yaml`.
- **2026-07-03 — A1 CLOSED (host-install v1.9.0 + agent v0.62.0)** — `Pool.Audit` added to
`FelhomAgentGuest`; the agent's stale-lock reaper now intersects its scan with
`GET /pools/felhom` membership (fail-safe skip on read failure). Spike:
`documentation/audits/SPIKE-a1-pool-membership-read-2026-07-03.md`. Deploy order on any box:
rescope ACL first, then the agent. STILL PENDING (operator): rotate + de-git the 3 committed
secrets in `manifests/felhom.secret.yaml` (spike appendix; REUSE §2 errata added).
- **2026-07-03 — hub v0.31.0 LIVE** (critical-severity ingest fix + UI badges; ArgoCD Synced/Healthy;
live probe: critical event stored + operator email delivered).
- **2026-07-03 — Felhom skills exist**: `skills/{felhom-build-deploy,felhom-ui-design,felhom-testing}`,
+2 -2
View File
@@ -99,7 +99,7 @@
| Gate script | scripts/site_gates.py | Byte-level mechanical gates (BOM, emoji codepoint ranges, nav/footer diff, analytics, banned tokens, cache-bust); run `python scripts/site_gates.py` after ANY website change; non-zero exit on failure. |
| Fetch-validate-install (shell) | scripts/felhom-host-install.sh `step_agent_install` (~L1108) | `fetch_raw` to mktemp → syntax-check (`bash -n`) → `install -m0755 -o root -g root` → only then activate; guarded-mkfs wrapper installed BEFORE the sudoers that references it (ordering is the safety property). All mutations through `run()` (dry-run aware). |
| Website deploy (manifest) | manifests/webpage.yaml | git-sync sidecar (sparse-checkout `/website/` + `/scripts/`, `--link=current`) + init container waits for first sync; nginx serves `current/website`; push to main = deployed, no image build. |
| Secret handling (manifest) | manifests/hub.yaml (env, ~L142) | Secrets via `secretKeyRef` to OUT-OF-BAND secrets (`resend-api`, `gitea-creds`) created per documentation/runbooks/secrets.md — never inline stringData (see §3). |
| Secret handling (manifest) | manifests/hub.yaml (env, ~L142) | Secrets via `secretKeyRef` to OUT-OF-BAND secrets created per documentation/runbooks/secrets.md — never inline stringData (see §3). ERRATA (2026-07-03): only `resend-api` is truly out-of-band today; `gitea-creds` is COMMITTED in manifests/felhom.secret.yaml AND live-consumed by hub.yaml — rotation + de-git is a pending operator task (spike SPIKE-a1 appendix). |
| Hub deploy (GitOps) | manifests/hub.yaml `image:` (~L129) | Pinned explicit tag, bumped in git, deliberate ArgoCD sync (auto-sync OFF). Code push alone deploys nothing. |
## 3. Dangerous lookalikes — do NOT reuse
@@ -109,7 +109,7 @@
| `(*Handler).handleNotify` + `formatNotificationEmail` + `sendResendEmail` (hub/internal/api/handler.go ~L1289/1624/1589) | Legacy pre-dispatcher notification trio: no cooldowns, no operator channel, no allowedEventTypes gate, duplicate Hungarian formatter. Controller path is FROZEN until slice-10 cutover. | `POST /api/v1/event``Dispatcher.ProcessEvent` + `notify.Format*Email` |
| Severity `"critical"` POSTed to a PRE-v0.31.0 hub | Fixed in hub v0.31.0 (`handleEvent` now accepts critical). Older hubs coerce `critical``"info"`, which never notifies — silent alert loss. Case-variants (`"Critical"`) still coerce on every version. | Against an old hub send `warning`/`error`; otherwise lowercase `critical` is safe |
| `compareVersions` for anything security-ish (hub/internal/web/server.go ~L571) | Returns 0 (equal) on unparseable input — a garbage version passes a floor check. `gitea.compareSemver` behaves differently (lexical fallback). | Validate input with `normalizeFloorInput` first; then compareVersions is safe |
| Inline `stringData` secrets à la manifests/felhom.secret.yaml | Commits real credentials to git (healthchecks superuser pw, umami APP_SECRET/POSTGRES_PASSWORD still live there). | Out-of-band `kubectl create secret` + `secretKeyRef` (hub.yaml resend-api pattern; runbook documentation/runbooks/secrets.md) |
| Inline `stringData` secrets à la manifests/felhom.secret.yaml | Commits real credentials to git (healthchecks superuser pw, umami APP_SECRET/POSTGRES_PASSWORD, gitea-creds admin password still live there). | Out-of-band `kubectl create secret` + `secretKeyRef` (hub.yaml resend-api pattern; runbook documentation/runbooks/secrets.md) |
| `kubectl apply` / `kubectl set image` on manifests/ | ArgoCD app `felhom` reverts drift on next sync; live state lies about git. | Edit manifest in git → push → ArgoCD sync (CLAUDE.md steps 35) |
| `:latest` image tag in manifests | Re-push doesn't change the manifest → no redeploy; Synced/Rollback misreport. | Pinned version tag, bumped per deploy |
| grep/regex hunting emoji in website HTML | Windows grep false-negatives multibyte emoji (proven in D0). | `python scripts/site_gates.py` (codepoint-range check) |
+12
View File
@@ -1,5 +1,17 @@
# Felhom scripts — Changelog
## felhom-host-install.sh v1.9.0 — Pool.Audit for the stale-lock reaper (A1) (2026-07-03)
Companion to felhom-agent v0.62.0 (audit A1: pool-membership ownership check). `PVE_PRIVS_GUEST`
gains **`Pool.Audit`** (12 → 13 privs, granted at `/pool/felhom` via the existing FelhomAgentGuest
role) so the agent can read `GET /pools/felhom` — its stale-lock reaper's ownership registry.
`Pool.Allocate` does NOT satisfy the read (spike SPIKE-a1-pool-membership-read-2026-07-03 T2).
No structural change: `_ensure_role` already `role modify`s to the exact priv set, so re-running
`--rescope-acl` (or a fresh install) upgrades an existing box idempotently; `remove_scoped_acl`
deletes by role name and needs nothing. **Deploy order on a live box: rescope FIRST, then deploy
agent v0.62.0** — the added read priv is harmless to an older agent, while the new agent on an old
ACL fail-safes its reaper (skips) and reports `pve:pool-read` degraded until the rescope lands.
## docs — SPIKE: A1 pool-membership read for the stale-lock reaper (2026-07-03)
Findings doc `documentation/audits/SPIKE-a1-pool-membership-read-2026-07-03.md`. Live-probed on
+6 -2
View File
@@ -104,7 +104,7 @@
set -euo pipefail
SCRIPT_VERSION="1.8.0"
SCRIPT_VERSION="1.9.0"
#-------------------------------------------------------------------------------
# Logging (mirrors felhom-controller/scripts/docker-setup.sh)
@@ -177,10 +177,14 @@ PVE_ROLE="FelhomAgent" # the PRE-3b single broad role (removed on rescope/unin
# dynamically-enrolled removable drives (felhom-usb/felhom-flash) it observes but never registers — a
# per-storage Audit grant hid them → false "drive detached" alerts. Audit is read-only, so box-wide Audit
# keeps WRITE containment (Allocate/AllocateSpace stay per-storage). Only Sys.Audit/SDN.Use/Datastore.Audit box-wide.
# `Pool.Audit` (v1.9.0, audit A1): the agent's stale-lock reaper reads GET /pools/felhom as its
# ownership registry (agent v0.62.0+); without it the reaper fail-safes (skips) and reports the
# `pve:pool-read` capability degraded. NOTE: Pool.Allocate does NOT satisfy the read — the spike
# (SPIKE-a1-pool-membership-read-2026-07-03 T2) 403'd with Allocate granted; Audit is required.
PVE_ROLE_GUEST="FelhomAgentGuest"
PVE_ROLE_STORE="FelhomAgentStore"
PVE_ROLE_BASE="FelhomAgentBase"
PVE_PRIVS_GUEST="VM.Allocate VM.Audit VM.Config.Disk VM.Config.CPU VM.Config.Memory VM.Config.Network VM.Config.Options VM.PowerMgmt VM.Snapshot VM.Snapshot.Rollback VM.Backup Pool.Allocate"
PVE_PRIVS_GUEST="VM.Allocate VM.Audit VM.Config.Disk VM.Config.CPU VM.Config.Memory VM.Config.Network VM.Config.Options VM.PowerMgmt VM.Snapshot VM.Snapshot.Rollback VM.Backup Pool.Allocate Pool.Audit"
PVE_PRIVS_STORE="Datastore.Allocate Datastore.AllocateSpace"
PVE_PRIVS_BASE="Sys.Audit SDN.Use Datastore.Audit"
# Storages the agent reads/writes (archive+dump=local, restore=local-lvm, offsite DR=felhom-pbs). The