Commit Graph

1812 Commits

Author SHA1 Message Date
rcourtman 8ea94d792b Add the Patrol weekly digest endpoint
Nothing in Pulse showed a paying customer what Patrol had done for them:
about 164 runs a month per install, findings raised, investigations and
fixes, and none of it summarised. GET /api/ai/patrol/digest rolls the last
N days (default 7, max 30) up from records Pulse already keeps: run
history, the findings store, Patrol-origin action audits, and the usage
cost store. It adds no telemetry and persists nothing. The payload
reports when the bounded run history no longer covers the window and when
model pricing is unknown, rather than quietly under-counting.

This is the first slice of the "Patrol weekly digest" named bet in the
pulse-pro demand ledger; the in-app "This week" card follows once its
browser pass is recorded. docs/PATROL_WEEKLY_DIGEST.md holds the design
note and the honest limits of each line.

status.json registers the patrol-value-visibility coverage gap, the
candidate lane, and its work claim. It also drops the second, identical
copy of the ai-provider-guided-setup coverage gap that landed with #1853;
the duplicate id fails the status audit on main for every pull request.
2026-09-02 08:53:50 +01:00
rcourtman ac328e1eee Guide the Patrol model choice with cost preview and budget pause
Forty-two percent of paying installs never get Patrol running, and the
support and issue evidence says the wall is model choice and cost: a
prospect asked which models Pulse recommends before trying, a Pro customer
picked Flash-Lite to save money and Patrol then could not file verdicts,
and a mispriced Opus row tripped the 20 USD budget so the circuit breaker
disabled Patrol with nothing but a log line (#1789).

Answer both questions at the point of choice. The Patrol and shared
default model pickers pin guided models in a "Suggested for Patrol"
section with recommended / suggested / caution markers and a one-line
reason: the Ollama preflight blessing is the only recommended entry,
Gemini Flash-Lite the only caution, and cloud starting points are labelled
price-driven and unqualified until this install's own readiness pass
upgrades them to "Verified on this install". A server-computed cost
preview sits under the model choice: monthly estimate from Pulse's price
table, the schedule, and the install's own median full run once three
priced runs exist (a measured 104,528-in / 4,491-out run otherwise), with
the assumption stated and a token explained once, plus 30-day spend
against budget. Each schedule preset is priced in the Schedule select.

When a per-token model is picked while the schedule is still the 6-hour
default, the cost model proposes the slowest preset that keeps scheduled
runs under half the budget (20 USD reference when none is set) and says
what the slower check costs in detection delay; a schedule the install
already chose is never changed.

A used-up budget is now a spending decision, not a provider fault: the
budget refusal is a typed sentinel with the figures, classified as the
budget_exhausted cause, excluded from circuit-breaker accounting, and
promoted into the Patrol block state, so the Patrol page shows "Patrol
paused" with the spend and limit and a "Raise the cost budget" action
instead of "Check Patrol model".
2026-09-02 07:55:42 +01:00
pulse-triage[bot] 0e341583cb Align shared-token uninstall API regression
Change-source: pulse-maintainer
2026-09-01 17:41:43 +01:00
pulse-triage[bot] c9e6a8346b Preserve shared unified agent credentials
Change-source: pulse-maintainer
2026-09-01 17:34:30 +01:00
pulse-triage[bot] 78023e0e42 Serve signed Windows agents from canonical assets
Change-source: pulse-maintainer
2026-09-01 16:16:56 +01:00
pulse-triage[bot] 9a5af6ff79 Keep Windows agent update signatures addressable
Change-source: pulse-maintainer
2026-09-01 16:11:04 +01:00
rcourtman 53267e149d Harden least-privilege installer lifecycle state 2026-09-01 15:53:02 +01:00
rcourtman 3c31aa4805 Stop Docker records flip-flopping under shared same-hostname tokens
The Docker analog of the #1753 estate was still broken: two live
machines reusing one short hostname and one pasted unified install
token collapsed into a single flip-flopping DockerHost record, because
the hostname+token identity fallback adopts a record whose machine ID
disagrees with the report's. That fold is deliberate for recreated
containers (whose /etc/machine-id regenerates), so it cannot simply be
guarded by machine-ID inequality - the discriminator is a revisit: a
recreated container transitions to its new machine ID exactly once,
while two live machines alternate. Removing the collapsed record then
revoked the shared token unconditionally, rejecting every surviving
module - host reports included, since a unified install shares one
credential - with 401 "Unauthorized access attempt".

Consult the identity flap tracker before the hostname fallbacks adopt a
machine-ID-disagreeing record: a report whose machine ID returns to a
value already seen behind that identity is a second live machine and is
not folded. The machine whose identifiers minted the record reclaims it,
so the first site keeps its record and history, and the other site falls
through to the token binding check, converging on the documented "Each
Docker / Podman module must use a unique API token" rejection instead of
silently overwriting the record every cycle. RemoveDockerHost now skips
token revocation while any host or Docker record still authenticates
with the credential, mirroring the host-agent removal guard.

Regression coverage: an end-to-end router test walking the two-site
shared-token estate (host + Docker reports, alternating cycles, removal)
asserting the first site's identity stays stable, the second site gets
the unique-token guidance, and the shared token survives removal; a
router test proving removal of a machine's Docker record keeps the
unified token its host record still uses; and a state-layer test pinning
the reclaim/no-flip-flop convergence. Recreated-container adoption and
the existing token-uniqueness rejections keep their tests unchanged.
2026-09-01 15:26:48 +01:00
rcourtman a49a4e65d1 Stop shared-token same-hostname agents collapsing identities
Two standalone Proxmox sites that reuse one short node name and one
pasted install token still folded into a single host record: the
token+hostname binding and the continuity store's hostname+token
fallback both resolve identity without consulting the machine ID, so
the second site's reports adopted the first site's host ID and the
record flip-flopped between machines. Removing that collapsed record
(the agent uninstaller does this on reinstall) then revoked the shared
token because the still-used guard could only see one record, leaving
every surviving agent rejected with 401 "Unauthorized access attempt"
on /api/agents/agent/report - the new symptom reported on #1753 after
the v6.4.2 retest.

Qualify host token binding slots with the reporting machine's identity
so two machines behind one token+hostname hold separate bindings,
refuse to adopt a bound or continuity identity whose recorded machine
ID disagrees with the report's, and rebuild bindings machine-qualified
after token reloads. Reports without a machine ID keep the legacy
binding shape and same-machine reinstalls keep their stable IDs.

Regression coverage: a state-layer test proving two shared-token pve01
machines keep distinct stable identities, continuity-match tests for
the machine guard, and an end-to-end router test walking the reporter's
estate - both install patterns, alternating reports, uninstall and
reinstall - asserting report auth never breaks and no token is revoked
while its sibling site still uses it.
2026-09-01 14:36:49 +01:00
pulse-triage[bot] d083f50fbc Classify oversized agent reports correctly
Change-source: pulse-maintainer
2026-09-01 13:59:36 +01:00
pulse-triage[bot] 12b90e2e5e Keep Proxmox agent links within provider scope
Change-source: pulse-maintainer
2026-09-01 13:17:41 +01:00
pulse-triage[bot] b49b3d89f4 Merge remote-tracking branch 'origin/main'
Change-source: pulse-maintainer
2026-09-01 08:18:37 +01:00
rcourtman 2ad094c927 Deflake typed action containment tests
The group-writable executable check wrote its fixture through os.WriteFile,
which filters the mode through the process umask, so under the runner's 022
umask the group-write bit never reached disk and validateTrustedExecutable
correctly trusted the file. Chmod the fixture to the asserted mode.

The Proxmox guest executor tests stamped their after observations one second
past the test-start clock, but actionStartedAt is stamped inside
ExecuteAction after handler setup, so a loaded runner overran the margin and
the independent observation was discarded as pre-action. Widen the
observation offset to a minute.

Both failures broke build-and-test on main (run 33454838531). Reproduced the
hostagent failure on Linux under umask 022 and verified both packages green
after the fix.
2026-09-01 06:15:46 +01:00
pulse-triage[bot] c253495045 feat: add fleet health home
Change-source: pulse-maintainer
2026-09-01 01:02:17 +01:00
rcourtman 4724c0764b Fence runner activation session authority 2026-08-31 23:52:25 +01:00
rcourtman 0c76b5d756 Stabilize API test log capture 2026-08-31 19:47:01 +01:00
rcourtman 17a1c2096d Decouple WebSocket contract tests from monitor workload 2026-08-31 17:59:38 +01:00
pulse-triage[bot] 1bb23545b6 Require TrueNAS API key owner identity
Change-source: pulse-maintainer
2026-08-31 17:09:53 +01:00
rcourtman bbdc2d5413 Stabilize WebSocket contract tests under release load 2026-08-31 16:32:56 +01:00
rcourtman 669cce6787 Restore main CI after guardrail and contract drift
Three consecutive main runs were red because landed features outran their
guardrails while change detection kept the affected shards out of their own
runs.

- Resync shipped copies of API.md, CODE_SIGNING_POLICY.md, and
  PRODUCTION_SECURITY.md from repo docs.
- Track the schema v16 telemetry disclosure (workload-history adoption
  counts) in the settings architecture guardrail.
- Track route-scoped TrueNAS hydration queries and the conditional Kind
  column class in the platform overview layout guardrails.
- Track the contained command-authority copy (legacy combined command
  profile) in the installer and setup guide tests.
- Match the sentence-split delivery retention copy in AlertDeliveryLogCard.
- Provide selectedAgentUrl, insecureMode, and customCaPath in the
  InfrastructureWorkspace operations mock for the safe-collector commands.
- Include the resource list facets block in the two API contract snapshots,
  normalizing the direct-marshal fixture the way every handler does.
- Require the fail-closed installer checksum wording in the
  AGENT_SUBSTRATE doc contract.
2026-08-31 06:26:53 +01:00
pulse-triage[bot] 92b5f3a9e1 Bound security request decoding 2026-08-31 01:43:55 +01:00
Richard Courtman 79967177ea Enforce explicit SSO administrator grants 2026-08-31 00:43:42 +01:00
pulse-triage[bot] 0f2d7ae055 Make notification delivery failures actionable 2026-08-31 00:39:53 +01:00
Richard Courtman 08f7c5f0d5 Harden secure agent runtime boundaries 2026-08-31 00:06:24 +01:00
Pulse Test 16a1574a18 Add multi-location service delivery evidence 2026-08-30 21:44:32 +01:00
Richard Courtman d9e9bf3679 Prove production action runner credential lifecycle 2026-08-30 21:03:37 +01:00
Richard Courtman 77afff4f60 Bind action runner to enrolled agent identity 2026-08-30 18:49:12 +01:00
Pulse Test 6ee3b7e24e Add discovery-led service assurance onboarding 2026-08-30 18:40:06 +01:00
Richard Courtman 770733fc92 Make action runner rotation activation-safe 2026-08-30 18:00:33 +01:00
Pulse Test c0b190bfa1 Add application response contracts to availability checks 2026-08-30 17:38:59 +01:00
Richard Courtman 34849268e5 Enforce collector credential scope allowlists 2026-08-30 17:18:38 +01:00
pulse-triage[bot] 323da54067 Support agent retargeting after server moves 2026-08-30 16:17:15 +01:00
Pulse Test 712f1a3bfe Add availability history and fleet view 2026-08-30 15:38:34 +01:00
Pulse Test 7e92ac8118 Harden secure runtime separation boundaries 2026-08-30 14:26:01 +01:00
Pulse Test 161e322ee0 Add workload history discovery lens 2026-08-30 13:49:58 +01:00
pulse-triage[bot] 68887f7114 Bound chart response cache retention 2026-08-30 12:55:50 +01:00
Richard Courtman 87fda2a235 Scope provider inventory hydration by route 2026-08-30 09:32:35 +01:00
pulse-triage[bot] 74cf2c1283 Require authority for infrastructure actions 2026-08-30 08:36:31 +01:00
pulse-triage[bot] ac2a2fe020 Complete release and helper download safeguards 2026-08-30 01:51:58 +01:00
Pulse Test d06ffc233d Harden secure agent runtime transitions 2026-08-30 01:41:57 +01:00
Pulse Test 5d3571dbe8 Scope remaining provider inventory hydration 2026-08-30 00:42:18 +01:00
Pulse Test 5b2e377843 Expose secure agent runtime posture 2026-08-30 00:38:44 +01:00
Pulse Test 54c3cdc061 Let fresher evidence decide merged connection member state
When the Proxmox node inventory and the unified resource registry both
project the same machine as a system member, the merge paired the most
severe state from one plane with the newest LastSeen from the other. A
lapsed projection, such as an orphaned registry entry left behind by a
remove/re-enroll cycle, could therefore permanently render a member as
Stale beside a heartbeat seconds old, and the cluster rollup inherited
the contradiction onto the parent row.

The plane that has seen the machine more recently now decides liveness.
Severity still decides when neither side carries evidence or the
timestamps tie, so genuinely offline members keep their state.

Refs #1728
2026-08-30 00:02:15 +01:00
Pulse Test d607d5cf46 Separate agent remediation runtime 2026-08-29 23:48:28 +01:00
Pulse Test 6d4ee48000 Add typed agent privilege helper 2026-08-29 22:51:58 +01:00
pulse-triage[bot] 1a48d3cbd6 Honor namespaced cgroup memory limits 2026-08-29 22:23:54 +01:00
Pulse Test a966264bb1 Contain agent command authority 2026-08-29 22:12:41 +01:00
pulse-triage[bot] e3c0990e15 Stabilize release validation boundaries
Authenticate global API tokens before cold tenant runtime resolution, lock the cross-org behavior into integration coverage, and refresh schema-14 telemetry browser proof.

Contract-Neutral: Restores bounded cross-org token authorization and records verification without changing the API contract

Change-source: pulse-maintainer
2026-08-29 14:54:04 +01:00
pulse-triage[bot] 9e8cdde75c Keep first-run credential reset atomic
Commit restart-time token revocation before clearing live authentication or sessions. Restore auth environment files and remove the staged bootstrap credential when token persistence fails, preserving a usable retry path.

Contract-Neutral: hardens development reset failure handling without changing successful API payloads or extension contracts
2026-08-29 06:16:52 +01:00
pulse-triage[bot] 56fc9b8255 Keep token issuance atomic on persistence failure
Restore the complete live token inventory when durable writes fail during legacy regeneration, quick security setup, and container-runtime token preparation. Return an error instead of exposing or claiming an unpersisted credential.

Contract-Neutral: hardens internal failure handling without changing successful API payloads or extension contracts
2026-08-29 05:19:19 +01:00