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:
+6
-7
@@ -3,13 +3,12 @@
|
|||||||
> Created with the REUSE.md rollout (2026-07-03). Authoritative history: `hub/CHANGELOG.md` (hub),
|
> 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`.
|
> `website/CHANGELOG.md`, `scripts/CHANGELOG.md`; end-of-task detail in `REPORT.md`.
|
||||||
|
|
||||||
- **2026-07-03 — SPIKE A1 (pool-membership read) COMPLETE** —
|
- **2026-07-03 — A1 CLOSED (host-install v1.9.0 + agent v0.62.0)** — `Pool.Audit` added to
|
||||||
`documentation/audits/SPIKE-a1-pool-membership-read-2026-07-03.md`. **The A1 impl spec is now
|
`FelhomAgentGuest`; the agent's stale-lock reaper now intersects its scan with
|
||||||
unblocked** and must cite it: winner = `Pool.Audit` added to `FelhomAgentGuest`
|
`GET /pools/felhom` membership (fail-safe skip on read failure). Spike:
|
||||||
(`felhom-host-install.sh` L183) + `GET /pools/felhom` cross-check in
|
`documentation/audits/SPIKE-a1-pool-membership-read-2026-07-03.md`. Deploy order on any box:
|
||||||
`staleLockController.Guests()` (fail-safe skip). Enumeration proven pool-filtered under the
|
rescope ACL first, then the agent. STILL PENDING (operator): rotate + de-git the 3 committed
|
||||||
production token; fix is defense-in-depth for the broad-token precondition. Appendix: operator
|
secrets in `manifests/felhom.secret.yaml` (spike appendix; REUSE §2 errata added).
|
||||||
follow-up to rotate + de-git the 3 committed secrets in `manifests/felhom.secret.yaml`.
|
|
||||||
- **2026-07-03 — hub v0.31.0 LIVE** (critical-severity ingest fix + UI badges; ArgoCD Synced/Healthy;
|
- **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).
|
live probe: critical event stored + operator email delivered).
|
||||||
- **2026-07-03 — Felhom skills exist**: `skills/{felhom-build-deploy,felhom-ui-design,felhom-testing}`,
|
- **2026-07-03 — Felhom skills exist**: `skills/{felhom-build-deploy,felhom-ui-design,felhom-testing}`,
|
||||||
|
|||||||
@@ -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. |
|
| 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). |
|
| 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. |
|
| 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. |
|
| 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
|
## 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` |
|
| `(*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 |
|
| 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 |
|
| `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 3–5) |
|
| `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 3–5) |
|
||||||
| `:latest` image tag in manifests | Re-push doesn't change the manifest → no redeploy; Synced/Rollback misreport. | Pinned version tag, bumped per deploy |
|
| `: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) |
|
| grep/regex hunting emoji in website HTML | Windows grep false-negatives multibyte emoji (proven in D0). | `python scripts/site_gates.py` (codepoint-range check) |
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
# Felhom scripts — Changelog
|
# 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)
|
## 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
|
Findings doc `documentation/audits/SPIKE-a1-pool-membership-read-2026-07-03.md`. Live-probed on
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_VERSION="1.8.0"
|
SCRIPT_VERSION="1.9.0"
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Logging (mirrors felhom-controller/scripts/docker-setup.sh)
|
# 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
|
# 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
|
# 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.
|
# 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_GUEST="FelhomAgentGuest"
|
||||||
PVE_ROLE_STORE="FelhomAgentStore"
|
PVE_ROLE_STORE="FelhomAgentStore"
|
||||||
PVE_ROLE_BASE="FelhomAgentBase"
|
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_STORE="Datastore.Allocate Datastore.AllocateSpace"
|
||||||
PVE_PRIVS_BASE="Sys.Audit SDN.Use Datastore.Audit"
|
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
|
# Storages the agent reads/writes (archive+dump=local, restore=local-lvm, offsite DR=felhom-pbs). The
|
||||||
|
|||||||
Reference in New Issue
Block a user