v0.40.0: bootstrap pull+merge onboarding (controller pulls config from hub)
Fix the onboarding 401: instead of seeding controller.yaml from the agent's HOST hub key (which the hub's customer-scoped /api/v1/report rejects), the controller now PULLS its full controller.yaml from the hub on first boot using the bootstrap's retrieval passphrase (yielding the customer-scoped key) and MERGES in the per-guest local_api block. - internal/bootstrap: contract v1->v2 (customer.id + hub.url + hub.retrieval_password + local_api; drop host key/identity). MaybeIngest gains an injected PullFunc (keeps bootstrap free of the heavy report package), pulls with bounded transient-only retry, merges local_api at YAML-map level (preserves all hub-emitted fields), idempotent + fail-safe + never-crash. - main.go: wire report.PullConfig as the pull adapter (maps ErrHubUnreachable -> ErrPullTransient; auth/not-found permanent). - Lockstep with felhom-agent v0.19.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,35 @@
|
||||
## Changelog
|
||||
|
||||
### v0.40.0 — bootstrap pull+merge onboarding (controller pulls its config from the hub) (2026-06-11)
|
||||
|
||||
Lockstep with `felhom-agent` v0.19.0. Fixes the onboarding 401: a freshly provisioned guest used to
|
||||
seed a "configured" controller.yaml from the agent's **host** hub key, which the hub's `/api/v1/report`
|
||||
(customer-scoped auth) rejects → the controller could never report ONLINE. Now the controller **pulls**
|
||||
its full controller.yaml from the hub on first boot (the hub mints the **customer-scoped** key) and
|
||||
**merges in** the per-guest `local_api` block.
|
||||
|
||||
#### Changed — bootstrap contract `v1 → v2` (`internal/bootstrap`)
|
||||
- `SchemaV1 → SchemaV2 = "felhom.bootstrap/v2"`. `BootstrapCustomer` drops `name`/`domain`/`email` (keeps
|
||||
`id`); `BootstrapHub` drops `api_key`/`host_id`, adds **`retrieval_password`** (SECRET). `local_api`
|
||||
unchanged. A non-v2 schema → setup mode.
|
||||
- **`MaybeIngest(configPath, cfg, logger, pull PullFunc)`** — new injected `pull` arg (decision (b): keeps
|
||||
`bootstrap` from importing the heavy `internal/report` package; wired in `main.go` to `report.PullConfig`).
|
||||
Flow: idempotent (configured → return, **no pull**) → parse + validate v2 → **pull** the hub config with
|
||||
bounded retry (1 + 3 backoff attempts on transient `ErrPullTransient` only; auth/not-found fail fast) →
|
||||
**merge** the per-guest `local_api` at the YAML-map level (preserves every hub-emitted field — assets,
|
||||
CF, backup) → write 0600 atomic → reload. Fail-safe throughout: a hub outage at first boot leaves the
|
||||
guest in setup mode (the manual wizard remains the fallback), never crashes.
|
||||
- New sentinel **`ErrPullTransient`**; `main.go`'s pull adapter maps `report.ErrHubUnreachable` onto it
|
||||
(transient/retryable) and passes auth/not-found through as permanent. Removed `configFromBootstrap`
|
||||
(the host-key-seeding path) and the struct-marshal writer.
|
||||
|
||||
#### Tests (`internal/bootstrap`)
|
||||
- Pull+merge (asserts the merged controller.yaml carries the **customer** key + identity + a preserved
|
||||
unmodeled `assets.source_url` **and** the bootstrap's `local_api`, with **no host key**); idempotency
|
||||
(pull **never invoked** when configured); transient-retry (N attempts then setup); permanent-no-retry;
|
||||
non-v2 schema reject; missing-required reject; malformed/absent. Cross-repo render→ingest round-trip
|
||||
verified against the agent's v2 renderer. `go build ./... && go test ./...` green.
|
||||
|
||||
### v0.39.1 — 8C orphan-template cleanup (source hygiene) (2026-06-11)
|
||||
|
||||
Dead-template removal — no behaviour change. Slice 8C de-privileged the controller and retired the
|
||||
|
||||
Reference in New Issue
Block a user