Commit Graph

40 Commits

Author SHA1 Message Date
rcourtman 2a7019b0fa Use typed Proxmox runners and preserve cross-clock action evidence
Proxmox planning and dispatch now require a unique credential-admitted typed
runner with durable receipts. Development authentication preserves explicit
bearer identity so runner activation keeps its tenant and credential scope.

Preserve observer and receiver timestamps from their separate clocks instead
of rejecting or rewriting valid evidence. Keep completed execution separate
from stale or inconclusive verification, and label independent observations
accurately in action reviews.

Verified with targeted race suites, action-review tests and frontend build,
plus a real Assistant start plan and approved VM110 start/stop with independent
Proxmox confirmation. Final action reviews passed Playwright at 1440, 900 and
390 pixels, including retained completed, rejected and expired history.
2026-09-07 12:00:48 +01:00
rcourtman 60d0651a88 Cancel abandoned typed agent operations 2026-09-01 11:21:26 +01:00
rcourtman 4724c0764b Fence runner activation session authority 2026-08-31 23:52:25 +01:00
Richard Courtman 08f7c5f0d5 Harden secure agent runtime boundaries 2026-08-31 00:06:24 +01:00
Richard Courtman 770733fc92 Make action runner rotation activation-safe 2026-08-30 18:00:33 +01:00
Pulse Test 7e92ac8118 Harden secure runtime separation boundaries 2026-08-30 14:26:01 +01:00
Pulse Test d06ffc233d Harden secure agent runtime transitions 2026-08-30 01:41:57 +01:00
Pulse Test d607d5cf46 Separate agent remediation runtime 2026-08-29 23:48:28 +01:00
rcourtman 6163a2a564 feat(patrol): verify Docker actions independently 2026-08-27 20:14:38 +01:00
rcourtman 3a9dffa850 Fix Docker command recovery after token rotation 2026-08-23 09:18:44 +01:00
rcourtman 4c7b1a2434 Fix Docker-in-LXC probe storm against slow Proxmox hosts
The guest Docker socket probe hung minipc hard enough to need a power
cycle (2026-08-20): ~100 orphaned pct exec children, load 133, sshd and
pveproxy starved. Three bugs chained, each fixed here:

1. Dispatcher re-issued a probe while the previous one was still
   executing. The poll cycle's enrichment context had expired, so
   ExecuteCommand dispatched, returned the context error 50ms later,
   and the next 3s cycle sent the identical command again — unbounded
   concurrency against a host that was slow to begin with. The
   monitoring dispatcher now takes a per-guest in-flight claim before
   dispatching probe or inventory commands (completed probes release
   it; abandoned ones hold it for a 2-minute window), and both dispatch
   paths bail out under a dead context.

2. The host agent never got the July process-leak fix: 45480a5cc
   landed only on pulse/v6-release, so main-line agents killed just the
   direct shell on timeout, orphaning pct exec → lxc-attach children
   and blocking Wait on their inherited pipes (10s timeouts reported as
   300s+ durations). Port it: run each command in its own process
   group, SIGKILL the group on cancel, bound Wait with WaitDelay, and
   treat ErrWaitDelay after a clean exit as success.

3. Server-side abandonment never reached the agent. ExecuteCommand and
   ReadFile now refuse to dispatch under an already-expired context,
   and send a best-effort cancel_command when they stop waiting; the
   agent cancels the in-flight execution (killing its process group)
   and reports "command canceled". Older agents ignore the unknown
   message type.

Also add a per-node circuit breaker: three consecutive command failures
on one node suspend all Docker probe/inventory dispatch to it on the
existing 1m→30m backoff schedule, so a host-level stall (NFS flapping)
stops the probing entirely instead of failing guest by guest.

Regression tests simulate the storm without hardware: a never-returning
executor is not re-issued across poll cycles, an expired context
dispatches nothing and records no failure, abandoned probes hold their
claim, the breaker blocks new guests on a failing node, and the agent
kills the whole process group on timeout and on server-issued cancel.

