Commit Graph

9273 Commits

Author SHA1 Message Date
rcourtman d18fcf55ad Bucket agent candidates for the child-count parent pass
The derived-parent recompute in buildChildCounts may hit the agent
fallback scan once per guest, and that scan walked every resource with
per-entry string checks, an O(n^2) pass per registry ingest. The pass
now buckets agent resources by node name up front and the fallback
walks only the matching bucket. Selection is order-independent (best
score with smallest-ID tie-break) and scores read live through shared
pointers, so results are unchanged; callers outside the pass keep the
full scan.

Contract-Neutral: performance-only scan bucketing in the parent recompute pass, selection semantics unchanged
2026-08-16 22:01:55 +01:00
rcourtman 81de801e58 Batch incident recorder metrics lookups
The incident recorder asks for current metrics for every monitored
resource every five seconds, and the adapter answered each request by
scanning all views with a formatted VMID comparison per candidate. At
thousands of resources that is millions of comparisons and allocations
per tick, forever. The adapter now exposes a batch method that builds
the full lookup in one pass over the views, keyed by every ID form the
per-ID method matches with the same first-match precedence, and the
recorder uses it when the provider offers it. Per-ID behavior is pinned
to the batch by an equivalence test, including VMID collisions.

Contract-Neutral: performance-only batch lookup addition, per-ID metrics resolution is pinned unchanged by equivalence tests
2026-08-16 21:48:31 +01:00
rcourtman 7f444fc29a Prune the top-level system hostname fallback candidate scan
The fallback attachment pass compared every group against every other
group with pairwise hostname normalization, allocating sorted root and
host sets per call. Resolving top-level systems runs inside the state
request path, so large estates paid a quadratic normalization storm per
request. Hostname equivalence requires the two names to share either a
comparable form or a short form, so an index of groups by those forms
prunes each group's candidates to the provable superset and the
original pairwise matcher still decides every surviving pair. Attachment
decisions are unchanged.

Contract-Neutral: performance-only candidate pruning, the original matcher still decides every attachment
2026-08-16 21:48:31 +01:00
rcourtman fc50f61cdb Index source targets instead of scanning every mapping per resource
Rebuilding the registry view caches resolved each resource's metrics
target by scanning every bySource mapping, an O(resources x mappings)
pass holding the write lock. At about five thousand resources that is
tens of millions of map iterations per rebuild, and rebuilds run on
every poll cycle. The rebuild now inverts the mappings once and the
cached index also serves single-resource lookups while the views are
clean, with the legacy scan kept for dirty-state callers. An
equivalence test pins index results to the legacy scan.

The agent-parent fallback scan also ran its per-entry string
canonicalization before the type check, so every guest in the estate
paid allocation cost in a scan that only ever matches agents. The
cheap checks now run first.

Contract-Neutral: performance-only rework of metrics-target resolution and scan ordering, resolved values are pinned unchanged by equivalence tests
2026-08-16 21:48:31 +01:00
rcourtman 90081dacb4 Handle informational and bodyless gzip responses 2026-08-16 17:24:01 +01:00
rcourtman 2e4e85d9af docs: add focused security review packet 2026-08-16 17:13:02 +01:00
rcourtman a682af0b7b Harden large-estate response and polling limits 2026-08-16 17:07:59 +01:00
rcourtman d08ca46194 Expose Docker health-check dependencies to Patrol 2026-08-16 16:58:44 +01:00
rcourtman 25368abcdb Merge remote-tracking branch 'origin/main' 2026-08-16 16:52:08 +01:00
rcourtman bbd67e0ae0 docs: clarify security review scope 2026-08-16 16:28:19 +01:00
rcourtman c4b736850e Merge origin/main into main
Contract-Neutral: merge reviewed origin/main changes without altering their contracts
2026-08-16 16:02:38 +01:00
rcourtman 40897a46e6 Preserve causal evidence in Patrol conclusions 2026-08-16 15:48:03 +01:00
rcourtman ab34168731 Disambiguate Docker service evidence for Patrol 2026-08-16 15:26:12 +01:00
rcourtman 71347eac4c Decide gzip eligibility before a pre-write flush commits headers
A handler that flushes before its first write commits the response
headers with an implicit 200. The gzip wrapper deferred its
compress-or-not decision to the first write, so such a handler would
send headers without Content-Encoding and then a compressed body,
which clients cannot decode. Every current flusher is an SSE handler
whose content type never compresses, so this was latent, but any
future streaming JSON endpoint would have corrupted silently. Flush
now runs the decision first, matching net/http header-commit
semantics.
2026-08-16 14:08:55 +01:00
rcourtman 3fe1f7f181 Quiet the broadcast ticker and allow larger poll worker pools
The broadcast ticker logged seven fields at Info level every tick and
built a full state snapshot to do it, before checking whether any
WebSocket subscriber existed. An idle server paid a snapshot copy plus
roughly 8600 log lines a day for nothing. The log is now Debug and both
the log and the snapshot build sit behind the subscriber check.

