Commit Graph

28 Commits

Author SHA1 Message Date
admin 3286c7faaf v0.143.0 — guest RAM resize UI (R-24) — MinAgent: 0.90.0
The customer sees the guest's current memory + allowed range on the Rendszer settings
page and resizes it. 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).

agentapi: GuestMemory + ResizeMemory; ruled 412 -> *MemoryRefusedError (code+bounds);
pre-0.90 agent 404 -> typed *StatusError. Capability: FeatureGuestMemoryResize +
featureMinAgent 0.90.0 + a featureProbes row (type-asserts GuestMemory so the shared
SupportProber/netAgent are untouched).

UI (internal/web/system_memory_handlers.go, settings_system.html): "Szerver memoria (RAM)"
card + number input; POST /api/system/memory/resize -> capability gate -> agent -> flash.
JS confirm only on shrink. Code->Hungarian map; agent-outdated hides the control;
agent-unreachable falls back to the guest's /proc/meminfo. Agent English never shown raw.

Tests: agentapi (decode, 404, refusal-code, capability table) + web handler (success/
below_usage_floor/agent_outdated). Gates + go build/vet/test all pass.
2026-07-17 19:10:00 +02:00
admin 0515d153db F6 deeper half: poll agent /disks/format/status on the 15s client timeout (slow USB mkfs runs detached) — agentapi.FormatStatus + awaitAgentFormat; then mount+register. Found on the live leg. 2026-07-17 09:41:50 +02:00
admin 26a43708b7 v0.116.0: observability pass — always-on debug ring + leveled sweep + agent tab + self-log pull — MinAgent: 0.81.0
Capture layer: LogBuffer always exists; logger = MultiWriter(LevelFilterWriter
(stdout, logging.level), ring) so DEBUG detail exists remotely without a config
flip while docker logs keep respecting the level. New internal/logx leveled
helpers. Report ACK gains controller_log_requested (additive); next report
ships controller_log_tail (128KB, consume-once, app-tail wire byte-compatible).
Debug page: Vezérlő|Ügynök tabs; agent tab proxies agent /debug/logs with the
pre-0.83 notice on typed 404. Sweep: netstorage_job phases, netprobe, handler
validation refusals + orphan WARN, SupportsWithSource gate line, agentapi
per-call DEBUG, migrate phase lines, tier2/offbox unswallowed persists.
Red-proofs: filter-disabled, drain-removed, dropped-phase-line all FAIL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-11 16:45:57 +02:00
admin b6842a4bc3 v0.115.0: version-aware Supports (agent header channel) + DSM-validated NFS guidance — MinAgent: 0.81.0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-11 15:03:41 +02:00
admin d347dc48d2 feat: agent-capability gate for coupled features — typed StatusError + Supports probe/cache + netstorage add gate (option-1)
- agentapi: non-2xx GETs now surface as typed *StatusError (same text); features.go
  adds Feature/SupportState/SupportCache (route probe, TTL 5m, Yes/No cached,
  Unknown never cached or refused) + Client.Supports
- web: handleNetStorageAdd refuses up front (412, code agent_outdated, Hungarian
  message) when the agent predates /netstorage/verify-status (= pre-0.81 add
  semantics); gate runs BEFORE the single-flight claim; SupportUnknown passes
  through to the existing agent-error paths
- netAddSupport page-render helper lands here; its template consumer follows
- tests: T1 gate refusal (job never starts, slot free), T2 unchanged happy path +
  warm-cache negative assertion, T3 indeterminate never 'too old', T4
  classification incl. the string-match trap, T6 TTL, wire-level 404-typing;
  red-proofs RP1-RP4 run and reverted

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-11 13:45:49 +02:00
admin bb8737a81f netstorage: verify-before-commit orchestration — agentapi verify fields, uid-1000 re-exec probe, detached add job, orphan rows
Controller half of the verify pipeline (SPIKE-nas-verify b57f6c1): AddNetStorage
gains verify/job fields + typed NetAddRefusedError; NetVerifyStatus polls the
agent slot; --netprobe hidden re-exec mode (SysProcAttr.Credential uid/gid 1000,
no shell) proves in-guest writability; the add handler starts a detached
single-flight job (agent_add → verifying → probing → registering LAST) with full
rollback on any failure incl. verify-lost-after-restart (Scenario F); §3.2
Hungarian error map server-side; live-but-unregistered shares surface as remove-
only 'Árva megosztás' rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-11 09:59:33 +02:00
admin a38c743926 v0.107.0: key-auth-first bridge + staged-secret wipe on escrow confirm
Key-auth-first: a KeyAuthProber seam lets the bridge skip consume+install
when the already-installed key still authenticates (pinned to the freshly
verified host key) — descriptor changes on provisioned guests no longer
loop on consume-404. Fingerprint verify still precedes everything.

