Commit Graph

781 Commits

Author SHA1 Message Date
pulse-triage[bot] 105fb05cc3 Decode native retained metric numbers without per-point reflection
Keep database/sql conversion for atypical values and rejection of NULL, malformed and overflowing observations. Compare native and fallback scans against primitive destinations and protect the allocation-free native path.

Contract-Neutral: Retained observations, numeric acceptance and rejection, SQL, sampling, source choice, extrema and access boundaries are unchanged; only application-side numeric assignment avoids per-observation reflection.
Change-source: pulse-maintainer
2026-10-24 17:32:56 +01:00
pulse-triage[bot] c36672b2a7 Assemble retained metric series directly by statement-local number
Avoid per-observation string resolution and nested-map flushes for interleaved filtered fleets. Bind only nonempty series after each read and bound the direct buffers, preserving the existing wide-sparse fallback, SQL snapshots, tiers and exact observations. Verify against single-resource readers on both layouts.

Change-source: pulse-maintainer
2026-10-24 13:38:25 +01:00
pulse-triage[bot] 35af9de7f5 Transport filtered retained metrics by statement-local series number
Reuse bound resource and metric identities instead of decoding a metric string and a resource number for every raw fleet observation. Preserve scoped tier reconciliation, nullable values, caller ownership and display aggregation. Verify reordered bindings, both layouts/projections and filtered chunk boundaries; keep raw reads as one consistent statement.

Change-source: pulse-maintainer
2026-10-24 11:51:02 +01:00
pulse-triage[bot] 5a852abe66 Compose native SCALE CPU repair with current main
Retain main's guest-agent fixture isolation in the two overlapping hunks, the deterministic recycled-listener regression, and main's additional per-field fixture fix. Import the unchanged native aggregate CPU repair and pinned loser observation correction. No production guest safeguards change.

