docs: REPORT + CONTEXT for v0.95.0 (Impl-2b raw enrollment; live-validated)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+14
-1
@@ -7,7 +7,20 @@
|
|||||||
>
|
>
|
||||||
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
|
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
|
||||||
|
|
||||||
Last updated: 2026-06-30 (v0.94.0 — pull-based config-refresh; hub v0.26.0 retires the inbound controls)
|
Last updated: 2026-07-01 (v0.95.0 — enrollment wizards use the raw-device scan; end-to-end raw enrollment live)
|
||||||
|
|
||||||
|
> **2026-07-01 — v0.95.0 (deployed on 9201): Impl-2b — raw-drive enrollment wizards.** Both enrollment
|
||||||
|
> wizards (`storage_init.html` / `storage_attach.html`) now fetch candidates from the agent's raw-device
|
||||||
|
> scan `GET /api/disks/candidates` (proxy `agentDiskCandidatesHandler` → agent Impl-2a) instead of the
|
||||||
|
> `Observe()`-based `/api/disks`, so a brand-new (non-PVE-storage) drive is discoverable + enrollable.
|
||||||
|
> `agentapi.ListCandidates` + `resolveEnrollUUID` (a raw candidate isn't in `/disks` → resolve its
|
||||||
|
> fs-UUID from the scan). Live end-to-end on felhom-pve: raw `/dev/sdd` (SD card) → wizard → format (Impl-1
|
||||||
|
> guard) / attach → mount → bind → intent → guest-bind → **"Aktív"** in the UI (3rd drive, no false
|
||||||
|
> detach). Needed a chain of AGENT fixes (v0.56–0.58: durableIDForMount / ReassertGuestBinds / HostReader
|
||||||
|
> wiring / /disks GuestPath+BoundUnderParent for registry rows) — the whole enroll+tracking machinery had
|
||||||
|
> assumed a PVE-storage drive. **Follow-ups:** `runStorageInit` should poll the agent's detached-format
|
||||||
|
> status (`/disks/format/status`) so a SLOW-device format completes in one flow (pre-existing); Impl-3
|
||||||
|
> shared-box operator format gate.
|
||||||
|
|
||||||
> **2026-06-30 — v0.94.0 (deployed on 9201): pull-based config-refresh.** The hub report ACK now also
|
> **2026-06-30 — v0.94.0 (deployed on 9201): pull-based config-refresh.** The hub report ACK now also
|
||||||
> carries a per-customer **`config_version`** (hub v0.26.0; a stored counter bumped on every config save).
|
> carries a per-customer **`config_version`** (hub v0.26.0; a stored counter bumped on every config save).
|
||||||
|
|||||||
@@ -1,60 +1,62 @@
|
|||||||
# REPORT — pull-based config-refresh (controller v0.94.0)
|
# REPORT — Impl-2b: enrollment wizards → `/disks/candidates` + end-to-end raw enrollment (controller v0.95.0)
|
||||||
|
|
||||||
**Date:** 2026-06-30
|
**Date:** 2026-07-01 · **Repo:** `felhom-controller` · **Class:** Risky/supervised (first live raw
|
||||||
**Task:** retire the hub's inbound controls + add pull-based config delivery. Controller half (companion:
|
enrollment runs the Impl-1 guarded mkfs inside a real enroll). Companion agent work: v0.55.0→**v0.58.0**.
|
||||||
hub v0.26.0). The controller re-pulls `controller.yaml` + self-restarts when the report ACK's
|
|
||||||
`config_version` changes — the box-pulls-config replacement for the retired "Push Config".
|
|
||||||
|
|
||||||
## Baselines
|
Final piece of the drive-enrollment series: both enrollment wizards now source candidates from the
|
||||||
- felhom-controller `464b14f` (v0.93.0) → `419d3d0` (**v0.94.0**, live on guest 9201 / felhom-pve).
|
agent's raw-device scan, so a brand-new (non-PVE-storage) drive is discoverable + enrollable end-to-end.
|
||||||
- Companion hub v0.26.0 live on k3s (advertises `config_version` in the report ACK).
|
|
||||||
|
|
||||||
## What changed
|
## 1. Baseline → target
|
||||||
- `internal/report/pusher.go`: `PushResponse.ConfigVersion` (`json:"config_version"`).
|
|
||||||
- `internal/report/config_refresh.go` (NEW): `ConfigRefresher.Reconcile` — testable reconcile with
|
|
||||||
injected side effects. change → Refresh→Record→Restart; first-run records baseline (no restart);
|
|
||||||
unchanged = no-op; failed pull keeps config (no record/restart, retries); record-before-restart.
|
|
||||||
- `internal/bootstrap/bootstrap.go`: `RefreshConfig` — re-pull + re-write controller.yaml, re-merging
|
|
||||||
local_api from bootstrap.json (reuses pullWithRetry/mergeLocalAPI/writeFileAtomic). Overwrites
|
|
||||||
controller.yaml (hub = source of truth); never touches settings.json; fail-safe.
|
|
||||||
- `internal/settings/settings.go`: `applied_config_version` + Get/Set.
|
|
||||||
- `internal/api/selfrestart.go`: exported `GracefulSelfRestart` (reuse; the unexported one calls it).
|
|
||||||
- `cmd/controller/main.go`: wired `ConfigRefresher` into `OnPushResponse` beside the floor reconcile.
|
|
||||||
|
|
||||||
## Tests & green gate
|
felhom-controller `6ce61e8` (v0.94.0) → **v0.95.0**. Agent v0.55.0 → **v0.58.0** (separate repo — the
|
||||||
- `go build ./... && go vet ./... && go test ./...` — **all green**.
|
raw-drive path needed agent fixes the spec's "N/A agent" premise didn't anticipate; see agent REPORT).
|
||||||
- `Reconcile`: change→refresh+record+restart; **same-version NO-OP red-proof**; baseline-no-restart;
|
|
||||||
failed-pull no-record/no-restart; zero-version no-op; record-fail skips restart.
|
|
||||||
- `RefreshConfig`: re-pull overwrites + re-merges local_api; failed pull leaves config unchanged;
|
|
||||||
absent bootstrap errors without writing.
|
|
||||||
- **Red-proof (no-restart-storm):** removing the `ackVersion == applied` guard makes
|
|
||||||
`TestReconcile_SameVersion_NoOp` FAIL (`refreshes=1/restarts=1, want 0`) — proving the guard prevents
|
|
||||||
a restart-every-report loop. Restored → green.
|
|
||||||
|
|
||||||
## Live validation (guest 9201 on felhom-pve, hub.felhom.eu)
|
## 2. Files changed + commits
|
||||||
Deployed 0.94.0 via the golden/bootstrap mechanism; push_interval temporarily 1m for fast cycles
|
|
||||||
(restored to 15m after).
|
|
||||||
|
|
||||||
1. **First-run baseline (no restart):** first report after deploy recorded `applied_config_version=1`
|
- `internal/agentapi/client.go` — `ListCandidates` + `CandidatesResult`/`DiskCandidate` (mirror the
|
||||||
with `RestartCount=0` — baseline recorded, no restart.
|
agent's `/disks/candidates`).
|
||||||
2. **Config edit converges:** bumped demo-felhom `config_version` 1→2 in the hub DB (equivalent to a
|
- `internal/web/agent_disk_handlers.go` — `GET /api/disks/candidates` passthrough proxy.
|
||||||
config save). Next report ACK carried 2; controller logged:
|
- `templates/storage_init.html` / `storage_attach.html` — fetch `/api/disks/candidates`; render
|
||||||
`config-refresh: hub config_version=2 != applied=1 — re-pulling controller.yaml` →
|
`initialize` / `attach`; dropped the client-side "already-managed" filter.
|
||||||
`re-pulled … merged local_api (192.168.0.162:8443)` →
|
- `internal/web/storage_handlers.go` — **`resolveEnrollUUID`**: a raw candidate isn't in `/disks`, so
|
||||||
`applied config_version=2 — self-restarting` → `Graceful self-restart: exiting (0)`.
|
resolve its fs-UUID from `/disks/candidates` (the enroll blocker the "reuse unchanged" premise missed).
|
||||||
Container restarted (`RestartCount 0→1`, healthy); `applied_config_version=2`; the re-pull is proven
|
Both `runStorageInit` + `runStorageAttach` use it.
|
||||||
by `web.session_secret` changing (`e1ed2dd3…` → `f456197e…`, regenerated by the hub each pull);
|
- Tests: `agentapi` `TestListCandidates`(+`_Error`), `web` `TestRunStorageAttach_RawCandidate`(+red-proof).
|
||||||
`local_api` block preserved.
|
- `CHANGELOG.md`, `README.md`. Commits **`feab92c`** (wiring) + **`bea05ea`** (resolveEnrollUUID) on `main`.
|
||||||
3. **No restart storm:** 4 further report cycles at config_version=2 → zero config-refresh activity,
|
|
||||||
container stayed up (no further restart).
|
|
||||||
4. **Apps unaffected:** during the config-apply restart only `felhom-controller` restarted — every
|
|
||||||
customer app (nextcloud, gitea, calcom, rallly, sparkyfitness, …) stayed `Up (healthy)`.
|
|
||||||
5. **No inbound:** the whole flow ran with the hub never connecting into the box (it only answers the
|
|
||||||
box's report); hub `grep client.Do internal/web/` is clean of ControllerURL targets.
|
|
||||||
|
|
||||||
## Notes
|
## 3. Green gate
|
||||||
- A config change rotates `web.session_secret` (configgen regenerates it per pull) → controller
|
|
||||||
dashboard sessions are invalidated on a config apply. This is identical to the retired Push Config
|
`go build ./...`, `go vet ./...`, `go test ./...` — clean. Image `felhom-controller:0.95.0` built on 180,
|
||||||
behaviour (same configgen path) — no regression; only on actual config changes, not every report.
|
deployed to guest 9201 (golden/bootstrap), `Up (healthy)`.
|
||||||
- The first-boot `bootstrap.MaybeIngest` never-clobber is untouched; the refresh is a separate explicit
|
|
||||||
re-pull keyed on a version change.
|
## 4. End-to-end live validation (real UI, claude-in-chrome, felhom.demo-felhom.eu)
|
||||||
|
|
||||||
|
| Step | Result |
|
||||||
|
|------|--------|
|
||||||
|
| **Discovery (init wizard)** | ✅ `/dev/sdd` (SD/MMC/MS PRO, 59 GB, ext4) offered as the **sole** candidate; sda/sdb/sdc NOT shown. |
|
||||||
|
| **Format via Impl-1 guard** | ✅ enroll ran the guarded mkfs on /dev/sdd (`formatted device … fstype=ext4`) — the first real customer-flow guarded format. |
|
||||||
|
| **Discovery (attach wizard)** | ✅ the ext4 /dev/sdd offered under `attach`. |
|
||||||
|
| **Enroll end-to-end** | ✅ wizard attach → `resolveEnrollUUID` → mount (`/mnt/teszt_enroll`) → bind (`/mnt/felhom-drives/teszt_enroll`) → intent `enrolled` → guest-bind recorded → registered. UI shows **"Aktív"** (USB), alongside felhom-usb + felhom-flash, all Active — no false detach. |
|
||||||
|
| **Health-tracked** | ✅ the watchdog (RegistryKnownTargets) detected + self-healed a drop of the drive during testing. |
|
||||||
|
|
||||||
|
**Note on the init slow-format path:** a *slow* device (the 60 GB SD card, ~25 s mkfs) exceeds the
|
||||||
|
controller→agent request timeout; `runStorageInit` calls `FormatDisk` synchronously and does NOT poll the
|
||||||
|
agent's detached-format status (`/disks/format/status`), so on a slow device it aborts after the format
|
||||||
|
detaches and the mount/bind back-half doesn't run. **Pre-existing** (F20-BUG3-adjacent; not the Impl-2b
|
||||||
|
wiring). The end-to-end above was completed via the **attach** path (no mkfs) — which fully exercises the
|
||||||
|
candidate→enroll→active pipeline. Follow-up: make `runStorageInit` poll the detached-format status.
|
||||||
|
|
||||||
|
## 5. NOT done / follow-ups
|
||||||
|
|
||||||
|
- **Init slow-format polling** (above) — `runStorageInit` should poll `/disks/format/status` so a
|
||||||
|
slow-device format completes the enroll in one flow.
|
||||||
|
- **Impl-3** — the shared-box operator gate for self-serve format (raw-passthrough blind spot). The demo
|
||||||
|
is a dedicated box, so self-serve format is appropriate there.
|
||||||
|
- The throwaway `/dev/sdd` (SD card) is left **enrolled + Aktív** as living proof; the operator can eject
|
||||||
|
it via the UI (Leválasztás) and unplug when done.
|
||||||
|
|
||||||
|
## 6. Observations
|
||||||
|
|
||||||
|
- The spec's premise ("just wire the wizards; the enroll flow already works") was optimistic: the entire
|
||||||
|
enroll + tracking machinery assumed a PVE-storage drive (`Observe()`), so making raw drives first-class
|
||||||
|
required `resolveEnrollUUID` here + a chain of agent fixes (see agent REPORT). All shipped + validated.
|
||||||
|
|||||||
Reference in New Issue
Block a user