Wipe-on-escrowed: confirm-escrow now calls the agent's new
DELETE /escrow/stage-secret (v0.78.0) best-effort, closing the hygiene gap
where a ceremony-less confirm left the staged password file behind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-09 22:26:53 +02:00
admin 0b09a799cb v0.105.0: fork-4 offsite password custody — hand-off + atomicity gate + DR inject + coord
Pairs with agent v0.77.0. StageEscrowSecret pushes the repo password to the
agent (POST /escrow/stage-secret) at offsite-enable → EscrowState="pending".
Atomicity gate: RunOffboxBackup (scheduler + handler) refuses until
EscrowState="escrowed" (operator POST /backup/offbox/confirm-escrow after the
escrow ceremony) — no un-recoverable offsite ciphertext can exist. DR:
POST /backup/offbox/inject-password pre-places a recovered 64-hex password 0600
(honored by WriteOffboxSecrets' IsNotExist guard; refuses clobber without
force). DR recipe gains non-secret offsite_restic coords (DRResticCoord); SFTP
key regenerated at DR, not escrowed. New settings.OffboxTarget.EscrowState.
Tests + atomicity & inject companion red-proofs green; UI gates pass. NOT yet
live-validated (supervised ceremony).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-09 15:13:59 +02:00
admin c997d79246 feat(agentapi): surface agent disk-op refusal reasons; v0.101.0 + CHANGELOG/REPORT
EjectDisk/Decommission switched from c.post (drops non-2xx body) to
postWithStatus + shared refusalError, so the agent's informative 403 body
("…decommission refused (role: X)") reaches the operator instead of a bare
"HTTP 403" (campaign F2 evidence gap). Generic post + other callers untouched.
Tests T-D1/T-D2/T-D3 + ok:false case; T-D1 red-proof shows the pre-fix bare
"HTTP 403". Bundles the v0.101.0 CHANGELOG entry (this + the F3 sync deadline).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
2026-07-06 14:11:44 +02:00
admin feab92ccfc v0.95.0: enrollment wizards use the raw-device scan /disks/candidates (Impl-2b)
Both wizards now source candidates from the agent's Impl-2a raw-device scan
(GET /disks/candidates, proxied) instead of the Observe-based /api/disks — so a
brand-new non-PVE-storage drive is finally discoverable + enrollable end-to-end.
agentapi.ListCandidates + a passthrough proxy (no controller-side filtering; the
agent's unclaimed filter is authoritative). storage_init renders `initialize`,
storage_attach renders `attach`; the enroll flow + Impl-1 guarded mkfs unchanged.
Tests + go build/vet/test clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 18:04:49 +02:00
admin 364dc50794 controller v0.92.0: NAS network storage Part A2 (registry + UI + per-share health)
Controller-side of NAS network storage, proxying to agent A1 /netstorage/*. Distinct
'network' storage kind (no drive lifecycle); add/list/remove + per-share health UI;
unreachable NAS is a recoverable warning, never the drive missing/stop cascade; SMB
creds pass through to the agent, never persisted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxLA1mZurFq9kt8hneFeCs
2026-06-30 14:40:47 +02:00
admin 3c1e91b5f0 v0.85.0: self-update reworked — in-guest pull + agent swap (Phase 1)
Replaces the dead in-container docker-compose self-update (composePath doesn't
exist in the LXC guest). The controller now docker-logins+pulls the target image
in-guest (shared socket, its registry token), then delegates the container swap to
the host agent (agentapi.SwapController -> POST /controller/swap), which owns the
restart + health-verify + rollback. Removed performUpdate compose flow /
updateComposeFile / composePath. NewUpdater takes an AgentSwapper. DryRun reports
agent_reachable + pull_capable. UI button + poll unchanged; latest-only.

Tests: up-to-date no-op / pull-fail agent-not-called / happy pull-then-swap /
no-agent unavailable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TtXesNa2LGbMmE4DNL6SE7
2026-06-26 21:26:14 +02:00
admin 2a5b88fbf9 v0.74.0: fix controller->agent connection leak (reuse one agentapi client)
agentClient() built a new agentapi.Client (new bare http.Transport, IdleConnTimeout:0)
per call and discarded it without closing idle conns -> one leaked idle ESTABLISHED
socket per call to the agent :8443, exhausting the ephemeral port range after ~5 days
(EADDRNOTAVAIL). Memoize one shared client via sync.Once; harden Transport
(MaxIdleConns/PerHost + IdleConnTimeout 90s). Agent/firewall untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:19:24 +02:00
admin 42f69dadda controller v0.68.0: storage lifecycle on intermediary model (H2/H3/M1/M3 + boot-id)
H2 decommission UI button (migrate / anyway); H3 one-click re-enroll of a
decommissioned drive; M1 default reassignment (auto-promote + block-if-none);
M3 migrate re-asserts 2775 setgid on userdata dirs; deterministic guest-reboot
recreate via agent boot_id (replaces the timed sample). Fixes the {path}/{where}
H1 JS bug. Non-hollow tests + companions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 19:26:02 +02:00
admin 55c896624f controller v0.67.0: intermediary-mount — HDD_PATH repoint + drive-absent gate + H1 routes
Drives are visible in-guest only at the STABLE /mnt/felhom-drives/<name>; the
registered path + HDD_PATH + FileBrowser source repoint there while agent calls
map back to raw /mnt/<name> (agentWhere). Enroll binds-under-parent before
register. Drive-absent GATE (planDriveGates + 30s driveGateLoop) stops/blocks
apps when a drive vanishes and auto-restarts on return; start-gate refuses start
when the drive is absent. H1 endpoints (disconnect/reconnect/restart-apps) routed
onto host-side ops. Non-hollow tests + companions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:45:13 +02:00
admin f2596ea433 B2b: decommission orchestration + missing-storage indicator + re-enroll fix (v0.65.0)
agentapi.Decommission + handleStorageDecommission (migrate-all-or-none, Change 2):
migrate-then-decommission via the migration done-hook, or decommission-anyway (stop
apps, keep HDD_PATH). 'Hiányzó tárhely' badge on dashboard/stacks/app card when an
app's drive is decommissioned/disconnected/absent. Change 4: registerStoragePath
clears the decommissioned marker on re-enroll (ClearDecommissioned had no callers).
Non-hollow tests incl. mutation-proven Change-4 companion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 20:08:52 +02:00
admin 688ba0d2a5 v0.63.0: surface agent wipe_durable_id (F20-BUG2) + guest_attached (F9) on /api/disks
The agent (v0.31.0) now returns these on /disks, but the controller dropped them when
re-marshalling into its agentapi.DiskInfo mirror. Added both fields (additive) so they reach
/api/disks + the dashboard: wipe_durable_id (gate scheme, for the wipe-confirm) and guest_attached
(drive bound into THIS guest vs merely host-present). Controller behaviour otherwise unchanged.
2026-06-14 15:21:29 +02:00
admin 2cf3fadaec F20-BUG1: surface swallowed format error in agentapi.FormatDisk
A failed agent format (e.g. 502 'device is mounted', ok:false, data:null) fell
through FormatDisk's trailing 'return out, nil', so the web layer reported a
zero-value FormatResult as ok:true — a failed DESTRUCTIVE format read as success.
postWithStatus now returns the full envelope; FormatDisk returns a non-nil error
on any non-2xx/ok:false that is not a recognized refusal (403/needs-confirmation).
Test TestFormat_MountedFailureSurfacesError (502 → non-nil err) fails on old code.
2026-06-14 09:46:28 +02:00
admin 13c6a0929a v0.57.0: stable host-storage list + per-app Tier-2 config panel
Part A of the UI-fixes/storage-spike spec.

A1: enrichHostStorageTargets sorts /api/host-metrics storage_targets
server-side and attaches friendly Hungarian labels + purpose, fixing the
#host-storage-bars reorder-on-poll bug. Display labels only — PVE storage
ids are never renamed.

A2: new GET/POST /stacks/{name}/backup Tier-2 config panel; the "2. mentés"
Beállítás button is repointed there from the dead-end deploy page. Customer
can pin a target drive or disable Tier 2; preference is preserved across the
runner's status writes. Always visible (single-SSD + non-HDD apps included).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 14:23:34 +02:00
admin 2a353572f7 controller v0.49.0: slice 10 P2 activation — pending-drive detection + restart button
pendingActivationDrives() flags registered drives the agent shows attached but not
live-mounted in the container; settings banner + "Újraindítás most" button →
/api/storage/activate → agentapi.GuestReboot. Batches all pending into one restart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 17:19:27 +02:00
admin ee5b6304a7 controller v0.48.0: slice 10 P2C — enroll passes the drive into the guest
agentapi GuestAttach(where) → POST /disks/guest-attach; runStorageInit/Attach +
handleStorageRegister call attachIntoGuest after register (best-effort, P3 heals).
Closes Branch A: enrolled drives become usable in the guest, banner clears.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 15:42:52 +02:00
admin bbed5af662 controller v0.47.0: backups page — whole-guest backup visibility + manual trigger
Part 2 of the USB/backup spec. agentapi: StatusResponse.Backup record, DueResponse
age_seconds, RestoreTestStatus(). New "Rendszermentés (teljes mentés)" section
(read-only: last backup/target PBS-vs-local/next-due/restore-test) + "Mentés most"
manual trigger that goes through the quiesce loop (controller owns quiescing):
quiesce.Loop gains mutex + TriggerNow() (single-flight, async). New
/api/guest-backup/{trigger,status} (distinct from apiRouter's /api/backup/*).
App-data rows relabeled under an "Alkalmazás-mentések" divider. Config → slice 10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 11:15:25 +02:00
admin 12064dcd88 v0.44.0: role-aware drive management — protected lockout + customer type-to-confirm wipe + drive-list restyle
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 21:44:50 +02:00
admin 29a9dcdd8c v0.43.0: rebuilt storage management (guided init/attach/eject on agent disk model)
Controller-only UI/orchestration over the agent's disk endpoints + StoragePath
registry. New: storage overview (data_bearing badges), guided init (format ->
resolve fs UUID -> assign -> register; data-bearing REFUSAL surfaces the
felhom-opsign command, no force-format), guided attach, eject (+deregister,
dependent-guest warning). agentapi: DiskInfo.DurableID/FSUUID + FormatResult.
PendingOp (parsed from the 403). Honest buttons (migrate disabled, no 404s).
Phase 3: removed dead CrossDrive blocks in deploy.html/backups.html.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 19:47:58 +02:00
admin d8d1e17758 slice 9: host-health view on the monitoring page (v0.39.0)
Add agentapi HostMetrics() + a thin /api/host-metrics proxy to the agent's
new GET /host/metrics, and a 'Szerver allapota (gazdagep)' card on the
monitoring page rendering host CPU%/load/mem/CPU-temp(n/a)/uptime + per-
storage capacity bars (thin-pool fill, disk temp/wear). Polls every 8s.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 16:16:15 +02:00
admin 0294513906 slice 8C Phase B.1: agentapi disk client (Disks/AssignDisk/EjectDisk/FormatDisk)
ErrFormatRefused surfaces the agent's data-bearing refusal distinctly. Tests:
list, blank format OK, data-bearing refused, eject dependents.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:23:00 +02:00
admin 68fc153d9c slice 8B (controller half): app-consistent backup quiesce loop (v0.36.0)
internal/quiesce: poll /backup/due -> quiesce (stop app stacks) -> POST /backup
-> poll /backup/status -> unquiesce (restart exactly those). Crash-safety:
persisted marker before stopping, guaranteed unquiesce (defer), max-quiesce
guard, startup Recover, single-flight. agentapi BackupDue/StartBackup/
BackupStatus; stacks.RunningAppStacks(); config QuiesceConfig; main wiring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 10:44:52 +02:00
admin 2a0d9a1b7a slice 8A (controller half): bootstrap.json ingestion + pinned agent local-API client (v0.35.0)
internal/bootstrap: first-run bootstrap.json ingestion (decision (c)) — seed
controller.yaml + skip setup; idempotent + fail-safe. internal/agentapi:
minimal pinned local-API client (leaf-cert SHA-256 pin, fails closed). config
LocalAPIConfig; startup /storage connectivity probe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 09:47:54 +02:00