Contract-Neutral: monitor.go delta is three private struct fields holding Docker probe dispatch state; host-agent deletion/re-enrollment lifecycle untouched — contracts and all other proofs are staged
2026-08-20 23:33:36 +01:00
rcourtman e31fc37983 Gate Patrol actions on agent preflight 2026-08-14 01:12:49 +01:00
rcourtman a7646e5f86 Fix Patrol autonomy and refusal reporting 2026-08-13 23:26:08 +01:00
rcourtman c41edb65a0 Fix agent command channel admission 2026-07-24 13:00:03 +01:00
rcourtman 8e1d5729d4 Point the exec rejection message at a fresh commands-enabled install
The rejection told operators to re-run the agent installer, but the
token minted by the original install command cannot gain the exec scope
after the fact and install tokens are single use, so re-running the
same command loops forever (issues #1586, #1564). Name the actual
recovery step instead.

Contract-Neutral: reworded operator-facing rejection copy (#1586, #1564); no behavior or contract change
2026-07-18 11:54:09 +01:00
rcourtman 5abb2d8f48 refactor(agentexec): dedupe host operation dispatch and receipt-identity validation
Contract-Neutral: dupl lint dedupe refactor; no behavior or contract delta (typed host-operation dispatch and receipt-identity validation extracted to shared generic helpers)
2026-07-17 17:36:17 +01:00
rcourtman acd5637485 Drive executing-action restart recovery at startup and agent registration
RecoverExecutingActions existed with full test coverage but had no
production caller, so any typed action mid-dispatch across a server
restart (container update, start/stop/restart, host update, storage
cleanup) stayed in the executing state forever and sat in the Actions
inbox as live work, even after the agent persisted its terminal durable
receipt. Reproduced live on the dev instance with a Docker container
update (act_bf77dfe860ad3d8e4e0a91dc8eb83b44).

The router now runs a bounded, serialized recovery pass per organization
from a startup background worker, and again whenever an agent
(re)registers on the agentexec command server via a new registration
notifier, because a receipt-pending attempt can only be reconciled while
the owning agent is connected. Both triggers reuse the existing
query-only reconciliation semantics; nothing gains a resend authority.

Task 07 owns this residual; the api-contracts and agent-lifecycle
subsystem contracts now record the production trigger. The
rg-07-durable-delivery gate suite stays green, and a new router-level
test pins that a receipt-pending executing action completes from the
agent receipt without a second dispatch.
2026-07-14 14:19:19 +01:00
rcourtman 3c778e2b26 Restore one-click Docker container updates through the typed action plane
v6.1.0-rc.1 retired the legacy update endpoints before a replacement
existed, so the UI's Update button failed with an internal-jargon 410
(issue #1564). This lands the replacement end to end: update_container
is a typed agentexec operation with its own strict codec, durable
receipts, and a request digest bound to the image digest the plan
observed; the unified agent bridges execution to the Docker module's
existing pull/backup/recreate/verify/rollback implementation (which now
reports rollback attempt and outcome); and the container action
executor plans, dispatches, and reconciles the operation with declared
backup/rollback compensation truth. Containers advertise an
admin-approval update capability while an image update with a stated
current digest is detected. The legacy endpoints stay retired but
return actionable copy.

Proven live against a Colima daemon: single-container update, the
issue-1564 shared-network-namespace update, and the full UI journey
(Update button, governed review, approve, run) all completed with the
namespace preserved and the backup retained.
2026-07-14 12:19:04 +01:00
rcourtman 0062128414 Add durable Docker restart lifecycle proof 2026-07-12 12:11:16 +01:00
rcourtman 4aac79dc72 Add durable agent operation receipts 2026-07-12 04:16:22 +01:00
rcourtman e77ab9518d Add safe APT workflow foundations 2026-07-12 02:53:01 +01:00
rcourtman 4edf5f8265 fix: close chat command authority boundary 2026-07-11 11:54:56 +01:00
rcourtman eb9954618a Add governed storage pressure cleanup 2026-07-11 10:11:35 +01:00
rcourtman 1312da3acb Add governed host update autonomy 2026-07-11 01:25:14 +01:00
rcourtman 61c8e8ca00 Make agent command-exec token rejection actionable, not a silent 'Invalid token'
An agent enrolled for metrics but whose token the server doesn't recognise (or
that lacks the agent:exec scope, or is bound to a different agent) was rejected
on the command-exec WebSocket with a bare 'Invalid token' and — for the
token-not-found case — no server log at all. The agent then retried forever,
logging only 'Invalid token', so the operator had no signal that discovery
deep-scan was failing or why. (Confirmed live: delly/minipc agents pointed at a
backend that didn't recognise their token retried thousands of times; discovery
abstained for every guest as a result.)

- agentexec/server.go: the registration-rejection message the agent logs
  verbatim now says 'agent token not authorized for command execution — re-run
  the agent installer to enroll an agent:exec-scoped token'.
- api/agent_exec_token_binding.go: the previously-silent token-not-recognised
  branch now logs the specific reason with the agent hostname.

Contract-neutral: same rejection behaviour, just legible. Regression test:
TestHandleWebSocket_RejectionMessageIsActionable. Verified live end-to-end.
2026-06-09 17:55:09 +01:00
rcourtman 06fd4fc89e Bypass approval gate for trusted internal Discovery commands
Discovery wraps every probe in `docker exec <container> sh -c '...'`.
The agentexec command policy lists `^docker\s+exec\s` as RequireApproval
(a sound default for user-driven docker exec) and Discovery has no path
to mint or supply an ApprovalID. Result: every probe was rejected, the
scanner returned empty CommandOutputs, and the AI fell back to
"Unknown Infrastructure Resource" at confidence 0. The Discovery sub-tab
rendered empty after a "successful" run.

Add a Trusted bool to ExecuteCommandPayload on both the server-facing
agentexec type and the agent's wire struct. When set, the approval gate
is skipped on both ends and the server does not attempt to auto-mint an
approval grant (which would fail with "approval id is required").
PolicyBlock still applies; this is not a way to run arbitrary commands.

Only the discoveryCommandAdapter sets Trusted=true. The flag is never
populated from a deserialised HTTP body or any user-driven path. Patrol
fixes, Assistant remediation, and AI tool calls continue to flow through
the governed approval-record path with a real ApprovalID.

Contracts: amend agent-lifecycle Completion Obligations and Current
State to document the lone exception to the on-agent approval rail, and
amend ai-runtime to fence the Trusted flag to the discovery adapter
only.
2026-05-17 21:59:39 +01:00
rcourtman 9bada35337 Harden unified agent runtime and installer 2026-04-23 23:04:18 +01:00
rcourtman 60d7db6ef9 Harden agentexec token binding and disk filtering 2026-04-23 15:54:48 +01:00
rcourtman c1d0d34c16 Cap agent exec websocket connections per IP 2026-04-22 07:22:44 +01:00
rcourtman ccb2edc3b8 Require explicit websocket origin continuity 2026-04-22 04:46:13 +01:00
rcourtman 3ec2c0779e Harden agent command and deploy trust boundaries 2026-04-21 23:50:34 +01:00
rcourtman 62ec34ef02 Route hostname lookups through canonical equivalence 2026-04-21 22:47:23 +01:00
rcourtman 05fa111ca1 Stabilize backend race tests for v6 RC publish 2026-04-11 22:46:34 +01:00
rcourtman 778a2577b6 feat: Pulse v6 release 2026-03-18 16:06:30 +00:00
rcourtman b7a94bad9f security: fix websocket scope and agent impersonation
1. Enforce monitoring:read scope on WebSocket upgrades
   - Prevents low-privilege tokens (e.g. host-agent:report) from accessing
     full infra state via requestData on the main WebSocket.

2. Enforce agent token binding to prevent impersonation
   - Added Metadata field to APITokenRecord to support bound_agent_id
   - Updated agentexec server to validate token-to-agent binding if present
   - Prevents agent:exec tokens from registering as arbitrary agent IDs
2026-02-03 20:40:08 +00:00
rcourtman c6bd8cb74c Improve internal package test coverage 2025-12-29 17:25:21 +00:00
rcourtman 83cb858c15 fix(agentexec): eliminate race conditions in WebSocket message sending
- Move deadline/pong handler setup BEFORE registering agent in map
- Use writeMu mutex consistently for all WebSocket writes
- Prevents race between registration response and ExecuteCommand calls
- Fixes flaky TestExecuteCommand_RoundTripViaWebSocket in CI
2025-12-23 20:05:46 +00:00
rcourtman ae7b66ecff refactor(ai): Remove over-engineered URL discovery service
Keep only the simple AI-powered approach:
- set_resource_url tool lets AI save discovered URLs
- Users ask AI directly: 'Find URLs for my containers'
- AI uses its intelligence to discover and set URLs

Removed:
- URLDiscoveryService (rigid port scanning)
- Bulk discovery API endpoints
- Frontend discovery button

The AI itself is smart enough to iterate through resources
and discover URLs when asked.
2025-12-10 08:35:24 +00:00
rcourtman 8948e84fe5 feat: AI features, agent improvements, and host monitoring enhancements
AI Chat Integration:
- Multi-provider support (Anthropic, OpenAI, Ollama)
- Streaming responses with markdown rendering
- Agent command execution for remote troubleshooting
- Context-aware conversations with host/container metadata

Agent Updates:
- Add --enable-proxmox flag for automatic PVE/PBS token setup
- Improve auto-update with semver comparison (prevents downgrades)
- Add updatedFrom tracking to report previous version after update
- Reduce initial update check delay from 30s to 5s
- Add agent version column to Hosts page table

Host Metrics:
- Add DiskIO stats collection (read/write bytes, ops, time)
- Improve disk filtering to exclude Docker overlay mounts
- Add RAID array monitoring via mdadm
- Enhanced temperature sensor parsing

Frontend:
- New Agent Version column on Hosts overview table
- Improved node modal with agent-first installation flow
- Add DiskIO display in host drawer
- Better responsive handling for metric bars
2025-12-05 10:37:02 +00:00
rcourtman 53d7776d6b wip: AI chat integration with multi-provider support
- Add AI service with Anthropic, OpenAI, and Ollama providers
- Add AI chat UI component with streaming responses
- Add AI settings page for configuration
- Add agent exec framework for command execution
- Add API endpoints for AI chat and configuration
2025-12-04 20:16:53 +00:00