The poll task worker pool was also fixed at ten workers no matter how
many instances an estate monitors. POLL_TASK_WORKERS now overrides the
count and the cap, bounded at 128, following the existing env knob
pattern. Behavior without the variable is unchanged.

Contract-Neutral: operational log demotion and an opt-in worker pool env knob, no monitoring or agent-lifecycle contract semantics change
2026-08-16 13:24:08 +01:00
rcourtman 17ccb8240d Preserve causal evidence before Patrol proposals 2026-08-16 11:45:24 +01:00
rcourtman 64db84a7e3 Clarify governed restart proposal policy 2026-08-16 11:32:36 +01:00
rcourtman 870e2f7f97 Rewire Patrol investigation after chat restart
Contract-Neutral: AI chat lifecycle rewiring does not change dependent agent, performance, security, or storage contracts
2026-08-16 11:15:16 +01:00
rcourtman 4195943300 Harden Patrol investigation evidence traversal 2026-08-16 10:56:34 +01:00
rcourtman 85db074eff copy: simplify security guidance punctuation
Contract-Neutral: Punctuation-only copy revision with no lifecycle behavior or contract change
2026-08-16 10:36:54 +01:00
rcourtman 20cd89dcc2 Raise the client WebSocket frame ceiling to 32 MiB
The browser advertises max_message_bytes on the upgrade request and the
server withholds any state frame larger than that, falling back to REST
recovery. The advertised value was still the historical 8 MiB, which
pushed estates past roughly 3100 resources off the cheap socket delta
path and onto a 30 second full-state REST poll loop. 32 MiB keeps
estates about four times that size on socket deltas. The server honors
whatever the client advertises, so no backend change is needed.


Contract-Neutral: transport frame-limit constant, no alert lifecycle semantics change
2026-08-16 10:28:30 +01:00
rcourtman 205df735c7 Compress API responses for clients that accept gzip
Large estates that exceed the WebSocket frame ceiling recover over an
uncompressed /api/state poll loop, which made the biggest deployments
pay the heaviest transfer cost. Wrap the main HTTP handler in a gzip
middleware that compresses JSON and other text responses roughly an
order of magnitude, while leaving WebSocket upgrades, SSE streams,
Range requests, small declared bodies, and binary content untouched.


Contract-Neutral: transport-only gzip response middleware, no RBAC or persistence semantics change
2026-08-16 10:28:08 +01:00
rcourtman 787a191a5e docs: publish security review evidence 2026-08-16 10:23:15 +01:00
rcourtman effcf2d50c Harden Patrol qualification lifecycle 2026-08-16 10:14:16 +01:00
rcourtman 88c380b514 docs: add production security deployment guide 2026-08-16 09:21:36 +01:00
rcourtman d0e7c747d9 Clear stale ZFS alerts when a storage loses its pool attachment
checkZFSPoolHealth only runs while storage.ZFSPool is attached, so its
clear paths never fire once the attachment goes away and previously raised
zfs-pool-state, zfs-pool-errors, and zfs-device alerts linger until the
multi-day stale cleanup. Clear them on the spot when a storage is checked
without a pool. This is the recovery path for the shared storages that were
wrongly matched to a node-local pool before the matcher's type gate.

