hub v0.16.0 + host-install v1.1.0: Day-0 artifact manifest + self-install the agent (BUNDLE slice)

Hub (v0.16.0):
- store: ArtifactManifest{agent,golden version+sha256} in hub_settings; Get/SetArtifactManifest.
- handler: GET /api/v1/artifacts/{id} (passphrase auth, mirrors config-retrieve). Unset => 200 empty.
- web: operator UI "Day-0 artifacts" card (POST /configs/artifacts), semver + 64-hex validation.
- artifact_test.go: returned-verbatim / unset-empty / 401 / 404 / store round-trip.

host-install (v1.1.0):
- new step 5/8 agent-install: manifest + git token (config-retrieve) -> fetch binary from Gitea ->
  verify sha256 vs hub manifest (abort on mismatch) -> install non-root felhom-agent user + binary +
  sudoers (visudo -cf) + canonical unit. Idempotent.
- new step 7/8 golden: local fallback else fetch+verify+import from Gitea (--force-gitea-golden).
- agent now runs non-root (privileged.mode sudo), config chowned to the service user.
- README prerequisites trimmed to: install PVE + create customer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 08:38:26 +02:00
parent d65b2f74ea
commit 39ef64e128
11 changed files with 707 additions and 49 deletions
+27
View File
@@ -1,5 +1,32 @@
# Felhom Hub — Changelog
## v0.16.0 — Day-0 artifact manifest (hub-vouched agent + golden checksums) (2026-06-28)
The hub now serves a passphrase-authed **artifact manifest** so the host-bootstrap script can
**fetch-then-verify** the agent binary + golden archive from Gitea before installing them. The hub is
the checksum **trust root** — a different root than Gitea (which only stores the bytes). Part of the
BUNDLE slice that lets a fresh PVE box self-install the agent (no more manual binary/unit step).
- **`store.go`:** `ArtifactManifest{agent_version, agent_sha256, golden_version, golden_sha256}` with
`Get/SetArtifactManifest`, persisted as four discrete rows in the existing `hub_settings` key/value
table (no schema change — same mechanism as the controller-version floor; survives restarts; partial
sets round-trip). Added generic `getSetting/setSetting` helpers.
- **`handler.go`:** new `GET /api/v1/artifacts/{customer_id}` — auth mirrors `handleConfigRetrieve`
EXACTLY (`X-Retrieval-Password`, 404-then-401 order, constant-time compare). Returns
`{"agent":{version,sha256},"golden":{version,sha256}}`. An unset manifest returns **200 with empty
fields** (not an error) so the script falls back to the local golden / fails clearly on a missing
binary. v0.16.0 returns the GLOBAL current set for every customer (per-customer pinning is a future
hook).
- **`configs.go` + `configs.html` (operator UI):** a "Day-0 artifacts — agent & golden" card beside the
managed-update floor controls, with version + sha256 fields for each artifact. `POST /configs/artifacts`
(CSRF-protected); versions validated as bare semver (reusing the floor validator), sha256s as 64-hex,
blank-to-clear.
- **Tests (`artifact_test.go`):** recorded set returned verbatim; unset → 200 empty; wrong/missing
passphrase → 401; unknown customer → 404; store partial-set round-trip. Plus the floor-render smoke
test updated for the new list-page data field.
- Reuse: no new auth path (passphrase, like config-retrieve + host-enroll); no new table (hub_settings);
no new fetch credential downstream (the script reuses the config-retrieve git token).
## v0.15.0 — Phase 2 managed updates: per-customer controller-version floor (2026-06-27)
The operator can now set a **minimum controller version** (FLOOR) — per-customer, defaulting to a