6.5 KiB
REPORT — felhom-agent v0.90.0 (agent train: guest RAM resize R-24 + fast-tick R-28)
Date: 2026-07-17 · Baseline: v0.89.0 (9127f54) → v0.90.0 (ac112c9, pushed) · MinAgent coupling: felhom-controller v0.143.0 gates its guest-memory-resize UI on this agent.
1. What shipped
Two ruled features on one train (Viktor's one-train/one-floor ruling), plus a diagnosis passenger.
- R-24 guest RAM resize (controller-direct) — new self-scoped local-API surface
GET/POST /guest/memory(internal/localapi/guestmemory.go). The agent is the security boundary: bounds recomputed FRESH per request from a live read; applies via PVESetConfig(live cgroup apply, no reboot); verify-after-apply. - R-28 fast-tick-until-first-convergence —
internal/fasttick/, wired over four cached sources; pulses the shared out-of-band trigger every 30 s while unapplied, self-disarms on convergence. Seams added:reconcile.Engine.LastResult(),wgtunnel.Manager.TunnelConvergence(). - Guests-0/0 — diagnosed live, hypothesis REFUTED (no code change).
2. Phase-0 probe (the gate — PASSED)
Prerequisite: prove PUT /nodes/{node}/lxc/{vmid}/config memory=X hot-applies under the AGENT token. Run on
the nested demo box demo-vm-felhom-4846bc (hostname felhom-appliance, guest LXC 9201) — reached via the
hub-vaulted G1 break-glass root credential (the box's root PW was unknown; retrieved operator-side from the
hub SQLite host_recovery table, then shredded). Transcript:
node=felhom-appliance vmid=9201 token_user=felhom-agent@pve
BEFORE config.memory=2890 MB | status.maxmem=3030384640 B (2890 MB) | status=running | host-uptime=8226s
BEFORE guest MemTotal: 2959360 kB | guest /proc/uptime=8227.76
--- PUT memory=3146 (agent token) --- PUT data field: None (empty == synchronous apply, no UPID)
AFTER config.memory=3146 MB | status.maxmem=3298820096 B (3146 MB) | status=running | host-uptime=8229s
AFTER guest MemTotal: 3221504 kB | guest /proc/uptime=8231.54
VERDICT maxmem_grew=True guest_running=True guest_not_rebooted=True
REVERT memory=2890 → maxmem 3030384640 B (2890 MB), running; guest MemTotal 2959360 kB
Proven: live cgroup apply (maxmem moves, guest running), no reboot (guest /proc/uptime keeps climbing), synchronous (no UPID), agent token has the privilege (no 403), and /proc/meminfo ripples via lxcfs — the mechanism that makes the controller's deploy-page memory math follow for free. The gate PASSED → the resize leg was implemented.
3. Files created / modified
internal/localapi/guestmemory.go(new) +guestmemory_test.go(new);internal/localapi/server.go(Options.Memory + fields + routes);cmd/felhom-agent/main.go(wire Memory=px; construct + launch fasttick).internal/fasttick/fasttick.go+fasttick_test.go(new).internal/reconcile/engine.go(LastResult/recordResult) +lastresult_test.go(new).internal/wgtunnel/manager.go(TunnelConvergence+ cached snapshot refresh inApply).CHANGELOG.md,REPORT.md(this),CONTEXT.md,REUSE.md.
4. Tests + red-proofs (all restored green)
- localapi memory: GET fields, grow, shrink-above-floor, the three refusals (each asserting SetConfig count == 0), cross-guest 403, fresh-bounds-per-request, verify-not-reflected 502, nil-config 503.
- fasttick: pulse-while-unconverged, silent-when-converged, the ruled disarm-on-convergence, full-channel
non-blocking drop, first-reason. reconcile:
LastResulteffect + pre-first-run ok=false. - Red-proofs (run-fail-restore): (i) floor guard removed → below_usage_floor 412 test fails; (ii) max
guard removed → above_max test fails; (iii) fast-tick ignores convergence (always pulse) → the silent +
disarm tests fail. All confirmed red, then restored. Full suite:
go build ./... && go vet ./... && go test ./...— all 28 packages pass.
5. Guests-0/0 diagnosis — hypothesis REFUTED, no fix
Live read-only checks on the nested box: pool felhom members: [9201]; the agent token's ListLXC sees
[9201] (VM.Audit comes from the /pool/felhom grant — so pool membership IS the visibility mechanism);
hub reports 1/1. The agent log shows the observed 0/0 was the pre-provision window: 16:11 stale-lock … pool=felhom listed=0 + desired … guests=0, then 16:16 lanresolver … vmid=9201 (guest created). No guest
existed yet → empty list, no error (the silent shape) — correct, not a bug. The existing PoolAddVMID
re-assert (bringup.go:498) already covers the known restore-over-existing edge (campaign-2 R2). Item 2's
fast-tick is precisely the mitigation — it shortens the pre-provision reporting window from ≤15 min to ~30 s.
6. Deploy verification (BOTH hosts)
Built on 180 (go build -ldflags -X main.version=0.90.0), deployed to both Proxmox hosts (backup + install +
restart):
- felhom-pve (the N100):
felhom-agent 0.90.0active;capabilities self-check ok=63 total=63 degraded=0;fast-tick armed: 30s out-of-band cadence while desired-state is unapplied; poke listener + local-api up. - demo-vm-felhom-4846bc (nested, 192.168.0.135):
felhom-agent 0.90.0active;capabilities self-check ok=63 degraded=0;fast-tick armed: 30s …; local-api on 192.168.0.135:8443. (Converged box → fast-tick quiet after the startup line, as designed.)
7. NOT yet live-validated / operator GO
- Fast-tick real-onboarding proof: the fast-tick's value shows on a REAL fresh box's pre-tunnel window (the rehearsal S5 bind sequence should now be visibly instant). Not manufacturable non-destructively here; both agents show the armed startup line + the unit tests prove the pulse/disarm.
- Floor LAST (operator GO): both demo agents verifiably run 0.90.0 (the safety gate) and the controller coupling is PROVEN LIVE via the X-Felhom-Agent-Version header (see the controller REPORT's live refusals). Publishing 0.90.0 to Gitea + the hub Day-0 manifest vouch + MinAgent-floor raise to 0.90.0 are set via the operator-password-gated Day-0 manifest UI (CC cannot log in) — the operator (Viktor) GO step, matching the v0.89 train's "publish/vouch = operator follow-up".
8. Observations
- The nested demo host is tiny (4 GB): its resize bounds are degenerate (
max_mb 1866 < min_mb 2048 < current 2890) — no resize succeeds through the bounded endpoint there (all refused correctly). The successful apply is Phase-0-proven at the agent layer; the controller live-validation exercised the refusals (above_max, below_min) end-to-end. - REUSE.md gained the fasttick Source pattern + the localapi MemoryOps seam.