de34d170d2
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.2 KiB
5.2 KiB
REPORT — agent v0.19.0: bootstrap contract v2 (relay hub passphrase, no host key in guest) (2026-06-11)
Overwrite-latest report. Cumulative history: CHANGELOG.md.
Lockstep two-repo change with felhom-controller v0.40.0. Fixes the onboarding 401 found last
session: the agent used to bake its host hub key into the guest's bootstrap.json, but the hub's
/api/v1/report authenticates a customer-scoped key, so the controller could never report ONLINE.
The agent now bakes a v2 bootstrap carrying only what the controller needs to pull its own
config from the hub — the agent never touches the customer-scoped key or the CF tokens. Validated live
on the demo (guest 9201).
What changed
internal/provision/doc.go—SchemaV1 → SchemaV2 = "felhom.bootstrap/v2".DocCustomerdropsname/domain/email(keepsid);DocHubdropsapi_key/host_id, addsretrieval_password(SECRET);DocLocalAPIunchanged. Byte-compatible with the controller'sinternal/bootstrap.Bootstrap.internal/provision/backhalf.go— renders the v2 Doc; validation now requirescustomer.id+hub.url+hub.retrieval_password(wascustomer.id+customer.domain). Mint / 0600 write /chown 100000:100000/pct set -mp9 …,ro=1unchanged.cmd/felhom-agent/main.go--selftest=provision— new required-hub-passwordflag (the customer's hub retrieval passphrase; the customer must already exist in the hub). Stops bakingcfg.Hub.APIKey/cfg.Hub.HostID.-customer-domain/-name/-emailstill accepted (bring-up may use them) but not baked into the v2 Doc. Success text updated (reboot → controller pulls + merges).configs/build-golden.sh— defaultCONTROLLER_IMAGEbumped off the stale:v0.35.0→:0.40.0(matches the registry's no-vtag convention; latent footgun fixed).
Cross-repo contract checksum-diff (rendered bootstrap.json field set)
Agent renderer output ingested by the controller's json.Unmarshal — every field populated, exact:
| level | fields (agent emits == controller ingests) |
|---|---|
| top | schema, customer, hub, local_api |
| customer | id |
| hub | url, retrieval_password |
| local_api | endpoint, fingerprint, token |
Tests — internal/provision, all green
doc_test.go: pins the v2 key set —customer:[id],hub:[url, retrieval_password](asserts noapi_key/host_id),local_api:[endpoint, fingerprint, token]; schema ==felhom.bootstrap/v2.backhalf_test.go: asserts the rendered bootstrap hasSchema==SchemaV2,hub.url+hub.retrieval_password(no host key), the per-guest token inlocal_api, 0600, chown to100000:100000,pct set -mp9 …,ro=1, and the token never leaks into theResult.
go build ./... && go test ./... green.
Live validation (demo Proxmox felhom-pve)
- Agent v0.19.0 built (
-X main.version=0.19.0), streamed to the demo host (sha256-verified), installed at/usr/local/bin/felhom-agent, daemon restarted (active,felhom-agent 0.19.0). - Golden re-baked with controller
:0.40.0→local:backup/vzdump-lxc-9100-2026_06_11-13_26_45.tar.zst(baked image confirmed:0.40.0). --selftest=provision -customer-id demo-felhom -hub-password <passphrase>→ guest 9201 brought up + bootstrap mounted. The writtenbootstrap.json(0600) hascustomer:[id],hub:[url, retrieval_password](no host key),local_api:{endpoint,fingerprint,token}. ✓- After
pct rebootthe golden's baked unit deployed controller:0.40.0, which pulled its config from the hub (customer-scoped key4b11c0c3…) and merged in the per-guestlocal_api; the guest reported ONLINE at v0.40.0 (no 401). The agent baked no customer secret beyond the per-guest local-API token (passphrase aside — see below). ✓ - 8C invariant unaffected: agent-direct
POST /disks/formaton data-bearing/dev/sdb1→ HTTP 403 pending_signature, disk untouched. ✓
What broke / what's missing
- Finding #1 still open (separate spec): provisioning while the daemon runs leaves the new guest's
local-API channel 401 until
systemctl restart felhom-agent(the running daemon doesn't reload the freshly-minted token). Workaround applied; reproduced. - Passphrase-at-rest: the retrieval passphrase rides in the guest's 0600
bootstrap.json(decision (d) — the agent relays it, never the customer key/CF tokens). Acceptable for now (single-customer scoped; matches the olddocker-setup.sh --hub-passwordmodel); post-onboard hardening (detach mount / rotate) is a separate future item. - Operational note: the UTF-8 (Hungarian) passphrase must be transported byte-exact (base64) to the
provision call; a
kubectl cpstderr line polluted the first capture and produced a garbage passphrase (re-extracted + re-provisioned cleanly).
Versions / artifacts
- Agent v0.19.0 (CHANGELOG updated). Pushed to
main: commite5a1819(code) — this REPORT in the follow-up commit. Lockstep controller v0.40.0 (commit6a594f9). New golden volid above. - No secrets committed (passphrase, customer key, CF tokens, local-api token — out-of-band/redacted).