Files
felhom.eu/REPORT.md
T
admin 89a2e508a7 scripts: felhom-host-install.sh v1.0.0 — Day-0 host bootstrap (live-validated)
Operator-run Day-0 automation for a fresh PVE host: pveum token -> hub
POST /host-enroll (single secret, option C) -> agent config -> felhom-agent
--selftest=provision -> verify. Idempotent/resumable (state.json), --dry-run,
--resume, --force, --preserve-from; --mode dr is a documented 10D stub.

Live-validated on felhom-pve: authorized wipe of demo guest 9201 + full
re-provision from the golden -> controller config-pull + public tunnel HTTP 200
+ host-report of 9201; idempotent --resume no-op. Found+fixed one ordering bug
during the run (token ACL must be applied AFTER token rotation — token-remove
purges the ACL).

Adds scripts/README.md (operator runbook) + scripts/CHANGELOG.md; REPORT
overwritten; spike doc pointer added. No service code changed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TtXesNa2LGbMmE4DNL6SE7
2026-06-26 20:04:25 +02:00

102 lines
7.2 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).
---
## Day-0 host-bootstrap script — built + live wipe-and-provision test — 2026-06-26
**Task:** Deliver `scripts/felhom-host-install.sh` (operator-run Day-0 bootstrap) and validate it with
an **operator-authorized destructive live test**: wipe demo guest 9201 and re-provision it from scratch
via the script. Single repo (`felhom.eu`, new `scripts/`).
### Confirmed baselines
- hub `1539746` v0.14.0 (`/host-enroll` option C, just shipped) · felhom-agent `4725396` v0.41.0
(`--selftest=provision`) · felhom-controller `e0cf78b` v0.84.0.
- Live host `felhom-pve` (192.168.0.162): PVE 9.2.2, node `demo-felhom`, agent v0.41.0 at
`/root/.config/felhom-agent/agent.json` (systemd unit `-config` path — **not** `/etc/felhom-agent/`),
golden `local:backup/vzdump-lxc-9100-2026_06_23-16_38_49.tar.zst` (rootfs 32 / docker 16 / sysdata 8 GiB base).
### Deliverable
- `scripts/felhom-host-install.sh` **v1.0.0** — 7 idempotent/resumable steps (pre-flight → pveum token →
grows → host-enroll → agent config → provision → verify), single-secret (passphrase no-echo/0600 file),
`--dry-run`/`--resume`/`--force`/`--preserve-from`, `--mode dr` documented stub.
- `scripts/README.md` (operator runbook), `scripts/CHANGELOG.md` (v1.0.0).
- `documentation/audits/SPIKE-…-2026-06-26.md` — pointer added (script ships the gap inventory).
- `bash -n` passes (Windows + host). **shellcheck unavailable** on dev box, build box, and felhom-pve — not run.
### Live supervised test (felhom-pve, customer `demo-felhom`, authorized destructive)
Passphrase sourced operator-side from the hub DB into a 0600 file on the host (never printed); deleted
(shredded) after. Methods noted per check.
**A. Pre-wipe snapshot:** saved `pct config 9201`, controller `0.84.0 Up (healthy)`, `agent.json.bak`
(1574 B), token list. Confirmed `demo-felhom` host exists in hub (`demo-felhom-01`, plus two stale
`dr-drill-host*` cruft rows — `demo-felhom-01` has today's `updated_at`, so `GetHostByCustomer` returns it).
**B. Wipe (authorized):** `pct stop 9201` + `pct destroy 9201 --purge` (all 3 volumes removed); moved
`agent.json` + cleared state to simulate a fresh host.
**C. Dry-run:** every mutating command printed correctly — pveum role-modify (16 privs), **both** ACL
lines, token rotate, enroll curl, agent.json write, provision call. Pre-flight green (golden auto-found,
passphrase valid, vmid free).
**D. Real run — found + fixed a bug, then SUCCESS:**
- **Bug found live:** first run failed the post-config agent selftest with `HTTP 403 … missing privilege
Sys.Audit`. Root cause: the script applied the **token ACL before rotating the token**, and
`pveum user token remove` **purges the token's ACL** — so the new token had no ACL. **Fix:** apply both
ACL grants **after** the token is (re)created. (`scripts/felhom-host-install.sh` step 2.)
- **Re-run green:** token rotated (old config token failed selftest → rotate → ACL → selftest **passed**);
`host-enroll` → **200 REUSE** of `demo-felhom-01` (idempotency confirmed); agent.json written 0600 +
read-only selftest passed + daemon restarted; provision front-half bring-up (1m11s) + back-half bootstrap
mount `mp9` → `/etc/felhom-bootstrap` + local-api token minted; guest **9201 running, onboot:1**, layout
rootfs 32G / mp0 200G / mp1 50G / mp8 bind — exactly the standard appliance.
**E. Verify (method per check):**
| Check | Method | Result |
|-------|--------|--------|
| guest running + onboot:1 + volumes | `pct status/config` | ✅ running, onboot 1, 32/200/50G + mp8 bind |
| controller healthy | `pct exec docker ps` | ✅ `Up (healthy)`, image `felhom-controller:0.77.0` (from golden) |
| **controller pulled config from hub** | controller logs (`pct exec`) | ✅ `bootstrap: pulled config from hub for demo-felhom, merged local_api … coming up configured`; `Hub connectivity … HTTP 200` |
| serving | `pct exec curl :80` | ✅ HTTP 301 (traefik) |
| **Cloudflare tunnel connected** | cloudflared logs | ✅ 4 `Registered tunnel connection` (vie06, bud01) |
| **public URL** | curl from build box (public DNS) | ✅ `felhom.demo-felhom.eu → HTTP 200` via Cloudflare `188.114.97.11` (ingress `*.demo-felhom.eu → traefik`) |
| real dashboard | curl public | ✅ `<title>Vezérlőpult — Felhom.eu</title>` |
| **host-report includes guest 9201** | hub sqlite | ✅ `demo-felhom-01/9201` status `running`, last_seen current |
| storage plane (functional touch) | `pct exec ls` | ✅ `/mnt/felhom-drives` bound, `felhom-flash` + `felhom-usb` visible (data intact); local-api drive plane reachable |
> Public URL returned `000` from felhom-pve and the Windows dev box — split-horizon (the agent's LAN
> resolver points `*.demo-felhom.eu` at the guest's LAN IP, which serves `:80` not `:443`). Through the
> Cloudflare edge (build box) it is **200**. Not a provision fault.
**F. Idempotency:** plain re-run **refused** to clobber the live 9201 (exit 1, "pass `--force`");
`--resume` re-run **skipped** token/grows/enroll/agent_config/provision → verify-only **no-op**, token
**not** rotated (selftest still passes), guest **not** re-provisioned.
**G. Cleanup:** passphrase file shredded; `agent.json.bak` + snapshot retained; final state — 9201
running, host_id `demo-felhom-01`, agent active.
### pveum / token automation result
Role `FelhomAgent` normalised to the authoritative **16 privileges** (incl. `Sys.Audit`, `SDN.Use`); user
`felhom-agent@pve`; privsep token rotated; **both** ACL grants (user **and** token) applied **after** token
creation. Read-only `felhom-agent --selftest` passed against the live API with the minted token.
### Observations (out-of-scope — not acted on)
1. **`-hub-password` on argv:** the agent's only input for the retrieval passphrase during provision is the
`-hub-password` flag → briefly visible in `ps` during `--selftest=provision`. **Candidate agent
follow-up:** accept it via env/stdin. (Not fixed here — would modify the agent.)
2. **Golden freshness:** the golden bakes controller **0.77.0**; the freshly provisioned demo runs 0.77.0
(pre-wipe was 0.84.0, and the v0.78.0 drive-register stable-path fix is newer than the golden). Rebuild
the golden at the current controller for parity; full drive **re-enroll** via the UI needs controller
≥0.78.0. The Day-0 script itself is version-agnostic.
3. **Multi-host cruft:** `demo-felhom` carries two stale `dr-drill-host*` rows. `GetHostByCustomer`
(most-recent-updated) correctly returned `demo-felhom-01` here, but the Day-0 invariant is one host per
customer — drill artifacts should be cleaned up (left untouched; out of scope).
4. **Serving + binary/golden delivery:** the script lives in `scripts/`; serving it at
`https://felhom.eu/scripts/…`, plus agent-binary and golden central delivery, are documented
productionization hooks (the test used the local binary + golden).
### Commits
- `<this commit>` — script v1.0.0 + scripts/README + scripts/CHANGELOG + spike pointer + REPORT.
No secrets in any committed file. PVE-token secret + host api_key live only in the host's 0600 agent config.