Files
felhom-agent/REPORT.md
T

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 PVE SetConfig (live cgroup apply, no reboot); verify-after-apply.
  • R-28 fast-tick-until-first-convergenceinternal/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 in Apply).
  • 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: LastResult effect + 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.0 active; 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.0 active; 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.