Refs #1731
2026-08-16 06:27:23 +01:00
rcourtman 1ff0680c75 Stop attaching node-local ZFS pools to shared storages
matchZFSPoolForStorage's sole-pool fallback attached the node's only ZFS
pool to every storage the per-node endpoint returned, including NFS, CIFS,
PBS, and RBD entries that can never be backed by a local pool. One failing
device then raised a duplicate ZFS device alert per shared storage. Gate
the matcher on isInherentlySharedStorageType so remote-backed storages are
never matched, while dir-type storages keep the single-pool fallback.

Refs #1731
2026-08-16 06:22:55 +01:00
rcourtman 991b3cad35 Reject empty and blocked Patrol evidence 2026-08-15 22:56:51 +01:00
rcourtman 3cbd684fd4 docs: refine AI transparency wording 2026-08-15 22:55:36 +01:00
rcourtman acef44e162 Restrict Patrol grounding to infrastructure evidence 2026-08-15 22:16:00 +01:00
rcourtman b088319589 Validate all Patrol investigation manifests 2026-08-15 21:37:05 +01:00
rcourtman b3a9edd313 Require successful Patrol investigation evidence 2026-08-15 21:19:44 +01:00
rcourtman 857af3e3cc Keep scoped Patrol investigations grounded 2026-08-15 20:58:49 +01:00
rcourtman 1933a5be37 Align Patrol preflight with the live context envelope 2026-08-15 13:12:30 +01:00
rcourtman 12aa31b506 Keep live Patrol context aligned with readiness 2026-08-15 13:05:15 +01:00
rcourtman 80027e98d6 Project Patrol tools from resource scope 2026-08-15 12:38:41 +01:00
rcourtman 2a15ccbefa Reject unadvertised Patrol tool calls 2026-08-15 12:15:17 +01:00
rcourtman 0d0456f435 Require grounded Patrol investigations 2026-08-15 11:48:51 +01:00
rcourtman 73ce59a1ab Recover Patrol after provider circuit failures 2026-08-15 11:37:02 +01:00
rcourtman 3a99f72a5c Ground Patrol fallback summaries in evidence 2026-08-15 11:01:10 +01:00
rcourtman 2fd184315d Simplify and harden verified Patrol receipts
Contract-Neutral: Patrol compact receipt payload does not change agent lifecycle or storage recovery contracts
2026-08-15 10:25:04 +01:00
rcourtman d3a345bd66 Show verified Patrol work receipts
Contract-Neutral: Patrol receipt API does not change agent lifecycle or storage recovery contracts
2026-08-15 08:48:49 +01:00
rcourtman 7fb39f65cf Honor OpenRouter reasoning limits 2026-08-15 07:04:42 +01:00
rcourtman 746990db34 Require typed Patrol remediation proposals 2026-08-15 06:39:48 +01:00
rcourtman fe1f8d7582 Restrict Patrol objective recovery scope 2026-08-15 06:05:34 +01:00
rcourtman 9ebf084bae Recover truncated Patrol objective missions 2026-08-15 05:48:00 +01:00
rcourtman 3c97a04222 Make Patrol observer missions proposal-only 2026-08-15 05:15:00 +01:00
rcourtman 4efc428bb8 Keep Patrol objectives mission-scoped 2026-08-15 05:00:09 +01:00
rcourtman d218b012d1 Preserve canonical Patrol objective scopes 2026-08-15 04:30:09 +01:00
rcourtman 88de80022c Require Patrol to submit supported actions 2026-08-15 03:53:03 +01:00