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.
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".
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.
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.
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.
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.
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
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
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
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