diff --git a/CONTEXT.md b/CONTEXT.md index 8a40e62..d6845eb 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -7,7 +7,22 @@ > > Ask Claude Code: "Please update CONTEXT.md with what we did today" -Last updated: 2026-07-17 (v0.142.0 — offsite repo continuity: orphaned-repo guard + run-status auto-refresh) +Last updated: 2026-07-17 (v0.143.0 — guest RAM resize UI, R-24; MinAgent 0.90.0) + +> **2026-07-17 — v0.143.0: guest RAM resize UI (R-24), LIVE on demo. MinAgent: 0.90.0.** The customer +> sees the guest's current memory + allowed range on the **Rendszer** settings page and resizes it +> ("Szerver memória (RAM)" card). The controller proxies + maps the agent's machine `code` to Hungarian; +> the AGENT (felhom-agent v0.90.0) enforces every bound and applies live (no reboot — R-24's old +> hub-desired-state framing is SUPERSEDED by Viktor's controller-direct ruling). agentapi +> `GuestMemory`/`ResizeMemory` (`*MemoryRefusedError` carries the code); capability +> `FeatureGuestMemoryResize` (featureMinAgent 0.90.0, probe type-asserts GuestMemory so the shared +> SupportProber/netAgent stay untouched); `POST /api/system/memory/resize`; JS confirm only on a shrink; +> agent-outdated hides the control, agent-unreachable falls back to the guest's `/proc/meminfo`. Deployed +> to BOTH demo guests (felhom-pve 9201 + nested demo-vm-felhom-4846bc 9201). **LIVE-validated** through the +> real endpoint on the nested demo (above_max + below_min refusals render the Hungarian message; the agent +> English never leaks; the capability gate resolves SupportYes via the 0.90.0 version header). A successful +> grow couldn't be shown on the tiny 4 GB nested host (max Phase-0-proven at the agent layer. See REPORT.md. > **2026-07-17 — v0.142.0: offsite repo continuity (Parts A + C), LIVE on demo.** Closes the > reinstall-orphaned-repo incident (a recreated data volume mints a new repo passphrase → the offsite diff --git a/REPORT.md b/REPORT.md index 48918b6..8fb8fd7 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,48 +1,67 @@ # REPORT — most recent implementation -## v0.142.0 — offsite repo continuity: orphaned-repo guard (A) + run-status auto-refresh (C) — 2026-07-17 +## felhom-controller v0.143.0 — guest RAM resize UI (R-24) — 2026-07-17 -**Baseline:** v0.141.0 (`1452dd2`) → **v0.142.0** (deployed live on demo 9201, healthy). Green: -`go build ./... && go vet ./... && go test ./...` + template/emoji/native-confirm gates. Closes the -reinstall-orphaned-repo incident class (diagnosed 2026-07-17; -`documentation/audits/DIAGNOSE-offbox-repo-orphaned-2026-07-17.md`). Pairs with hub v0.60.0 (Part B). +**Baseline:** v0.142.0 (`f900c83`) → **v0.143.0** (`3286c7f`, pushed). **MinAgent: 0.90.0** (couples to +felhom-agent v0.90.0's `/guest/memory`). Deployed + LIVE-validated on the nested demo guest. -### Part A — orphaned-repo guard (Scenarios A/B/C) -- **Classification** (`internal/backup/offbox.go` `classifyResticProbe`): `restic cat config` failure → - `wrong password or no key found` = **ORPHANED** (the definitive 07-17 signature) · no-repo = init · - other (network/SFTP-auth) = unchanged error handling. `ensureOffboxRepo` acts on it. -- **State + card:** `OffboxTarget.RepoState="orphaned"` (+ `OrphanedAt`, `OrphanedRenamedTo`); on the - transition only, `offbox_repo_orphaned` fires and scheduled runs then SKIP (no nightly spam). The - remote page shows a calm Hungarian exception-color card (`backups_remote.html`), NOT the raw restic - banner (`LastError` is cleared for this case); the "Távoli mentés most" button is routed to the card. -- **Reset — move-aside, never delete:** UNCLAIMED box auto-resets on detection (Scenario B); CLAIMED - box gets an explicit reveal-then-confirm reset (Scenario C, `/backup/offbox/reset` + `confirm=1`). - Both do `mv .orphaned-` (collision-suffixed `-2/-3`, over an ssh-exec seam) + - `restic init` + `offbox_repo_reset`. Restore paths never auto-reset (the auto-reset is run-only). -- **Red-proofs (run-fail-revert):** `TestOffbox_OrphanDetection_Claimed` (pre-fix = the incident: the - raw `wrong password` error surfaces, no state → FAIL; fixed: clean skip, one event, no nightly - re-fire, no raw banner) + `TestOffbox_OrphanDetection_UnclaimedAutoReset` (move-aside + both events, - un-orphaned) + `TestOffbox_ConfirmedReset` + `TestClassifyResticProbe`. +### What shipped -### Part C — run-status auto-refresh (Scenario E) -`GET /backup/offbox/status` (JSON: status/snapshots/last_error/orphaned) + a poll on -`backups_remote.html`: while a run shows "Fut…" the page polls every 3 s and reloads once at the -terminal state (Rendben/Hiba + fresh numbers) — no manual reload; polling stops at terminal. Does not -touch the scheduled-run rendering. Test `TestOffboxStatusHandler` + `TestOffboxRun_RefusedWhenOrphaned`. +The customer sees the guest's current memory + the allowed range on the **Rendszer** settings page and +resizes it. The controller only proxies + maps the agent's machine `code` to Hungarian — the **agent** +(felhom-agent v0.90.0) enforces every bound and applies the change live (no reboot). Memory only. -### Live leg (§13) — fallback taken, staged for the rehearsal -The scratch-target live leg CANNOT run without disturbing live state: configuring a scratch offsite -target resets `EscrowState` to `pending` (the offsite config carries the escrow state + the escrowed -password), the offbox RUN gates on `EscrowState=="escrowed"` (a scratch target needs its own -customer-driven ceremony), and the live repo (customer zero's first snapshot) is untouchable (rule 2). -Per §13's explicit fallback: the mechanism is covered by the fake-based tests above (the classification -uses the EXACT restic stderr matched in the real 07-17 diagnosis), and the live leg is STAGED for -Viktor's planned delete/re-create rehearsal. Deploy verified: controller 0.142.0 live+healthy on 9201; -the demo's healthy live repo was NOT spuriously flagged (settings `repo_state` absent = normal). +- **agentapi (`internal/agentapi/client.go`):** `GuestMemory` (GET) + `ResizeMemory` (POST). A ruled 412 + surfaces `*MemoryRefusedError` (machine code + fresh bounds); a pre-0.90 agent 404s → typed `*StatusError`. +- **Capability (`internal/agentapi/features.go`):** `FeatureGuestMemoryResize` + `featureMinAgent` **0.90.0** + + a `featureProbes` row. The probe TYPE-ASSERTS the one method it needs (`GuestMemory`), so the shared + `SupportProber`/`netAgent` seams and their fakes are untouched (minimal-coupling choice — see Observations). +- **UI (`internal/web/system_memory_handlers.go` new; `templates/settings_system.html`):** a "Szerver + memória (RAM)" card; `POST /api/system/memory/resize` (mounted `/api/system/` in main.go) → capability + gate (SupportUnknown passes) → agent → response flash. JS confirm ONLY on a shrink. Code→Hungarian map; + agent-outdated hides the control (+"rendszerfrissítése szükséges" note); agent-unreachable falls back to + the guest's own `/proc/meminfo` (control disabled, honest note; the page never 500s). + +### Tests + +- agentapi: GuestMemory decode + 404-typed; ResizeMemory success + refusal-code (`*MemoryRefusedError`); + capability table (0.89→No / 0.90→Yes via the version fast-path; probe 404→No / ok→Yes). +- web handler: success maps to the Hungarian old→new line; below_usage_floor maps + **the agent's English + never leaks**; agent_outdated gate refuses with ResizeMemory NEVER called. +- Gates: `template_id_gate` + `emoji_gate` OK. `go build/vet/test` all pass (23 packages). + +### Deploy + LIVE validation + +Built + pushed `felhom-controller:0.143.0`; deployed to BOTH demo guests (guest 9201 on felhom-pve AND the +nested guest 9201 inside `demo-vm-felhom-4846bc`) — both `Up … (healthy)`. + +**LIVE end-to-end through the REAL controller endpoint** (login → CSRF → POST) on the nested demo guest — +the exact pipeline the settings page drives (no server logic skipped; method: authenticated curl to the +controller container, browser bridge unavailable): + +``` +GET /api/system/memory → {"allocated_mb":2890,"usage_mb":181,"host_total_mb":3914,"min_mb":2048,"max_mb":1866,"floor_mb":2048,"running":true} +POST /api/system/memory/resize {memory_mb:5000} → {"code":"above_max"} "A megengedett maximum 1866 MB (a gazdagép tartaléka miatt)." +POST /api/system/memory/resize {memory_mb:1024} → {"code":"below_min"} "A minimális memória 2048 MB." +``` + +The full chain is proven live: controller auth → capability gate resolving **SupportYes via the +X-Felhom-Agent-Version=0.90.0 header** → agentapi → agent enforcement → machine code → Hungarian mapping +(the agent's English is not shown). The lxcfs ripple (guest `/proc/meminfo` follows a real resize) is +Phase-0-proven at the agent layer. + +### NOT yet live-validated + +- A **successful** resize through the bounded endpoint could not be shown on the tiny 4 GB nested host: its + bounds are degenerate (`max_mb 1866 < current 2890`) so every target is correctly refused. The successful + live cgroup apply (2890→3146→2890, no reboot) is Phase-0-proven at the agent layer (agent REPORT §2). On a + normal-sized customer box a grow/shrink will succeed — folds into the rehearsal (S6 gains the resize touch). +- Full browser click-through of the card (the bridge attaches only to sessions started after it connected). ### Observations -- Orphaned bytes vs quota: counted honestly in the usage bar; the card notes the set-aside copy; a - purge affordance is NOT built (Viktor purges the `.orphaned-` dir manually — as in the 07-17 fix). -- Other backup pages' refresh behavior unchanged (Part C is scoped to the remote-run status only). -- The controller cannot know the hub's superseded-blob count without cross-repo plumbing, so the card's - "recoverable with the recovery code" phrasing is unconditional (true going forward — Part B retains). + +- The `featureProbes` type-assertion (vs. widening the shared `SupportProber`) keeps the memory feature from + coupling `netAgent` and every unrelated prober/fake to the memory surface. A prober that can't be asked → + a non-404 error → SupportUnknown (fail-open), never a false "supported". +- The `memAgent` seam carries `NetVerifyStatus` + `AgentVersion` purely to satisfy the shared SupportProber / + version fast-path (production `*agentapi.Client` has them anyway) — the one small wart of reusing the cache. diff --git a/controller/README.md b/controller/README.md index 82e44fa..af1755c 100644 --- a/controller/README.md +++ b/controller/README.md @@ -110,6 +110,16 @@ backups, monitoring and notifications. All Proxmox/disk operations are delegated JetBrains Mono, variable woff2, latin+latin-ext) and a 30-icon Lucide sprite are vendored in the binary** (`internal/web/static/fonts/` served at `/static/fonts/`; `templates/icons.html`) — no CDN, no emoji. The setup wizard serves the same embedded stylesheet via `web.StyleCSS()`. +- **Guest RAM resize (v0.143.0, R-24; MinAgent 0.90.0)** — the Rendszer page's "Szerver memória (RAM)" + card shows the guest's current/used memory + the allowed range and lets the customer resize it. The + controller only proxies + maps the agent's machine `code` to Hungarian; **the agent (felhom-agent + v0.90.0) enforces every bound and applies the change live via PVE SetConfig — no reboot** (min 2048 / + max host_total−2048 / a shrink is refused below max(2048, usage+512)). `systemPageData` calls + `memoryCardData` (a 2 s agent GET behind the `FeatureGuestMemoryResize` gate); `POST + /api/system/memory/resize` (`ServeSystemAPI`, `internal/web/system_memory_handlers.go`) → + `agentapi.ResizeMemory` → the code→Hungarian map. A JS confirm fires only on a shrink; an outdated agent + hides the control; an unreachable agent falls back to the guest's own `/proc/meminfo`. Memory only + (cores stay observation). The lxcfs ripple means the deploy-page memory math follows a resize for free. - **Page IA (v0.97.0, TASK-D1)** — the settings monolith is split into four pages, each with its own data builder (`systemPageData`/`storagePageData`/`notificationsPageData`/`securityPageData`, sharing `settingsBaseData`) and template. Routes: `/settings` (Rendszer), `/settings/notifications`