Change-source: pulse-maintainer
2026-10-24 02:12:05 +01:00
rcourtman 450ed161c6 Isolate guest-agent test fixtures from recycled ports (#3163)
Guest-agent fences intentionally outlive a closed fixture listener and
are keyed by endpoint URL. A test whose client used a fixture's raw
loopback URL therefore inherited an earlier case's cooldown whenever the
kernel handed its server a recycled port, and its first read came back
`agent-cooldown` instead of what the test expected. `Backend tests
(rest-0)` failed this way twice in a row on rcourtman/Pulse#3151
(`TestGuestAgentConfigResponseMustBeComplete`, then
`TestGuestAgentTypedSuccessDefersEveryRead`), and one in twelve local
runs of the guest-agent tests failed the same way.

- Single-server and per-case fixtures now use
`guestAgentFixtureEndpoint`, the package's existing per-case boundary
that the HTTP status and lock evidence tests already use. Clients within
one case still share their key.
- Tests whose clients share fences across raw endpoints by design (the
alias and cluster tests), and the older client and cluster API tests
that read guest data through helpers that own their server, start from
an empty guest registry (`isolateGuestAgentCapacity`) instead.
- The capacity tests keep raw endpoints: they seed protected work under
them and already reset the registry. Converting them breaks what they
prove.
- Inventory, node and config fixtures have no guest fences and are
unchanged, as is the untrusted-TLS test, which sends its requests
outside the guarded reads.

Testing
- On pulse-dev, before: 1 of 12 fresh-process runs of the guest-agent
tests failed. Converting only the two tests that failed in CI moved the
failure to a third test (`TestGuestAgentWireDoesNotFollowRedirect`).
After this change: 0 of 20 runs of the guest-agent, cluster-client and
client tests failed.
- `go test ./pkg/proxmox` passes with and without `-race`, gofmt and vet
are clean, and the registry, contract, completion and browser guards
pass on this commit.
2026-10-24 01:51:59 +01:00
rcourtman 871d94f53d Serialize Patrol action projection and finalize hung runs (#3151)
Three gaps remained in how Pulse projects a Patrol investigation's
action and closes its runs, after rcourtman/Pulse#3147.

- **Projections are serialized per investigation.** The lifecycle's
transition callback, an investigation read and a run's close each read
the action audit and apply it under the investigation's lock. A run's
close also finalizes before releasing it. A projection read from an
older state can no longer land after a newer one, and an approval can no
longer land between a run's reconciliation and its finalization.
- **The projection writes only what changed.** It records the changed
action and outcome through `SetAction` and `SetOutcome` when the store
offers `aicontracts.InvestigationActionStore` (a new optional interface;
a store without it keeps the old `Update`). It therefore cannot undo run
progress the orchestrator saved in between. The reference it writes
carries the broker's canonical result and verification status
(`dispositionFromRecord`) and keeps the model's causal attribution,
which the audit does not hold.
- **Runs that hang past their limit are finalized.** The stuck-run sweep
finalizes such a run with a copy of its session marked failed, leaving
the orchestrator's own session as it is. Once the orchestrator closes
the run itself, its stored session is finalized instead of the copy, and
a later refresh no longer reopens a record that finalization closed.
- **A recovered run keeps its own status** (second commit). The sweep
used to mark a run that a restart or its limit cut short after it
planned a fix as `completed`, because the finding already held the
action's outcome, while its session and record said `failed`. The
orchestrator records the same run as `failed` when it closes itself, and
every gate against a second plan keys on the `fix_queued` outcome, so
the sweep now keeps the outcome and the run's own status.

The matching pulse-enterprise change makes the orchestrator's saves keep
the action Pulse recorded, and gives its store `SetAction`. Each side
works without the other.

Testing
- New tests cover the per-investigation lock, field-only writes,
hydration returning the stored action after a refresh replaced the plan,
a close preferring the orchestrator's own close over the sweep's copy
and finalizing under the lock, a run past its limit finalized as given
up, a refresh not reopening a finalized record, the full action
reference, and a recovered run keeping the status it closed with.
Removing any one of the fixes fails its test.
- On pulse-dev: gofmt, vet, race runs of the targeted tests, and full
runs of `internal/ai/...`, `internal/api`, `internal/actionlifecycle`,
`internal/unifiedresources/...` and `pkg/aicontracts/...`. The registry,
contract, completion and browser guards pass on both commits.
- Live, on a private Pro stack built from this branch and
pulse-enterprise main, with a real model (Claude Sonnet through the
subscription CLI) investigating a Docker container whose web server had
died. In each run the model planned a container restart through
`patrol_propose_action`, and the next model turn was made to fail:
- **Provider error after the plan.** The run closed failed with the
error, `fix_queued` and the pending restart on the session, the finding
and its record. Re-investigation returned 409 `action_pending`, the
restart waited in Actions, and the finding's review showed the queued
fix with its plan. When the plan expired, the sweep published it, and
the session, finding and record moved to `needs_attention` with the
action expired.
- **Deadline after the plan.** With a 300-second limit and the next turn
hung, the run failed at the deadline with `fix_queued`, not `timed_out`,
and the next Patrol pass did not retry it.
- **Restart after the plan.** Pulse was killed mid-run. On boot the
stored session closed as interrupted with the plan kept,
re-investigation still returned 409, and once the grace passed a Patrol
pass recovered and finalized the run. With the second commit the finding
reads `failed`, matching its session and record; before it, it read
`completed`.
- Not observable on a local stack: the approval push, which needs a
relay client, and a run hanging past its limit without closing, since
the provider call is cancelled at the deadline. Unit tests cover both.
2026-10-24 01:39:10 +01:00
pulse-triage[bot] d6e72532e3 Isolate guest fixtures and safely observe pinned loser closure
Keep typed guest-agent cases on the existing namespaced fixture identity so recycled loopback ports cannot inherit a previous synthetic guest cooldown. Add a forced shared-listener regression without clearing the production admission registry.

Set the pinned loser peer deadline before cancellation can close the pipe, exercise the already-closed ordering deterministically, and verify that the winning connection stays usable. No runtime, transport policy, cooldown or admission behaviour changes.

Contract-Neutral: Repository-run test fixture isolation and closure observation only; production guest and pinned-dial contracts are unchanged.
Change-source: pulse-maintainer
2026-10-24 00:54:09 +01:00
rcourtman 8399207f91 Isolate guest-agent test fixtures from recycled ports
Guest fences intentionally outlive a closed fixture listener and are
keyed by endpoint URL, so a test whose client used a fixture's raw
loopback URL inherited an earlier case's cooldown whenever the kernel
handed its server a recycled port. Its first read then came back
agent-cooldown instead of what the test expected. That failed rest-0
twice in a row on one pull request, and one in twelve local runs of
the guest-agent tests.

Single-server and per-case fixtures now use guestAgentFixtureEndpoint,
the package's existing per-case boundary, as the HTTP status and lock
evidence tests already do. Tests whose clients share fences across raw
endpoints by design, the alias and cluster tests and the older client
API tests, start from an empty guest registry instead. The capacity
tests keep raw endpoints: they seed protected work under them and
already reset the registry.

None of twenty fresh-process runs failed afterwards, and the package
passes with and without the race detector.
2026-10-24 00:50:38 +01:00
pulse-triage[bot] aaa6aaa807 Distinguish failed PBS discovery from an empty backup scope
Carry failed and accepted-empty discovery through ordinary serialized backup polling. Preserve unreadable history, reconcile only confirmed absence, and reject malformed datastore envelopes before destructive reconciliation.

Contract-Neutral: Enforces existing PBS scope-evidence semantics; no API, model, policy or permission change.
Change-source: pulse-maintainer
2026-10-23 23:11:44 +01:00
rcourtman a4bfca4d32 Serialize Patrol action projection and finalize hung runs
Three gaps remained in how Pulse projects a Patrol investigation's
action and closes its runs.

Projections of one investigation could interleave: the transition
callback, an investigation read and a run's close each read the action
audit and applied it, so one that read an older state could land after
one that read a newer state, and an approval could land between a
run's reconciliation and its finalization. All of them now read and
apply under the investigation's lock, and a close finalizes before it
releases it.

The projection replaced the whole session through Update, so it could
undo run progress the orchestrator saved in between. It now writes
only the changed action and outcome when the store offers SetAction
(aicontracts.InvestigationActionStore), and the reference it writes
carries the broker's canonical result and verification status and the
model's causal attribution, which the audit does not hold.

A run that hung past its limit without closing was recovered by the
stuck-run sweep but never finalized. The sweep now finalizes it with a
copy of its session marked failed, leaving the orchestrator's session
as it is. A later real close is finalized instead, and a refresh no
longer reopens a record that finalization closed.
2026-10-23 22:26:09 +01:00
rcourtman ae19fcf5d6 Preserve PVE authentication endings while reading responses
A password login can end while its response is being read: the caller
cancels, its deadline passes, or the client's own login timeout expires.
Go's HTTP transport can still hand back the server's answer to that
abandoned request. The construction fixture answers a departed client
with an empty 200, which surfaced as "invalid authentication response"
and failed TestClientConstructionHonoursContext and its client-timeout
twin intermittently in the required backend check.

The shared password exchange now reports the ending instead of
diagnosing that answer, keeps transport errors that already carry the
ending, and starts no form attempt after an ended JSON refusal. This
mirrors the PBS client fix from 18 October. Renewal shares the exchange,
so the same rule applies when its own bounded timeout ends a login.
Complete sessions are still accepted, and construction still discards a
client whose caller has ended.

A new control ends the login during response reading, by caller
cancellation and by a simulated login deadline, for every unusable
answer; without this change all of its cases fail. It also checks that
both encoding attempts carry the one login budget the client timeout
sets. The TLS fixture now aborts a request whose client has left rather
than answering it with an empty 200.
2026-10-23 13:50:03 +01:00
rcourtman b3b5ad6e1f Record Patrol investigation conclusions as a typed tool call
Patrol investigations inferred their outcome from prose markers, so a
bold or bulleted RESOLVED line became the terminal needs_attention and
the finding was never investigated again. Timeouts clustered on
subscription agents. Patrol's 60s stream idle timeout bounds a whole
buffered CLI turn there, three investigations queued on one serial CLI
slot, and the final summary call was capped at 20s.

- Add patrol_conclude_investigation, an investigation-only,
  side-effect-free capture of resolved, cannot_fix or needs_attention.
  It is carried on InvestigationRunResult and
  OrchestratorInvestigationResult.
- After a conclusion, the next turn is the summary. The final turn
  offers only the tools that end a run. A deadline reserve makes the
  current turn final before the run times out, and the summary call
  may use the time left.
- Give investigation turns on subscription agents a 4-minute turn
  budget, a 25-minute default timeout and one investigation at a time.
  These limits are republished on config reload without holding the
  service lock across the Patrol lock.
- Derive Patrol's outer deadline from the effective timeout. Sessions
  now carry their own deadline, and the stuck-run sweep measures a live
  run against it, so neither a lowered timeout nor the previous run's
  time resets a running investigation.
- Record a run orphaned by a restart as interrupted, not timed out. A
  first investigation cut short that way used to stay running for
  good, which also blocked a manual re-run. Recovery applies only while
  the finding still shows that run, takes a result the run's session
  already recorded, and keeps an action outcome such as fix_queued.
- Add unclassified and interrupted outcomes, with lifecycle, telemetry
  bucket, digest and record handling.
2026-10-23 01:26:33 +01:00
rcourtman c3fcddf94c sqlite: bound every store's page cache to SQLite's default
Five stores set private page caches of 16 to 64 MB per connection.
The modernc driver allocates that cache with anonymous mmap outside
the Go heap, so GC and GOMEMLIMIT never see it, and each page costs
about twice its size. Measured: reading a 107 MB database through a
64 MB cache_size added 117 MB of resident memory, against 6 MB at
SQLite's 2 MB default. On a long-running dogfood install about 165 MB
of 360 MB resident sat outside the Go heap, with a 126 MB audit
database behind a 64 MB cache (#1803).

Audit, RBAC, notification queue, unified resources and the alert
event log (writer and both readers) now take pdb.PageCachePragma.
Pages that miss it come from the kernel file cache, which the kernel
can reclaim. The audit suite, including its large legacy migration,
takes the same time as before (309 s on both trees).

pkg/db tests reject any other cache_size in production code and bound
the resident growth of a 30 MB scan. Each store's test pins its
effective value. Negative controls: restoring the audit cache fails
the source scan, and a 64 MB reader cache grows a scan by 55 MB
against a 16 MB budget. modernc sorts _pragma values before applying
them, so a second cache_size in one DSN cannot reliably override.

Related to #1803
2026-10-21 14:16:08 +01:00
rcourtman 52c0c831e8 licensing: send in-app Buy to the licence server purchase page
Every Buy, Upgrade and Renew action opened the Pulse Account portal on cloud.pulserelay.pro, which has returned a Cloudflare 522 since hosted Cloud was hibernated on 2026-06-01, so no one could buy from inside Pulse. The licence server now serves the same handoff-to-checkout page at https://license.pulserelay.pro/buy, using the existing portal handoff and checkout session APIs, so payment still returns to the instance and activates it.
2026-10-21 09:17:45 +01:00
rcourtman 7a622c0b76 Reduce repeated resource decoding in retained metrics reads
Raw fleet reads copied resource IDs across SQLite for every observation and
merged tier branches by those repeated strings. Bind the requested scope once,
return statement-local resource numbers and restore each detached series to
stable timestamp order in the shared reader. Preserve indexed tier coverage,
metric filters, chunk limits and ordered streamed display buckets.
2026-10-19 09:37:00 +01:00
rcourtman 5153c4ccda Cluster retained metrics storage to remove chart row lookups (#3070)
Use clustered range reads and atomic migration for retained metrics, with bounded rollup aggregation and preserved write budgets.
2026-10-19 01:36:33 +01:00
rcourtman e9dbca7c2b Reuse bounded rollup query parameters
Bind the source tier and time window once for both clustered rollup stages.
Reusing the ordinary aggregate's six SQLite slots removes the duplicated
argument array and boxing that exceeded the small-rollup allocation budget.
Preserve indexed ranges, aggregation results and the legacy query path.
2026-10-19 01:08:37 +01:00
rcourtman fc58c597ca Read clustered rollups through bounded primary ranges
Use the covering time index to discover resources in each rollup window,
then aggregate their contiguous primary-key ranges. Group on a query-local
resource number to avoid repeating composite identity comparisons for every
point. Preserve the legacy aggregate until its physical migration completes.

Keep nullable extrema, bucket boundaries, resource identities and idempotence
equivalent to the legacy aggregate, and guard both indexed query stages.
2026-10-19 00:31:47 +01:00
rcourtman 5c37e620b4 Cluster retained metrics storage to remove chart row lookups
Retained chart reads found each sample in the identity index and then fetched its payload from the rowid table. Store observations in the time-major primary table to remove that second lookup while preserving sample identity and retention.

Migrate installed stores through deferred atomic WAL maintenance, preserving old read snapshots, queued writes, nullable bounds, tier reconciliation and rollup metadata. Share the rewrite deadline with auto-vacuum and retain the installed schema when capacity or migration checks fail.

Keep the existing write ceiling, query-cache bounds, chart sampling and 90 ms SLO unchanged. Qualify migration cancellation and crash recovery, cross-Store cached reads, restart and production checkpoint behavior.
2026-10-18 23:49:08 +01:00
rcourtman 487569d8a1 Keep guest HTTP tests on namespaced fixture identities
PR3063 landed endpoint namespaces while PR3068 was being prepared from an
older release base. Keep that existing fixture identity boundary and its
uncertainty-fence regression, and remove the redundant global-registry
resets and helper move added by PR3068.

The Proxmox files return to the first parent of PR3068. The installer
reader-readiness correction remains in place. This keeps one HTTP fixture
isolation mechanism and preserves earlier uncertainty fences throughout
the tests. Production guest admission and release controls are unchanged.

Refs #3063, #3068
2026-10-18 23:28:44 +01:00
rcourtman ed4a2c6b54 Stabilize private bootstrap and guest-agent HTTP fixtures (#3068)
Release preparation #3065 failed on two fixture boundaries: the PTY
could signal Bash before its read setup finished, and a guest-agent HTTP
case could inherit a previous fixture’s cooldown after ephemeral port
reuse. Wait for the actual blocked reader and isolate the registry per
serial HTTP scenario, retaining shared admission across clients and
aliases within each scenario.

Controlled regressions fail before the fixes and pass afterward,
covering premature signal delivery and inherited cooldown state. This
changes test fixtures only and preserves the production installer,
guest-agent safeguards and release qualification controls.

Validation:
- Full config API and Proxmox packages passed on Linux.
- Real BusyBox input and blocked-reader regression passed five
repetitions.
- Guest-agent tests passed with the race detector.
- Status and subsystem registry audits passed.

Refs #3065.
2026-10-18 23:23:53 +01:00
rcourtman d87c98cfab Stabilize private bootstrap and guest-agent HTTP fixtures
The two CI attempts for release preparation #3065 failed in fixture setup:
the PTY signalled a running Bash reader before input setup completed, and
an HTTP fixture inherited an earlier origin's uncertainty cooldown when
an ephemeral port was reused.

Wait for noncanonical, silent terminal input and a blocked Linux reader
before signalling. Isolate the shared guest-agent registry per serial
HTTP scenario while retaining admission sharing across its clients and
aliases. Move the existing isolation helper to the shared fixture file
and update its callers. Production guard and installer code are unchanged.

Both new regressions reproduce the old failures before the fixture fixes.
Retain those failures and the earlier unsuccessful compile proof. Source
test stabilization is separate from release qualification and publication.

Refs #3065
2026-10-18 22:57:44 +01:00
rcourtman 667e98082e Isolate guest HTTP fixtures from retained endpoint cooldowns
Reuse the existing per-case endpoint namespace helper for guest HTTP-status
tests so a recycled loopback address cannot inherit another fixture's
intentional uncertainty fence. Keep diagnostic and cluster clients within
each fixture on the same identity, preserving all runtime cooldown rules.

A shared-listener control reproduces the old bare-endpoint failure and
verifies independent first commands plus unchanged earlier fences. Targeted
and full Proxmox tests pass. Record this separate test-stabilization slice.
2026-10-18 21:45:43 +01:00
rcourtman 03adb23556 Avoid unused extrema and copies in persisted chart reads
Use the canonical timestamp/value model directly from the shared retained
read engine. Preserve the full extrema reader, tier reconciliation, Docker
observation provenance, filters and chart sampling while eliminating unused
column transport and a second series copy.

Verify both projections, detached reads and chart sampling equivalence.
Record the measured memory reduction and retain inconclusive timing and
failed capacity evidence. The idle-worker 90 ms proof remains open.
2026-10-18 21:37:54 +01:00
rcourtman ea3d71a799 Avoid duplicate extrema decoding in retained metric reads
Keep absent bounds nullable across the SQLite row boundary and apply their
existing value fallback during decoding. Preserve explicit bounds and the
same fallback before SQL and streamed display aggregation.

The full metrics suite passes. Ten alternating diagnostic samples reduce
allocations by 24.59% and bytes by 4.24%; their latency difference remains
inconclusive. Preserve the strict candidate failure and competing-capacity
receipt without claiming a qualified 90 ms result.

Contract-Neutral: Retained chart values, extrema, tier reconciliation, sampling and cache boundaries remain unchanged; only decoding allocation cost and capacity-error reporting change.
2026-10-18 20:52:20 +01:00
rcourtman 2a77054035 Fix filtered chart reads and verify the idle-worker SLO
Stream ungrouped retained reads in the existing time-major index order,
avoiding temporary metric sorts without changing tier reconciliation or
chart samples. Extend query-plan and filtered-result coverage.

Add an explicit strict mode for the existing 90 ms API budget and an
optional fresh-worker CI proof with fixed executable identities and raw
capacity evidence. Keep ordinary hosted budgets and required gates intact.

Contract-Neutral: Chart payloads, retained-tier reconciliation, sampling, index layout and source/release gates are unchanged; the implementation removes an unnecessary sort and adds controlled verification of existing latency budgets.
2026-10-18 19:13:30 +01:00
rcourtman b6e56c5baa Preserve PBS authentication cancellation while reading responses
A cancelled login can consume an empty response before the transport reports
cancellation. Preserve that context outcome for the owner and joined readers,
reject late session publication and prevent a cancelled refusal from retrying
another encoding. Keep live response validation and later recovery intact.
2026-10-18 18:11:01 +01:00
rcourtman 6bf9e818e8 Respect construction and shared session login lifetimes
Initial password authentication has one owner and must end with its
constructor. Keep its complete JSON/form exchange within the caller and
client timeout. Retained-session renewal still serves concurrent readers
independently of any one caller's cancellation.

Retain generation-based coalescing and the shared parser, exercise both
initial encodings under the client timeout, and document the ownership
boundary alongside existing startup and cluster cancellation controls.
Retain the failed CI and reproduced baseline in the existing reliability
record while final-source qualification remains open.
2026-10-18 15:57:16 +01:00
rcourtman 2fc54a5a1f Merge pending maintenance repairs with current main
Preserve selected memory freshness and the VM/LXC interpretation.
Retain both native tool contracts and current API concurrency controls.
Qualify this source separately from release and installed recovery.

Contract-Neutral: Integrate already-governed parents without a new contract change. Incoming verification files already match main. Fresh integration tests and source-bound browser proof cover the combined memory and installer surfaces.
2026-10-18 15:11:28 +01:00
rcourtman 3c103f1263 Fix pinned notification connectivity and cancelled DNS lookups
Serial pinned-address dialing could spend the request deadline on a stalled
first address while a validated fallback was healthy. Share one bounded
two-worker dialer with restricted HTTP clients and retain cancellation in
webhook DNS admission and dialing.

Add real stalled-TCP and DNS cancellation controls, persistent queue verdict
readback, shared transport ownership and explicit proof routing.
2026-10-18 08:49:56 +01:00
rcourtman a224a1a195 Measure aggregate reporting outcomes and repeat use
Record successful response writes, scheduled generation, file saves and SMTP
outcomes in a durable content-free tally. Distinct active days include manual
exports, while saved-schedule periods deduplicate retries and monthly reuse.

Publish bounded scalars through the existing opt-out-controlled heartbeat and
report missing coverage explicitly. Preserve pricing and customer entitlements.
2026-10-17 21:10:17 +01:00
rcourtman 22306e0a93 Add scheduled client backup health reports
Retain backup execution outcomes and collection coverage in each
organization's recovery store so provider inventory changes and restarts
cannot erase report evidence. Keep missing and incomplete history distinct
from an observed period with no attempts.

Reuse existing report scopes, calendar schedules, branding and delivery
recovery for client PDF and CSV reports. Register the paid renderer through
the existing private runtime boundary. Pricing and grants are unchanged.
2026-10-17 17:36:24 +01:00
rcourtman 3f511a5270 Scope report metrics to the requesting organization
The report engine read every report's metrics from the default
organization's store, whatever organization the request came from. A
tenant request for a resource ID outside its own inventory fell through
to that store with the caller's ID, so one organization could read the
default organization's history, and even a tenant's own resources were
reported from the wrong store. Reporting was closed to org owners until
this was fixed.

The engine now resolves the metrics store of each request's OrgID
through the multi-tenant monitor and refuses a request that names none,
so no report path can fall back to the default store. Report handlers
reject resources outside the requesting organization's inventory,
scheduled runs resolve their scope against their owning organization's
inventory and skip resources it no longer holds, and Pulse Assistant's
pulse_summarize reads its executor's organization.

With every report read scoped, the reporting routes move to
ensureOrgAdminSession and reportingRead is offered to org managers
again, so providers can build reports inside client workspaces.

The report engine wiring lives in pkg/server/server.go, which the
completion guard only let telemetry tests prove. server.go now has its
own security-privacy path policy that keeps those proofs and also
accepts pkg/server/reporting_engine_test.go; the other telemetry files
keep their existing policy unchanged.
2026-10-17 13:59:44 +01:00
rcourtman af3b578c06 Keep shared Proxmox login alive when its starter cancels
Session renewal became single-flight in 97e4c8eac7, but the caller that
started the login ran it on its own context. When that caller was a
short-lived read (a 10s guest-agent call, a guest-metadata fetch cancelled
on invalidation, an API request whose browser disconnected), cancelling it
aborted the shared login and every concurrent waiter failed with
"re-authentication failed: context canceled" despite live contexts. The
cluster client treats that text as an authentication error and skips
failover, and a failed GetNodes marks the instance's nodes unreachable for
the cycle.

Run the shared login detached from any single caller's cancellation,
bounded by the client's per-request timeout, and have every caller,
starter included, stop waiting only when its own context ends. A genuine
login failure is still shared with all waiters, and a login whose starter
has left still completes and is reused instead of being repeated.

Contract-Neutral: Proxmox password-session renewal keeps its API fields, TLS policy and guest-command rules; only which context bounds the shared login changes.
2026-10-16 23:55:16 +01:00
rcourtman 84226ad7bc Count successful history reads beyond seven days
Measure delivered extended history before changing free and paid features.
Count only successful responses containing an older metric point and retain
only bounded daily totals. Expose the rolling sum through schema v19 and
Preview with matching privacy disclosure and explicit report coverage.
2026-10-16 19:21:03 +01:00
rcourtman 36c63bbf8b Accept PMG 9 cluster ids and stop guessing backup nodes
Proxmox Mail Gateway 9 returns the cluster status cid as a string, so
decoding it into an int failed the whole cluster read and the poller
lost the node list (issue 2960). Pulse never reads cid, so the field is
no longer decoded and any format works.

With no nodes, the poller then requested backups from a node named
after the connection's display name ("PMG-01"), which PMG rejected with
a hostname-lookup error that pointed users at DNS. That fallback now
runs only when PMG answered with no cluster entries; after a failed
cluster read the request could never return backups, and the poll
already records the cluster failure.
2026-10-16 14:15:58 +01:00
pulse-triage[bot] 81d16def55 Keep unusable guest measurements distinct from foreign replies
Stream every nested result field without imposing a new uniqueness policy on completed filesystem measurements. Existing counter validation retains unusable/partial results without a cooldown; a later null or case alias cannot erase an earlier foreign interface field. Correct the legacy HTTP-200 error-payload assertion to the documented unverified-completion behavior.

Contract-Neutral: Restore the documented completed-measurement semantics and strengthen tests of existing foreign-reply refusal; no additional contract delta.
Change-source: pulse-maintainer
2026-10-14 18:57:17 +01:00
pulse-triage[bot] 9b887e78e4 Keep foreign QGA reply payloads behind shared admission
A structurally valid reply for another guest command must not become an empty current inventory, acquire filesystem identity or release the serial-command fence. Check known command-exclusive fields and information-result shape before postflight lock checks; preserve null/empty/shared/unknown fields, partial row decoding, unusable file measurements, trust and independent live polling. Add six-reader cross-command controls, alias/expiry/recovery controls and actual builder/state/broadcast/History proof. This detects contradictory payload shapes, not native command correlation, thaw or report attribution.

Change-source: pulse-maintainer
2026-10-14 18:25:10 +01:00
pulse-triage[bot] eecdbd57df Keep Proxmox denial diagnostics within intended credential scope
Distinguish observed 403 permission denials from 401 authentication and other HTTP failures, rather than prescribing token rotation or user-only grants. Carry fixed, identity-free recovery guidance into the existing Diagnostics error summary while preserving live-monitor/PBS probe attribution, TLS trust and ordinary request behavior. Add wire-status, ACL-scope, same-credential recovery and serializer controls with their subsystem contracts.

Change-source: pulse-maintainer
2026-10-14 16:23:23 +01:00
pulse-triage[bot] 9705e5df9b Use URL-safe fixture identities for repeated lock-evidence test names
Current qualification exposes Go's #NN repeated-name suffix as an invalid URL fragment in the new fixture path. Hash the complete evidence label so clients retain the same per-case key without using test labels as URL syntax. The actual hostname validation and guest fences remain unchanged.

Contract-Neutral: Fixture-only identity encoding repair; no runtime or trust-contract change.
Change-source: pulse-maintainer
2026-10-14 14:56:33 +01:00
pulse-triage[bot] 757acf055e Isolate lock-evidence fixture keys without clearing guest fences
Retained full-suite proof reports an inherited agent-cooldown before the invalid-UTF8 lock case dispatches any command. Give each synthetic case a distinct base path, keeping all clients in that case on its shared key. A same-listener control retains the prior uncertainty fence unchanged. No runtime guest guard, native target or assertion is weakened.

Change-source: pulse-maintainer
2026-10-14 14:47:17 +01:00
pulse-triage[bot] 5b895ff78e Bound routing ownership proof without a one-second latency assumption
The retained 277ec211 source proof times out on a correctly retained target, with injected requests zero and its pin unchanged. This test owns routing and trust, not a latency SLO. Use explicit five-second client and ten-second caller fixture bounds, record elapsed work, and retain every routing/pin/guest assertion. Runtime timeouts and timeout controls are unchanged; the original failed receipt remains adverse evidence, not a diagnosed host fault.

Change-source: pulse-maintainer
2026-10-14 14:37:46 +01:00
pulse-triage[bot] 277ec2110a Preserve contradictory saved cluster trust through producer and refresh
Carry exact duplicate-row conflicts into owned client trust before map deduplication. Keep unresolved saved trust through initialization, missing-client retry and topology refresh without replacing pins or stopping trusted siblings. Cover actual monitor lifecycle routes and member projections alongside client TLS controls.

Change-source: pulse-maintainer
2026-10-14 14:27:25 +01:00
pulse-triage[bot] bea93859d3 Refuse conflicting saved TLS pins for equivalent Proxmox URLs
Resolve all saved pins for an endpoint before credential-bearing construction. Contradictory trust is an explicit failure, not a missing-pin fallback or first-use opportunity. Preserve independent authorities, agreeing formats and saved configuration; prove zero provider work, sibling monitoring and normal reload through the member projection.

Change-source: pulse-maintainer
2026-10-14 13:36:51 +01:00
pulse-triage[bot] 9dcd5697cc Keep incomplete Proxmox guest inventories out of publication
Require whole bounded, unambiguous enumerations on the efficient and node fallback routes. Preserve scoped empty inventories, LXC integer compatibility and ordinary counter/receipt semantics. Validate retained readings, History, breaches and template policy through actual fallback and recovery.

Change-source: pulse-maintainer
2026-10-14 11:35:55 +01:00
pulse-triage[bot] ba9bb34d09 Require complete Proxmox node inventory before recovery
Consume the bounded response through EOF before admitting nodes or a cluster probe. Reject missing, ambiguous and incomplete inventories without leaking response details, retain scoped empty arrays and provider metadata, and cover trusted startup, recovery, cached reads and consuming endpoint projection.

Change-source: pulse-maintainer
Contract-Neutral: Tightens existing node-inventory and recovery-health admission without changing the public response schema, API surface, permissions, TLS or guest-command policy.
2026-10-14 10:10:50 +01:00
pulse-triage[bot] db8587f7d1 Align the cached fallback fixture with observed recovery
Replace the old optimistic-health expectation with cached selection, failed-request and later-success controls. Advance no production clock or safety fence and avoid an irrelevant constructor attempt in the in-memory fixture.

Contract-Neutral: Test-only correction to the recovery contract established in 4147f48db3; runtime and browser inputs are unchanged.
Change-source: pulse-maintainer
2026-10-14 07:39:58 +01:00
pulse-triage[bot] 4147f48db3 Keep failed Proxmox nodes probes out of recovered health
Require the actual nodes/login probe to succeed at startup, recovery and lazy selection instead of accepting guest-like response text. Retain failed single-endpoint health until an ordinary request succeeds, preserving cached last-resort reads, real guest error handling, temporary rate-limit handling and trust policy. Add producing-path and connected projection regressions and revise the old nodes-as-guest fixture contract.

Change-source: pulse-maintainer
2026-10-14 07:24:36 +01:00
pulse-triage[bot] 2d14da7380 Bind recovery fixtures to real fields and independent guest origins
The projection fixture now uses the actual saved ClusterEndpoint schema. Give each malformed-filesystem fixture an independent base path so reused listener ports cannot borrow a previous live uncertainty fence; all real guard state and response assertions remain.

Contract-Neutral: Test-only schema correction and independent synthetic endpoint identity; no production policy, deadline, wire or configuration changes.
Change-source: pulse-maintainer
2026-10-14 05:43:31 +01:00
pulse-triage[bot] bf67cf6532 Keep probe counts faithful to their actual observation
A completed first-use retry does not return its nodes payload. Omit that unknown diagnostic count rather than reporting zero. Concurrent ordinary reads may legitimately open additional TLS sockets, so the construction control bounds logins and probe reads rather than pooled read connections.

Contract-Neutral: Diagnostic-count fidelity and fixture-only connection counting; no wire, trust, polling, authority or consumer contract delta.
Change-source: pulse-maintainer
2026-10-14 05:40:56 +01:00