Prepare v6.3.0-rc.4 release

This commit is contained in:
rcourtman
2026-08-21 00:00:37 +01:00
parent 4c7b1a2434
commit 7da385cf28
26 changed files with 227 additions and 91 deletions
+1 -1
View File
@@ -1 +1 @@
6.3.0-rc.3
6.3.0-rc.4
+4 -4
View File
@@ -2,9 +2,9 @@ apiVersion: v2
name: pulse
description: Helm chart for deploying the Pulse hub and optional Docker, Kubernetes, or OpenShift monitoring agent.
type: application
version: 6.3.0-rc.3
appVersion: "6.3.0-rc.3"
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.3.0-rc.3/docs/images/pulse-logo.svg
version: 6.3.0-rc.4
appVersion: "6.3.0-rc.4"
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.3.0-rc.4/docs/images/pulse-logo.svg
keywords:
- monitoring
- proxmox
@@ -32,7 +32,7 @@ annotations:
description: Smoke tests with kind cluster deployment
artifacthub.io/links: |
- name: Documentation
url: https://github.com/rcourtman/Pulse/blob/v6.3.0-rc.3/docs/KUBERNETES.md
url: https://github.com/rcourtman/Pulse/blob/v6.3.0-rc.4/docs/KUBERNETES.md
- name: Support
url: https://github.com/rcourtman/Pulse/discussions
artifacthub.io/maintainers: |
+1 -1
View File
@@ -1,6 +1,6 @@
# pulse
![Version: 6.3.0-rc.3](https://img.shields.io/badge/Version-6.3.0--rc.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.3.0-rc.3](https://img.shields.io/badge/AppVersion-6.3.0--rc.3-informational?style=flat-square)
![Version: 6.3.0-rc.4](https://img.shields.io/badge/Version-6.3.0--rc.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.3.0-rc.4](https://img.shields.io/badge/AppVersion-6.3.0--rc.4-informational?style=flat-square)
Helm chart for deploying the Pulse hub and optional Docker, Kubernetes, or OpenShift monitoring agent.
+1 -1
View File
@@ -2,7 +2,7 @@ version: '3.8'
services:
pulse:
image: ${PULSE_IMAGE:-rcourtman/pulse:6.3.0-rc.3}
image: ${PULSE_IMAGE:-rcourtman/pulse:6.3.0-rc.4}
container_name: pulse
restart: unless-stopped
logging:
+4 -4
View File
@@ -6,10 +6,10 @@ Pulse release notes live on GitHub:
For historical v4 notes that previously lived in this repo, see:
`docs/releases/RELEASE_NOTES_v4.md`
For the current v6 release candidate packet (the current v6 support release
candidate packet), see:
- `docs/releases/RELEASE_NOTES_v6.3.0-rc.3.md`
- `docs/releases/V6_CHANGELOG_v6.3.0-rc.3.md`
For the current v6 release candidate packet
(the current v6 support release candidate packet), see:
- `docs/releases/RELEASE_NOTES_v6.3.0-rc.4.md`
- `docs/releases/V6_CHANGELOG_v6.3.0-rc.4.md`
For the current stable v6 packet, see:
- `docs/releases/RELEASE_NOTES_v6.2.1.md`
+2 -2
View File
@@ -4,8 +4,8 @@ This guide covers practical upgrade steps for existing Pulse installs moving to
For the current v6 release candidate packet, see:
- `docs/releases/RELEASE_NOTES_v6.3.0-rc.3.md`
- `docs/releases/V6_CHANGELOG_v6.3.0-rc.3.md`
- `docs/releases/RELEASE_NOTES_v6.3.0-rc.4.md`
- `docs/releases/V6_CHANGELOG_v6.3.0-rc.4.md`
For the current stable v6 packet, see:
@@ -437,6 +437,15 @@ removed because a saved view was only ever the page's URL query string, which
the browser's own bookmarks already capture and share. History narrowing stays
URL-owned so a filtered history page remains a shareable, bookmarkable link.
The alert history severity facet derives each option count through
`useAlertHistoryState.countForSeverity`, using the same
`filterAlertHistoryItems` predicate that supplies the rendered list for the
current fetched period and search term. It must not count an unfiltered or
separately reduced collection that can disagree with the selected chip's
result. These counts follow the shared Inventory totals visibility preference;
the Period facet remains uncounted because it selects the fetched time scope
rather than filtering the already-fetched rows.
Alert history row timestamps render clock time in the viewer's own locale and
must carry the absolute date and time as a title. The date otherwise lives
only in the day group header, which scrolls out of sight, and a hardcoded
@@ -4331,7 +4331,10 @@ first — `entries` carrying `notificationId`, `type`, `destinationId`,
counts are retention-bounded and must not read as lifetime history; webhook
secrets are redacted from error text before the payload leaves the API. The
frontend transport in `frontend-modern/src/api/notifications.ts` validates
entry shape and drops malformed rows rather than rendering them. Successful
entry shape and drops malformed rows rather than rendering them. It must route
`entries` through the shared `arrayOrEmpty` normalization helper instead of
creating a notifications-local array fallback that can drift from other API
clients. Successful
test-send responses from `POST /api/notifications/test` and
`POST /api/notifications/webhooks/test` must include `deliveryPaused: true`
whenever real alert delivery is gated off, so a passing test cannot be
@@ -1461,10 +1461,10 @@ diagnostics. The same release workflow also executes the generated self-signed
and custom-CA Windows installer commands through Windows PowerShell 5.1 before
release assembly, so the first HTTPS fetch is release proof rather than a
string-shape assertion.
The active prerelease `v6.3.0-rc.3` cut sets the repo-root `VERSION`,
The active prerelease `v6.3.0-rc.4` cut sets the repo-root `VERSION`,
repo-root `docker-compose.yml` image default, `scripts/install-docker.sh`
fallback, and Helm chart release metadata to the same `6.3.0-rc.3` release
version. It follows stable `v6.2.1`, opens the published 6.3 candidate line, uses
fallback, and Helm chart release metadata to the same `6.3.0-rc.4` release
version. It follows `v6.3.0-rc.3` on the published 6.3 candidate line, uses
`rollback_version=v6.2.1`, and keeps stable/latest pointers on `v6.2.1`. This
candidate establishes the new minor-release packet with durable scoped Patrol
objectives, validated read-only observers, verified work receipts, agent action
@@ -1473,8 +1473,11 @@ monitoring correctness fixes. The advanced branch also carries the decision-firs
Patrol inbox, first-class Actions workspace, canonical platform-admission
projection, and bounded concurrent unified-resource hydration. Subscription-backed
Patrol turns bound command cleanup after an idle deadline so descendant-held
output pipes cannot extend the caller-owned stall budget. The changes since
`v6.3.0-rc.2` do not require a Pulse Mobile client change and preserve the
output pipes cannot extend the caller-owned stall budget. This candidate also
adds canonical estate summaries and search, a real-delivery activity log,
least-privilege agent installation, and bounded Docker-in-LXC discovery while
preserving unreadable settings and AI state. The changes since
`v6.3.0-rc.3` do not require a Pulse Mobile client change and preserve the
existing mobile, Relay, onboarding, and mobile-facing API contracts, so the
server cut is classified `no-mobile-impact`; no companion upload or public
mobile-store rollout is part of this candidate.
@@ -1760,8 +1763,8 @@ For the active stable `v6.1.2` cut, the repo-root compose default and
`scripts/install-docker.sh` fallback must both pin `6.1.2` whenever the
governed `VERSION` is that stable cut. The stable promotion guard remains in
force and rejects leftover `-rc.` defaults.
For the active prerelease `v6.3.0-rc.3` cut, the repo-root compose default and
`scripts/install-docker.sh` fallback must both pin `6.3.0-rc.3`
For the active prerelease `v6.3.0-rc.4` cut, the repo-root compose default and
`scripts/install-docker.sh` fallback must both pin `6.3.0-rc.4`
until the next governed stable cut moves them forward. Each new release moves
these two pins together with the repo-root `VERSION` and the Helm chart metadata
in the same commit; a cut that leaves any of the four on a superseded value is a
@@ -4301,6 +4301,13 @@ search-aware active-state accessor to `FilterBar`; the shared shell owns where
the contextual Clear filters action appears, while the alerts feature must not
fall back to sequential per-control URL writes or hide that action for a
search-only result set.
Alert History severity option counts also follow the shared estate-orientation
contract: the alerts feature supplies counts from the exact predicate used by
its list, while `FilterBar` renders the values and the shared Inventory totals
visibility preference decides whether they are shown. A feature must not build
counts from page-wide alert totals after search or another active facet has
narrowed the rendered rows, and a time-scope option must not claim a count from
an unfetched period.
Because that popover combines view application, default selection, removal,
and an inline naming form, it is a labelled non-modal dialog rather than an
ARIA menu. Its trigger exposes the dialog relationship, Escape returns focus
@@ -344,6 +344,10 @@ unreadable queue is an error, never an empty log. This per-attempt surface is
deliberately distinct from the content-free telemetry aggregate above, which
remains identity-free. Test sends bypass the queue and must not appear in the
delivery log, and the destinations UI says so where the log renders.
The frontend client normalizes the `entries` collection through the shared API
collection helper before validating each record, so a malformed collection or
row cannot bypass the canonical client boundary or create a module-local
fallback shape.
Because test sends also bypass the alert activation gate, a bare success
result is exactly how installs come to believe delivery works while every
@@ -1,15 +1,15 @@
# Pulse v6.3.0-rc.3 Release Notes
# Pulse v6.3.0-rc.4 Release Notes
`v6.3.0-rc.3` is a release candidate for the next Pulse v6 minor release,
following `v6.3.0-rc.2` in the 6.3 line and stable `v6.2.1`. It carries the
`v6.3.0-rc.4` is a release candidate for the next Pulse v6 minor release,
following `v6.3.0-rc.3` in the 6.3 line and stable `v6.2.1`. It carries the
Patrol v2 operating model, a first-class Actions workspace, guarded agent
action preflight, large-estate response improvements, and monitoring
correctness fixes.
This candidate adds a group of alert-delivery repairs. Production telemetry
showed installs with a configured notification destination that had never
delivered anything and had no way to find out, because the channel that would
carry such a warning is the one that had failed.
This candidate adds estate-first platform summaries and search, an operator-
visible notification delivery log, a supported least-privilege agent profile,
and safer Docker-in-LXC discovery. It also prevents failed settings or AI-state
reads from silently overwriting preserved data.
## Highlights
@@ -19,6 +19,14 @@ carry such a warning is the one that had failed.
without granting mutation authority.
- Approved actions gain agent preflight and stable refusal telemetry; large
installations gain compressed APIs and indexed lookups.
- Platform pages now lead with estate totals, status facets, and search that
share the same predicates as their underlying tables.
- Notification settings show the outcome of real delivery attempts instead of
relying on test sends as a proxy for live delivery health.
- Docker-in-LXC discovery is explicitly controlled and backs off against slow
or failing Proxmox hosts instead of creating a probe storm.
- Unified Agent installs can opt into a supported least-privilege profile with
narrowly scoped elevation for the capabilities that require it.
## Added
@@ -35,6 +43,12 @@ carry such a warning is the one that had failed.
- Unified Agent preflight contracts for package updates, package-cache cleanup,
and Docker lifecycle or update operations.
- Production security deployment guidance and a focused security-review packet.
- Estate overviews and infrastructure search across Proxmox, Docker,
Kubernetes, TrueNAS, VMware, and standalone agent surfaces.
- A seven-day notification delivery activity log with destination and outcome
details for queued live alerts.
- An administrator setting for Docker-in-LXC discovery and a supported
least-privilege Unified Agent installation profile.
## Improved
@@ -65,6 +79,10 @@ carry such a warning is the one that had failed.
limits, readiness checks, deadlines, and continuation latency more reliably.
- Subscription-backed turns now complete their idle timeout promptly even when
a canceled CLI descendant still holds an inherited output pipe open.
- Platform and alert-history facet counts are derived from each table's own
filter predicate, keeping summary totals aligned with the visible rows.
- Failed settings and AI-state reads preserve the last known data and surface
the failure instead of treating unreadable storage as an empty value.
## Fixed
@@ -85,6 +103,10 @@ carry such a warning is the one that had failed.
- vSphere backup status, agent thermal history, explicit cluster-member address
overrides, and discovery-analysis request timeouts now reflect their actual
runtime state.
- Docker-in-LXC discovery no longer retries overlapping probes against slow
Proxmox hosts, and command enablement after install is reflected without a
reinstall.
- In-progress Proxmox backups are no longer presented as completed backups.
- The notifications surface now states when alert delivery is paused, and that
a passing test send does not prove live alerts are getting through. Test
sends bypass the delivery pause, so a configured destination could look
@@ -110,7 +132,7 @@ carry such a warning is the one that had failed.
## Upgrade Notes
Use the normal v6 install or update flow for `v6.3.0-rc.3` only when you are
Use the normal v6 install or update flow for `v6.3.0-rc.4` only when you are
comfortable testing a release candidate. The rollback target is `v6.2.1`.
The exact rollback reinstall command is:
@@ -119,7 +141,7 @@ The exact rollback reinstall command is:
./scripts/install.sh --version v6.2.1
```
The changes since `v6.3.0-rc.2` do not require a Pulse Mobile client change
The changes since `v6.3.0-rc.3` do not require a Pulse Mobile client change
and preserve the existing mobile, Relay, onboarding, and mobile-facing API
contracts. No companion mobile build upload or public mobile-store rollout is
part of this candidate.
@@ -1,7 +1,7 @@
# Pulse v6.3.0-rc.3
# Pulse v6.3.0-rc.4
_This changelog describes the changes since `v6.3.0-rc.2` and carries forward
the complete cumulative 6.3 packet since stable `v6.2.1`. `v6.3.0-rc.3` is a
_This changelog describes the changes since `v6.3.0-rc.3` and carries forward
the complete cumulative 6.3 packet since stable `v6.2.1`. `v6.3.0-rc.4` is a
prerelease and rolls back to stable `v6.2.1`._
## Added
@@ -19,6 +19,11 @@ prerelease and rolls back to stable `v6.2.1`._
- System-scoped alerts, so Pulse can report a fault in itself rather than only
in a monitored resource. Broken notification delivery is the first, raised as
an ordinary alert so it reaches the alert list and navigation badge.
- Estate summaries, status facets, and canonical search on the primary
infrastructure platform pages.
- A seven-day delivery log for real alert notification attempts.
- A supported least-privilege Unified Agent installation profile and an
administrator-controlled Docker-in-LXC discovery mode.
## Changed
@@ -39,6 +44,10 @@ prerelease and rolls back to stable `v6.2.1`._
source-tree value.
- Buffered subscription CLI turns cannot extend a caller-owned idle deadline
while a canceled descendant keeps an inherited output pipe open.
- Platform and alert-history counts now use the same canonical filter
predicates as their visible result tables.
- Failed settings and AI-state reads retain preserved data and expose the read
failure instead of silently replacing state with defaults.
## Fixed
@@ -67,6 +76,10 @@ prerelease and rolls back to stable `v6.2.1`._
drained and a resource oscillating just under the threshold was never damped.
- Mobile tables keep narrow values readable, preserve compact replication
values, and use a consistent density across platform surfaces.
- Docker-in-LXC discovery serializes and backs off slow probes, preserves
post-install command enablement, and surfaces failures without host churn.
- In-progress Proxmox backups remain in progress rather than being shown as
completed.
## Security
@@ -79,8 +92,8 @@ prerelease and rolls back to stable `v6.2.1`._
## Release Metadata
- Version: `v6.3.0-rc.3`
- Previous release: `v6.2.1`
- Version: `v6.3.0-rc.4`
- Previous release: `v6.3.0-rc.3`
- Previous stable: `v6.2.1`
- Rollback target: `v6.2.1`
- Rollback command: `./scripts/install.sh --version v6.2.1`
@@ -89,6 +102,6 @@ prerelease and rolls back to stable `v6.2.1`._
- Windows signing decision: the standing prerelease path publishes exact-SHA,
checksum, and detached-signature verified Windows agents without
Authenticode; stable `v6.3.0` restores mandatory SignPath signing
- Mobile decision: `no-mobile-impact`; changes since `v6.3.0-rc.2` preserve the
- Mobile decision: `no-mobile-impact`; changes since `v6.3.0-rc.3` preserve the
existing mobile, Relay, onboarding, and mobile-facing API contracts, and no
companion upload or public store rollout is required
+15 -16
View File
@@ -1,36 +1,35 @@
{
"version": 1,
"base_sha": "ee4cd560b6352fac50b988104e3ee370db8669c1",
"verified_at": "2026-08-20T21:08:35Z",
"base_sha": "4c7b1a2434385f13c5c89dd3a113cd894636ac4d",
"verified_at": "2026-08-20T22:47:56Z",
"result": "passed",
"changed_paths": [
"frontend-modern/src/features/alerts/AlertHistoryFiltersCard.tsx",
"frontend-modern/src/features/alerts/useAlertHistoryState.ts"
"frontend-modern/src/api/notifications.ts"
],
"content_sha256": {
"frontend-modern/src/features/alerts/AlertHistoryFiltersCard.tsx": "07836c5b85ea484cfe984b2d0de5c302202ef6b844eafdc776b3f979f0938acb",
"frontend-modern/src/features/alerts/useAlertHistoryState.ts": "7528bd735ff84961e8bd880d89f0253255c698ab5910c1eb2fb10cf0255e39e8"
"frontend-modern/src/api/notifications.ts": "ac714c8f87c1e26f1abf523b4c4c7b83c0ce75250c92741b69dc6a36b35557ff"
},
"routes": [
"/alerts/history"
"/alerts/notifications"
],
"viewports": [
{
"width": 1280,
"height": 720
"width": 1440,
"height": 1000
},
{
"width": 375,
"height": 812
"width": 390,
"height": 844
}
],
"states": [
"Alert history severity facet now counted from the live history data: All 47 / Critical 8 / Warning 13, with Period chips left uncounted as time scopes",
"Critical selected: the page's own summary reads '8 alerts', matching the chip count exactly (counts share filterAlertHistoryItems with the list)",
"Chips verified through the mobile Filters sheet (the hidden preview pane renders the responsive mobile layout); the same options render inline at desktop width"
"Notifications page loaded with delivery paused and all destination configuration sections available",
"Recent delivery activity rendered its seven-day empty state with the refresh control enabled",
"Desktop and narrow layouts kept the delivery card, explanatory copy, refresh control, and empty state visible without clipping"
],
"interactions": [
"Opened the Filters sheet on /alerts/history and read the counted severity chips",
"Clicked the Critical chip, confirmed the '8 alerts' summary, then reset the facet back to All"
"Refreshed delivery status at desktop width and confirmed the empty state remained available",
"Scrolled through the narrow notification layout and refreshed delivery status again",
"Checked the browser console after both refresh interactions and found no errors"
]
}
+52 -9
View File
@@ -2,15 +2,26 @@
Pulse agents incorporate several security mechanisms to ensure that the code running on your infrastructure is authentic and untampered with.
**Start with the least privilege that answers your monitoring question.** For
Proxmox VE, PBS, and PMG, that is usually no agent at all: API-only monitoring
with a read-only token covers inventory, status, and metrics, and the
generated setup script creates a privilege-separated monitoring user for it
(see [Proxmox Deployment Choices](#proxmox-deployment-choices)). Install a
host agent only where you want data the platform API cannot provide, and on
Linux consider the supported
[least-privilege profile](#least-privilege-agent-profile) before the root
default.
## Agent Privilege Model
Pulse's Linux/systemd installer runs the unified agent as `root` by default.
That is intentional for full host telemetry: disk SMART data, mdadm/RAID state,
temperature sensors, Docker or Podman socket reads, Proxmox host-local details
that are not available through the API, and some NAS/platform integrations
commonly require root or equivalent local privileges. Running the service as a
lower-privilege user may work for a narrow subset of metrics, but it is not a
supported full-telemetry profile today.
commonly require root or equivalent local privileges. On Linux/systemd hosts,
the supported alternative is the least-privilege profile documented below; it
trades the root-only collectors it has not been granted for a dedicated
non-root service user.
Treat a host agent like other infrastructure monitoring software with local
root read access:
@@ -123,12 +134,44 @@ mirrors the generated read/monitoring ACLs onto both the service user and the
token. For PBS, the generated script grants the `Audit` ACL to both the service
user and token.
Running `pulse-agent` as a custom non-root systemd user is possible by editing
the service unit, but it is not a supported full-telemetry mode today. Expect
gaps in SMART, temperature, Docker socket, ZFS/Ceph/mdadm, mount, and platform
integration data unless you deliberately grant equivalent capabilities or group
access. If you choose that route, treat it as a local hardening profile and
verify the exact metrics you care about after the change.
## Least-Privilege Agent Profile
On standard Linux systemd hosts, `install.sh --least-privilege` is a supported
alternative to the root profile. It runs the service as a dedicated
`pulse-agent` system user (nologin shell, owning only its state directory and
binary), joins the `docker` group when Docker monitoring is enabled so socket
reads keep working, and keeps every hardening directive of the root unit while
dropping the LXC-attach ambient capability grant entirely.
Two optional flags restore the collectors that genuinely need elevation, each
through an exact-command sudoers grant validated with `visudo` and a
root-owned wrapper the agent is pointed at via an absolute-path-only
environment override. Because `NoNewPrivileges` blocks `sudo` entirely, a
unit with an active grant sets `NoNewPrivileges=false` while keeping the
remaining hardening; a grantless least-privilege install keeps
`NoNewPrivileges=true`. Choose grants deliberately: each one is a scoped,
auditable widening of the profile.
- `--grant-smart` allows exactly `smartctl`, restoring SMART disk health.
- `--grant-pct` allows exactly `pct list` and `pct df`, restoring Proxmox LXC
filesystem capacity. The grant deliberately excludes `pct exec`, `start`,
`stop`, and `enter`, so guest Docker inventory stays a root-profile feature.
What the profile gives up: command execution (`--enable-commands` is refused
and a later server-side enable requires reinstalling the root profile),
`pct exec` guest Docker inventory, and any platform integration that needs
device or socket access you have not granted. Core metrics, mounts, `/proc`
RAID state, hwmon temperatures, and Docker socket reads work without root.
Ungranted collectors fail soft, and the agent reports its privilege profile so
**Settings → Infrastructure → Agent Doctor** shows the service user and active
helpers instead of presenting missing collectors as a fault. Appliance
platforms (TrueNAS, Synology, QNAP, Unraid) and non-systemd init systems keep
the root profile; the installer refuses `--least-privilege` there rather than
silently falling back to root.
`--update` preserves an existing least-privilege profile and its grants
without the flags being repeated. Uninstall removes the sudoers file and
helpers; the inert system user is left behind deliberately.
## Supply-Chain Boundary
@@ -48,10 +48,13 @@ The default posture limits that boundary:
- Proxmox guest Docker inventory through `pct exec` is disabled by default and
requires an explicit server setting.
A custom non-root systemd user is possible, but it is not currently a supported
full-telemetry profile. Expect gaps unless you deliberately grant equivalent
device, filesystem, or socket access. If API data is sufficient, API-only
monitoring is the cleaner least-privilege choice.
On standard Linux systemd hosts the installer also offers a supported
least-privilege profile: `--least-privilege` runs the service as a dedicated
`pulse-agent` system user, with optional `--grant-smart` and `--grant-pct`
flags that restore SMART and Proxmox LXC filesystem collection through
exact-command sudoers grants. Command execution and `pct exec` guest inventory
stay root-profile features. If API data is sufficient, API-only monitoring
remains the cleanest least-privilege choice of all — it needs no agent.
See [Agent Security](AGENT_SECURITY.md) for the precise command, guest-access,
update, and service-hardening boundaries.
+2 -2
View File
@@ -4,8 +4,8 @@ This guide covers practical upgrade steps for existing Pulse installs moving to
For the current v6 release candidate packet, see:
- `docs/releases/RELEASE_NOTES_v6.3.0-rc.3.md`
- `docs/releases/V6_CHANGELOG_v6.3.0-rc.3.md`
- `docs/releases/RELEASE_NOTES_v6.3.0-rc.4.md`
- `docs/releases/V6_CHANGELOG_v6.3.0-rc.4.md`
For the current stable v6 packet, see:
@@ -362,6 +362,17 @@ describe('NotificationsAPI', () => {
expect(log.windowDays).toBe(7);
});
it('normalizes a malformed delivery-log collection through the shared API boundary', async () => {
apiFetchJSONMock.mockResolvedValueOnce({
entries: { notificationId: 'not-a-collection' },
window_days: 7,
} as any);
const log = await NotificationsAPI.getDeliveryLog();
expect(log).toEqual({ entries: [], windowDays: 7 });
});
it('passes the deliveryPaused flag through from test-send responses', async () => {
apiFetchJSONMock.mockResolvedValueOnce({
status: 'success',
+1 -1
View File
@@ -339,7 +339,7 @@ export class NotificationsAPI {
const payload = await apiFetchJSON<Record<string, unknown>>(
`${this.baseUrl}/delivery-log${query}`,
);
const rawEntries = Array.isArray(payload.entries) ? payload.entries : [];
const rawEntries = arrayOrEmpty<unknown>(payload.entries);
const entries: NotificationDeliveryLogEntry[] = [];
for (const rawEntry of rawEntries) {
const entry = normalizeDeliveryLogEntry(rawEntry);
@@ -713,9 +713,7 @@ describe('workloadSelectors', () => {
it('returns the input list when no scope or exclusions apply', () => {
const guests = [makeGuest(1), makeGuest(2)];
expect(
selectVisibleWorkloadInventory({ guests, excludedTypes: noExclusions }),
).toBe(guests);
expect(selectVisibleWorkloadInventory({ guests, excludedTypes: noExclusions })).toBe(guests);
expect(
selectVisibleWorkloadInventory({
guests,
+27 -19
View File
@@ -20684,13 +20684,8 @@ func TestContract_AgentFleetDiagnosticsEndpointSurfacesStableShape(t *testing.T)
if err != nil {
t.Fatalf("read router_routes_registration.go: %v", err)
}
monitoringSource, err := os.ReadFile("../monitoring/agent_fleet_doctor.go")
if err != nil {
t.Fatalf("read monitoring agent_fleet_doctor.go: %v", err)
}
handlerSrc := string(handler)
routerSrc := string(router)
monitoringSrc := string(monitoringSource)
if !strings.Contains(routerSrc, `"/api/agents/diagnostics"`) ||
!strings.Contains(routerSrc, `RequireAdmin(r.config, RequireScope(config.ScopeSettingsRead, r.handleAgentFleetDiagnostics))`) {
@@ -20699,21 +20694,34 @@ func TestContract_AgentFleetDiagnosticsEndpointSurfacesStableShape(t *testing.T)
if !strings.Contains(handlerSrc, "GetAgentFleetDiagnosticsForTarget(serverVersion, agentUpdateTargetVersion, time.Now().UTC())") {
t.Error("agent fleet diagnostics handler must delegate to the monitoring-owned read-only producer")
}
for _, required := range []string{
"SchemaVersion int `json:\"schemaVersion\"`",
"GeneratedAt int64 `json:\"generatedAt\"`",
"ServerVersion string `json:\"serverVersion,omitempty\"`",
"AgentUpdateTargetVersion string `json:\"agentUpdateTargetVersion,omitempty\"`",
"Summary AgentFleetDiagnosticSummary `json:\"summary\"`",
"Agents []AgentFleetAgentDiagnostic `json:\"agents\"`",
"ConnectionID string `json:\"connectionId,omitempty\"`",
"AgentUpdate *AgentFleetDiagnosticUpdate `json:\"agentUpdate,omitempty\"`",
"AgentModules []AgentFleetDiagnosticModule `json:\"agentModules,omitempty\"`",
"Reasons []AgentFleetDiagnosticReason `json:\"reasons\"`",
"RepairActions []AgentFleetDiagnosticRepair `json:\"repairActions,omitempty\"`",
for _, required := range []struct {
typeOf reflect.Type
name string
goType reflect.Type
jsonTag string
}{
{reflect.TypeOf(monitoring.AgentFleetDiagnostics{}), "SchemaVersion", reflect.TypeOf(int(0)), "schemaVersion"},
{reflect.TypeOf(monitoring.AgentFleetDiagnostics{}), "GeneratedAt", reflect.TypeOf(int64(0)), "generatedAt"},
{reflect.TypeOf(monitoring.AgentFleetDiagnostics{}), "ServerVersion", reflect.TypeOf(""), "serverVersion,omitempty"},
{reflect.TypeOf(monitoring.AgentFleetDiagnostics{}), "AgentUpdateTargetVersion", reflect.TypeOf(""), "agentUpdateTargetVersion,omitempty"},
{reflect.TypeOf(monitoring.AgentFleetDiagnostics{}), "Summary", reflect.TypeOf(monitoring.AgentFleetDiagnosticSummary{}), "summary"},
{reflect.TypeOf(monitoring.AgentFleetDiagnostics{}), "Agents", reflect.TypeOf([]monitoring.AgentFleetAgentDiagnostic(nil)), "agents"},
{reflect.TypeOf(monitoring.AgentFleetAgentDiagnostic{}), "ConnectionID", reflect.TypeOf(""), "connectionId,omitempty"},
{reflect.TypeOf(monitoring.AgentFleetAgentDiagnostic{}), "AgentUpdate", reflect.TypeOf((*monitoring.AgentFleetDiagnosticUpdate)(nil)), "agentUpdate,omitempty"},
{reflect.TypeOf(monitoring.AgentFleetAgentDiagnostic{}), "AgentModules", reflect.TypeOf([]monitoring.AgentFleetDiagnosticModule(nil)), "agentModules,omitempty"},
{reflect.TypeOf(monitoring.AgentFleetAgentDiagnostic{}), "Reasons", reflect.TypeOf([]monitoring.AgentFleetDiagnosticReason(nil)), "reasons"},
{reflect.TypeOf(monitoring.AgentFleetAgentDiagnostic{}), "RepairActions", reflect.TypeOf([]monitoring.AgentFleetDiagnosticRepair(nil)), "repairActions,omitempty"},
} {
if !strings.Contains(monitoringSrc, required) {
t.Errorf("agent fleet diagnostics payload missing stable field %q", required)
field, ok := required.typeOf.FieldByName(required.name)
if !ok {
t.Errorf("%s payload missing stable field %s", required.typeOf.Name(), required.name)
continue
}
if field.Type != required.goType {
t.Errorf("%s.%s type = %s, want %s", required.typeOf.Name(), required.name, field.Type, required.goType)
}
if got := field.Tag.Get("json"); got != required.jsonTag {
t.Errorf("%s.%s json tag = %q, want %q", required.typeOf.Name(), required.name, got, required.jsonTag)
}
}
}
@@ -169,6 +169,7 @@ func TestTransportCommandCatalogsResolveToRegistry(t *testing.T) {
"MsgTypeExecuteCmd": {Role: TransportRoleMutationRequest, MutationID: "transport.agent.raw-command", DurableAuthorityID: "assistant.resource-action"},
"MsgTypeHostStorageCleanup": {Role: TransportRoleMutationRequest, MutationID: "transport.agent.host-package-cache-cleanup", DurableAuthorityID: "resource.host.package-cache-cleanup"},
"MsgTypeReadFile": {Role: TransportRoleProtocol},
"MsgTypeCancelCmd": {Role: TransportRoleProtocol},
"MsgTypeHostUpdate": {Role: TransportRoleMutationRequest, MutationID: "transport.agent.host-package-update", DurableAuthorityID: "resource.host.package-update"},
"MsgTypeDockerContainerLifecycle": {Role: TransportRoleMutationRequest, MutationID: "transport.agent.docker-container-lifecycle", DurableAuthorityID: "resource.docker.container-lifecycle"},
"MsgTypeDockerContainerUpdate": {Role: TransportRoleMutationRequest, MutationID: "transport.agent.docker-container-update", DurableAuthorityID: "resource.docker.container-update"},
+1 -1
View File
@@ -6,7 +6,7 @@ set -euo pipefail
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
DOCKER_IMAGE_REPO="${DOCKER_IMAGE_REPO:-rcourtman/pulse}"
CANONICAL_DEFAULT_PULSE_VERSION="6.3.0-rc.3"
CANONICAL_DEFAULT_PULSE_VERSION="6.3.0-rc.4"
resolve_default_pulse_version() {
if [ -n "${PULSE_IMAGE_VERSION:-}" ]; then
@@ -205,6 +205,9 @@ func renderLifecycleService(t *testing.T, stateDir, stateSource, unitPath, pulse
SERVER_FINGERPRINT=""
OBSERVERS_FILE=""
ENABLE_COMMANDS="` + commandFlag + `"
LEAST_PRIVILEGE="false"
GRANT_SMART="false"
GRANT_PCT="false"
HEALTH_ADDR_SET="true"
HEALTH_ADDR=""
ENROLL="true"
@@ -234,6 +237,7 @@ func renderLifecycleService(t *testing.T, stateDir, stateSource, unitPath, pulse
` + extractInstallShellFunction(t, "join_exec_arg_items") + `
` + extractInstallShellFunction(t, "build_exec_args") + `
` + extractInstallShellFunction(t, "systemd_agent_requires_lxc_attach") + `
` + extractInstallShellFunction(t, "systemd_agent_may_attach_lxc") + `
` + extractInstallShellFunction(t, "render_systemd_agent_unit") + `
` + extractInstallShellFunction(t, "save_connection_info") + recovery + `
ensure_runtime_token_file "$STATE_DIR"
@@ -658,6 +658,10 @@ func TestCurrentPrereleasePacketTracksInstallMetadata(t *testing.T) {
"Actions and Patrol identify whether a decision originated from a finding, alert, objective, or explicit operator request",
"Action refusal telemetry now classifies target changes, prerequisites, contract failures",
"Subscription-backed turns now complete their idle timeout promptly",
"Platform pages now lead with estate totals, status facets, and search",
"Notification settings show the outcome of real delivery attempts",
"Docker-in-LXC discovery is explicitly controlled and backs off against slow or failing Proxmox hosts",
"Unified Agent installs can opt into a supported least-privilege profile",
"The rollback target is `v"+previous+"`",
"The changes since `v"+comparisonVersion+"` do not require a Pulse Mobile client change",
"preserve the existing mobile, Relay, onboarding, and mobile-facing API contracts",
@@ -675,6 +679,9 @@ func TestCurrentPrereleasePacketTracksInstallMetadata(t *testing.T) {
"A primary Actions workspace for approvals, governed plans, and action records",
"Typed Unified Agent action preflight for supported host and Docker operations",
"Stable pre-mutation refusal codes and fleet telemetry buckets",
"Estate summaries, status facets, and canonical search on the primary infrastructure platform pages",
"A seven-day delivery log for real alert notification attempts",
"A supported least-privilege Unified Agent installation profile",
"Windows signing decision: the standing prerelease path publishes exact-SHA, checksum, and detached-signature verified Windows agents without Authenticode; stable `v"+stableTarget+"` restores mandatory SignPath signing",
"Mobile decision: `no-mobile-impact`",
"changes since `v"+comparisonVersion+"` preserve the existing mobile, Relay, onboarding, and mobile-facing API contracts",
@@ -358,6 +358,7 @@ func TestInstallDockerProofTracksPrereleaseContract(t *testing.T) {
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
"The active prerelease `v"+version+"` cut sets the repo-root `VERSION`, repo-root `docker-compose.yml` image default, `scripts/install-docker.sh` fallback, and Helm chart release metadata to the same `"+version+"` release version.",
"It follows `v"+comparisonVersion+"` on the published 6.3 candidate line",
"This prerelease keeps `rollback_version=v"+previous+"`, publishes a versioned public GitHub prerelease plus versioned Docker and Helm artifacts, and does not move stable/latest install pointers or stable semver aliases.",
"The changes since `v"+comparisonVersion+"` do not require a Pulse Mobile client change and preserve the existing mobile, Relay, onboarding, and mobile-facing API contracts, so the server cut is classified `no-mobile-impact`; no companion upload or public mobile-store rollout is part of this candidate.",
"The prerelease Windows path retains exact-SHA, checksum, and detached-signature verification without Authenticode; stable `v"+stableTarget+"` restores mandatory SignPath signing unless a new version-bound decision is recorded.",