A scheduled retry across UTC days can generate another artifact without
completing a new successful reporting period. Keep multi-day generation as
an activity observation rather than inferred repeat use, while retaining
successful distinct-period counts as their own signal.
Exercise both cross-day generation without success and multiple successful
periods on one day so the adoption summary cannot conflate these metrics.
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.
The route inventory treated RequireOrgAdmin routes as unauthenticated
"bare" routes, and the agent diagnostics contract and the API
reference anchors still named RequireAdmin for routes that now opt
into organization authority. The security-privacy contract still said
org managers receive reportingRead through ensureAdminSession; it now
names the org-bound routes' check and records that instance
administration refuses organization owners. The agent-lifecycle
contract notes that agent routes declare that authority explicitly.
Commit a5baa7563b stopped Patrol reading cache-inclusive Proxmox memory
as guest pressure (issue #2762, a QEMU VM). It also put
`cluster-resources` in that cache-inclusive group for every guest type.
An LXC without a linked Pulse agent only ever gets `cluster-resources`,
so Patrol dropped memory pressure for all agentless containers:
- a container at 97% of its limit got no triage flag, forecast or
anomaly
- the seed said "guest pressure unknown; may include reclaimable cache"
- older memory alerts went to a billed model review on every run, and
any resolve verdict was thrown away
**That premise is wrong for LXC.** On both PVE 9 (`master`) and PVE 8
(`stable-bookworm` / `stable-8`), Proxmox subtracts page cache before
reporting a container's memory:
- `pve-common` `src/PVE/CGroup.pm` `get_memory_stat`: `$res->{mem} =
$mem - $stat->{file};` on cgroup v2 (`memory.current`), and `$res->{mem}
= $mem - $stat->{total_cache};` on v1 (`memory.usage_in_bytes`).
- `pve-container` `src/PVE/LXC.pm` `vmstatus` stores that value with
`$d->{mem} = int($mem->{mem});`. `PVE::LXC::CGroup` inherits
`get_memory_stat` from `PVE::CGroup` without overriding it.
- `pve-manager` `pvestatd` `update_lxc_status` writes `$d->{mem}` into
RRD field 8. `/cluster/resources` (`PVE/API2/Cluster.pm` →
`PVE::API2Tools::extract_vm_stats`) returns `$entry->{mem} = ($d->[8] ||
0) + 0;`.
A QEMU guest's listing `mem` is the host's view of guest RAM, which does
include the guest's cache, so it stays pressure-unknown.
**Change**
- `unifiedresources.QualifyGuestMemoryForKind` now takes the guest kind:
other platform, Proxmox VM or Proxmox container.
- A current, dated `cluster-resources` reading counts as known pressure
for a Proxmox container and stays cache-inclusive and unknown for QEMU.
- `status-mem`, `status-freemem` and `derived-total-minus-used` are
unchanged for every guest type.
- `GuestMemoryEvidenceForResource` takes the kind from the resource
type, so the Patrol inventory, the observer runtime and AI query
projections agree.
- `QualifyGuestMemory(bool)` keeps the conservative QEMU reading for
callers that don't know the guest type.
- The wrong comments in `calculateLXCMemory` and the LXC monitor tests
are corrected.
- Everything else in a5baa7563b still applies: retained, undated,
unknown and unavailable readings still cannot establish pressure or
recovery.
**Tests**
- `TestPatrolAgentlessLXCListingMemoryIsPressure` (new) builds an LXC
and a QEMU guest at 97%, both on `cluster-resources`, through legacy and
canonical read state. The LXC gets a memory triage flag and alert review
reads its value; the QEMU guest gets neither.
- `TestGuestMemoryClusterResourcesIsTypeAware` (new) covers both guest
kinds, retained readings, the other cache-inclusive sources and the
resource-type mapping.
- The `cluster-cache` fixture in `TestPatrolGuestMemoryEvidence` now
expects pressure for the container variant. It previously encoded the
wrong behaviour.
- Against origin/main's production files (`go test -overlay`), the new
and updated Patrol tests fail; with this change they pass.
Refs #2762
Supersedes #3011, which failed the documentation recipe test that pinned
the old `QualifyGuestMemory(` call in Patrol; this commit also updates
that pin to `QualifyGuestMemoryForKind(`.
Patrol treated every guest's cluster-resources memory as cache-inclusive,
so an LXC without a linked Pulse agent, which only ever gets that source,
lost memory pressure entirely: no triage flag, forecast or anomaly at 97%
of its limit, a "may include reclaimable cache" caveat in the seed, and
older memory alerts sent to model review every run with any resolve
verdict discarded.
The premise holds for QEMU but not for LXC. Proxmox's container mem is
cgroup usage with page cache already subtracted: PVE::CGroup's
get_memory_stat returns memory.current minus memory.stat file (cgroup v2)
or usage_in_bytes minus total_cache (v1), PVE::LXC::vmstatus reports it
as mem, and pvestatd feeds that value to the RRD that /cluster/resources
reads. A QEMU guest's listing mem is the host's view of guest RAM and
does count the guest's cache.
Qualification now takes the guest kind. A current, dated cluster-resources
reading establishes pressure for a Proxmox container and stays unknown for
QEMU; status-mem, status-freemem and derived-total-minus-used stay
cache-inclusive for both, and retained readings still cannot prove
pressure. The typed read state derives the kind from the resource type,
so Patrol, the observer and AI queries agree. The calculateLXCMemory
comment that called the listing value cache-inclusive is corrected.
Contract-Neutral: monitoring change is a corrected comment in calculateLXCMemory and its tests; LXC memory collection and sources are unchanged
The server installer downloaded the release archive and extracted it
under /tmp. Debian 13 and many LXC templates mount /tmp as tmpfs, whose
pages are charged to the container's memory cgroup. A v6.5.0 archive is
~280 MB and extracts to ~750 MB, so an unattended update on a 1 GiB LXC
filled the cgroup with ~820 MB of shmem; the kernel OOM-killed the
container's systemd and Proxmox stopped the container mid-update.
Download and extract into owner-only hidden directories under the
install directory instead. It is always writable from the hardened
pulse-update.service sandbox, it is where the staged binary already
lives, and the disk headroom preflight now measures the filesystem the
update actually stages on. The Go updater already prefers its data
directory over /tmp for the same reason.
Unlike /tmp, the install filesystem keeps an interrupted run's staging
across reboots. Staging setup now removes stale staging directories that
match only the exact owner-only names, never symlinks or files, and only
once their ctime is over six hours old: install.sh has no update lock,
so age is what proves no live run still owns them. Every recursive
pulse chown of the install directory now goes through one helper that
skips staging, so the payload root still reads is never handed to the
service account and the ~750 MB tree is not rewritten for nothing.
The "Failed to print table: Broken pipe" line in the report was
systemctl list-unit-files | grep -q in v6.5.0's service discovery; main
already reads that inventory in full, and it was not the failure.
Contract-Neutral: Defect fix inside the existing private server-installer inputs and update-space boundaries; staging moves from tmpfs-prone /tmp to the install filesystem with no public contract change
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.
The Workloads table put a second line under memory and disk cells on
every stopped guest ("N/A / Unavailable", "- / Unavailable"), on every
agentless VM's disk, and "May include cache" under every agentless VM's
memory bar, even at 20%. None of them said why or what to do, and they
broke the row rhythm.
Memory that cannot be read now renders the same dash as an unreadable
disk, with its reason in the cell title, the sparkline's hover title
and assistive text. Disk shows a visible label only for a retained
reading (Last known) or a read the user can fix (No permission, Agent
error). The cache caption appears only when the guest's own memory is
at its warning or critical threshold, the figure that colours the bar
in host-share mode too, which is the case #2762 and #2639 were about;
below that the caveat stays in the title and assistive text.
An unavailable memory explanation names what is known instead of
"Unknown source": a powered-off guest reads "Unavailable while the
guest is powered off." MetricMiniSparkline gains an optional tooltip
so the chart covering the cell can carry that reason without changing
its accessible name.
The browser gate now uses the shared repository I/O module. Exercise its
real dependency in the prepush fixture so smoke tests reach qualification
checks instead of failing during import.
Contract-Neutral: Test fixture dependency only, no application or contract behavior changes.
Compose upstream bed5052eb5 without replacing reviewed history. Keep the accepted one complete stdout/stderr observation and blank-line refusal instead of upstream second-sample replacement. Retain upstream real-systemctl fresh-host assertions and its platform-availability decorator alongside every reviewed failure-retention control. No release, installed recovery or LXC stop-cause claim.
Change-source: pulse-maintainer
The previous commit inserted a new real-systemctl test above the old one
and took over its skipUnless decorator, so on macOS, which has no
systemctl, the new test skipped and the old one ran unprotected and
failed. Restore the decorator on the old test.
The installer proof required the real-systemctl control to have passed.
Where systemctl does not exist it must instead report skipped, so the
proof now checks for ok on a host with systemctl and for skipped on one
without, and still fails if the control is absent from the output.
Refs: #2785
The 2026-10-10 server service discovery change lists the two historical
unit names with one pattern read and refuses to continue when that read
exits non-zero. systemctl list-unit-files exits 1, printing nothing on
stdout or stderr, when none of the named unit files exist. That is every
fresh host, so install.sh refused with "Cannot identify the Pulse systemd
service: unit inventory failed" before it did any work. The first beta's
install.sh smoke test caught it on a clean Debian 12 container; no
release carries it, and v6.5.0 listed the whole inventory and grepped it.
The tests could not see it. Their systemctl double reports "no units" as
exit 0, and test_partial_failed_inventory_is_not_absence asserted that an
empty inventory with exit 1 must be refused, which is the defect itself.
Add one inventory read shared by the installer and the unattended
updater, and use it for the auto-update mask check, which had the same
hazard. It accepts exactly the silent exit 1 as an empty inventory, and
only after a second read confirms there is still no output and no
message. A timeout, a signal, any other status, or an exit 1 that carries
output or a message stays a failed inventory, so a real failure still
cannot pass as absence.
Make the doubles model systemd: a failure now prints its message, silent
exit 1 is absence, and a new test runs real systemctl --root over an
empty unit root for both scripts. Run against the unfixed scripts, the
four new fresh-host tests fail; the rest of the suite is unchanged.
The registered runtime proofs for install.sh and the unattended updater,
which drive this suite, now require the fresh-host controls to have run
and passed, so they cannot be skipped or deleted unnoticed.
Refs: #2785
Read optional intake precautions at their expandable report fields, bind OpenShift Secret tests to the validated private file, and target helper workflow negative controls by step. Preserve the prepared GNU timeout in the private teardown probe on macOS and keep the actual security migration boundaries mandatory. Production guidance, installer behaviour and platform coverage are unchanged.
Contract-Neutral: Fixture-only integration repair; no product, installer, workflow, deployment or safety contract delta.
Change-source: pulse-maintainer
Combined-source validation found the removal-help assertion still looking for a direct systemctl stop after accepted teardown moved that operation into its fail-closed shared helper. Verify the actual helper call before revocation and retain stop, disable and complete-set readback assertions, with missing, late and ignored-call adverse controls.
Contract-Neutral: Test-fixture reconciliation only; no installer, help, lifecycle, schema or authority change.
Change-source: pulse-maintainer
SSO operators can hold permitted non-administrative write capabilities. The guide now checks the actual instance-administration boundary, without instructing a settings mutation or changing roles.
Change-source: pulse-maintainer
Preserve the exact reviewed candidate and its portable proof. Hosted native systemd receipts, matching Go compilation, signed installed-agent transport and Windows recovery remain separate acceptance obligations.
Change-source: pulse-maintainer
Copy generated callbacks only after provider save, align group-claim advice with provider-specific scopes, and retain administrator access until separate-session permission checks pass. Keep shipped documentation and regression checks in step without changing authentication or provider behaviour.
Change-source: pulse-maintainer
Preserve the exact reviewed guide and regression-test commits. Retained command controls and browser evidence establish safer credential paths and permission diagnostics without cluster or privilege changes.
Change-source: pulse-maintainer
Preserve the five reviewed Core commits exactly. Blank-only bootstrap admission and the matching persistence path retain existing access; authenticated transfer remains unchanged.
Change-source: pulse-maintainer
Run the accepted Unix teardown functions on fresh public Linux x64 and ARM64 CI, with real stop refusal, socket activation, batch reactivation and independent process/file readback. Retain bounded source-bound phase and owned-cleanup receipts; keep synthetic revocation separate from installed TLS, signed-agent and Windows acceptance.
Change-source: pulse-maintainer
The copied token recipe followed linked paths and continued without its own failure boundary. Reject unsafe paths before editing or contacting the cluster, preserve scoped file-only provisioning, and distinguish Kubernetes RBAC from local pod privilege and missing usage. Exercise the exact copied recipes with synthetic inputs and render the served guidance.
Contract-Neutral: Existing Kubernetes help and its regression tests only; no runtime, API, chart, role, deployment profile or feature change.
Change-source: pulse-maintainer
Require a blank destination instead of using nodes.enc as a first-run sentinel. Recheck before writes, retain unknown or unreadable state, and leave deliberate authenticated transfer unchanged. Keep deployment .env provisioning and encrypted file precedence; explain the separate archive path in canonical and served help.
Change-source: pulse-maintainer
The v4-to-v5 entry point still offered privileged download-and-purge cleanup and duplicate update shortcuts despite safer current procedures. Preserve configuration, credential and workload recovery evidence, separate server and agent paths, and link the inspected local cleanup without broadening it.
Contract-Neutral: Existing customer guidance, shipped Markdown and focused regression checks only; no runtime, API, permission, installer or cleanup contract changes.
Change-source: pulse-maintainer
Retain exact PI candidate identity and its proof; existing configuration-transfer help now states the real admission gate and replacement limits.
Change-source: pulse-maintainer
Document the existing nodes.enc gate, replacement scope, file precedence and non-fatal import errors. Preserve agent identity and excluded state, isolate provisioning from live traffic, and keep one-shot inputs private rather than forcing a reset.
Contract-Neutral: Existing bootstrap and encrypted-import behaviour is unchanged; this corrects canonical and served help, with documentation regression controls.
Change-source: pulse-maintainer
Retain the exact Delivery candidate b3b368c21b and its attributed PR2855 adaptation. Independent review verifies one-read failure retention and unchanged runtime-proof bytes; no native installation or published availability is claimed.
Change-source: pulse-maintainer
Adapt rcourtman’s Pulse PR2855 (aef53d0d3e), which diagnosed systemd’s silent exit-one absence. The original contributor commit is unchanged and explicitly credited, not imported, amended or re-parented in this maintainer candidate.
One bounded stdout/stderr observation accepts genuine absence but retains failed first reads, other statuses and blank-line output. Include the existing registered installer/updater verification hooks, real-empty-root controls and missing-unit recovery double in this same source commit. This replaces the rejected foreign-history composition with a normal maintainer-reviewed implementation; no provenance policy, credential or host installation changes.
Source-attribution: rcourtman, https://github.com/rcourtman/Pulse/pull/2855, aef53d0d3e
Change-source: pulse-maintainer
Align existing removal and orphan-service help with credential-first Unix admission. Explain same-instance recovery, keep older served scripts and Windows behaviour distinct, and prevent stopping retries from being mistaken for revocation or completed removal.
Contract-Neutral: Existing operator guidance and regression assertions only; no runtime, schema, lifecycle or trust-boundary change.
Change-source: pulse-maintainer
Accept original failure evidence without another reproduction or a diagnostic export, and place public-sharing precautions before readers leave for GitHub. Keep the shipped documentation index in sync and guard both entrypoints within the README page budget.
Contract-Neutral: Documentation-only intake guidance and regression checks; no runtime, API, collection, authentication or release contract changes.
Change-source: pulse-maintainer
Accept exact product-intelligence candidate 5db15c9366. Retained source-stb_euh2 verifies all 46 selected documentation checks, public links and shipped mirrors; token generation and hint semantics agree with source. Runtime and credential policy are unchanged.
Change-source: pulse-maintainer
The Pi authentication report identified duplicated token characters despite a matching partial hint. Check correct, duplicated-middle and legacy input through the real generated terminal prompt and installer writer, including restoration of the original synthetic value and private hand-off cleanup. Clarify the existing guide without imposing a token-shape rule or claiming installed reporting recovery.
Contract-Neutral: Documentation and regression coverage only; command generation, credential selection, authentication, scopes, trust and runtime behaviour are unchanged.
Change-source: pulse-maintainer
The current host installer issues a 64-character lowercase hexadecimal token, but a bare file and matching inventory hint can still hide duplicated or changed middle characters. Explain the private whole-value check before first installation and its limits during authentication triage. Preserve other credential families, installation state, scopes and trust.
Contract-Neutral: Existing setup and authentication guidance only, with source-bound documentation checks. Runtime behaviour, credential formats, scopes and trust are unchanged.
Change-source: pulse-maintainer
Apply the existing ten-second deadline to both qm and its response reader. Stop only the local collection group even when a producer exits with an inherited pipe or a reader ignores TERM. Preserve byte limits, independent failures, private output and the refusal to probe or change a guest; test actual owned-reader termination and the mirrored help.
Contract-Neutral: Passive operator diagnostics and existing help only; Proxmox polling, guest-agent admission, APIs, storage, permissions and service behaviour are unchanged.
Change-source: pulse-maintainer
Compose the exact observed upstream additively without rewriting reviewed tip 805ea6d712 or any accepted ancestry. The new Go 1.26.9 and x/net graph need matching final-source evidence before this batch can be proposed.
Change-source: pulse-maintainer
The canonical security guide still encouraged Debug logging, broad service/log inspection and a fresh unmounted Docker container. Route verification through existing bounded private evidence, distinguish file rotation from journal, container and audit retention, and preserve the active deployment when changing logging. Keep the shipped guide synchronized and check these boundaries against the current server source.
Contract-Neutral: Correct existing security guidance and its regression checks without changing runtime, permissions, credential handling, API behaviour or subsystem contracts.
Change-source: pulse-maintainer
govulncheck on the pinned 1.26.8 toolchain reports twelve reachable
standard library vulnerabilities in Pulse (net/http, crypto/tls,
html/template, os, net/textproto), published on 2026-10-08 and fixed in
Go 1.26.9. golang.org/x/net v0.58.0 carries further advisories fixed in
v0.60.0. With 1.26.9 and x/net v0.60.0 the scan reports no affecting
vulnerabilities under both govulncheck v1.1.4 and v1.7.0.
Pulse's own govulncheck job last ran on 2026-10-05, so nothing flagged
this. The private Enterprise build and test job notices it: once the scan
finds reachable advisories it needs more than the 4 GiB CI VM holds, the
guest kernel kills govulncheck, and the job is cancelled. That has failed
every Enterprise pair check since 2026-10-08, including the first
beta's.
Move every release builder and local toolchain guard together, as 1.26.8
did, so no candidate artifact keeps an older compiler: go.mod, the
builder image digests (index digest for 1.26.9-alpine), the release and
dev scripts, the devcontainer, and the root pair diagnostic. Only x/net
is raised in the module graph; go mod tidy is deliberately not run, so
unrelated indirect requirements stay as they were.
Contract-Neutral: Toolchain and indirect dependency patch update that clears reported advisories; no product or runtime contract changed.
Preserve every accepted maintenance commit while incorporating human-authored diagnostics token visibility. Resolve API guide overlap by retaining the shared ai:execute narration gate, explicit deterministic fallback and CSV refusal in both mirrors.
Change-source: pulse-maintainer
Follows the token authority fixes. The API reference now says:
- agent uninstall is a collector's own-token removal, or an admin session;
- AI narration of reports needs ai:execute from API-token callers, for
single and fleet reports, and otherwise falls back to the deterministic
summary;
- API-token callers manage only the tokens in their own organizations,
and only an instance-wide token can create, edit, rotate or delete
another instance-wide token;
- quick setup that would change credentials needs an instance-wide
token from API-token callers.
Adds the missing entry for POST /api/security/tokens/<id>/rotate, which
the regenerate entry already points scoped tokens to.
test_api_reference_routes.py pins each statement to the code that
enforces it, so the reference and the gates cannot drift apart. Each pin
fails when the code or the reference is reverted.
Contract-Neutral: Customer docs corrected to match shipped behaviour; no runtime, API or contract change.
Preserve the exact PI candidate and its retained renderer and desktop/phone help proof. Existing help only; no alert or Patrol runtime change and no installed relief claim.
Change-source: pulse-maintainer
Preserve exact Delivery commits 5ef1729292 and cd1d162e30. Retained installer/vet and final smoke proofs cover unchanged fixture scopes; production verification, deadlines and recovery matrix are unchanged.
Change-source: pulse-maintainer
The FAQ promised every metric could be disabled globally or per resource with -1. Clarify supported editor controls, overriding thresholds, unchanged collection, queued notifications and the existing Patrol fallback. Keep ordinary notification help reachable without generating new alerts.
Contract-Neutral: Existing help and its source/rendering/browser checks only; no alert, notification, Patrol, settings or API behaviour changes.
Change-source: pulse-maintainer
Accept existing-help maintenance with retained exact-source documentation controls, frontend type-check/docs tests/build and desktop/phone renderer proof. This does not establish installed Pi authentication or reporting recovery.
Change-source: pulse-maintainer
The RETURN-trap smoke fixture has a simulated manager and script binary, not a real managed Linux process. Provide its exact-service/new-version comparator and require exactly one success-path invocation, with none on prerelease refusal. Keep actual process-identity refusals in the independent source/native controls; do not relax production verification.
Change-source: pulse-maintainer
Contract-Neutral: Legacy shell smoke fixture correction only; production updater and its running-executable, signing, restoration and deadline contracts are unchanged.
Use a counted-NUL shell logger double instead of starting Python for every installer line. Keep production log(), complete pipe draining, all eight first/later/final-error recovery cases and the existing 45-second bound. Require exact argument-frame and failure-sequence controls in the Go acceptance wrapper.
Change-source: pulse-maintainer
Contract-Neutral: Confined installer test fixture optimisation only; production updater, signing, native oracle, recovery matrix and safety deadlines are unchanged.
Keep bare, quoted, prefixed and unknown file forms distinct from credential selection and successful reporting. Link the existing private-file installation to non-destructive guidance, and verify its source alignment, rendered table and keyboard navigation.
Contract-Neutral: Help and regression controls only; no token parsing, authentication, permission, installer or runtime contract change.
Change-source: pulse-maintainer
The current native acceptance oracle can return success without a manager observation after comparing running executable bytes. Bracket the comparison with stable active process identity and compare both executables independently to the authenticated fixture, so a changing PID or equally wrong destinations cannot pass. Add required portable drift/refusal controls without changing installer authority or hosted admission.
Change-source: pulse-maintainer