Prepare v6.1.2 stable patch release

This commit is contained in:
courtmanr@gmail.com 2026-07-26 20:38:42 +01:00
parent 7f4db466be
commit e405270e89
19 changed files with 205 additions and 63 deletions

View file

@ -1 +1 @@
6.1.1
6.1.2

View file

@ -1004,14 +1004,15 @@ func TestLoadConfig(t *testing.T) {
})
t.Run("state directory flag overrides platform default", func(t *testing.T) {
cfg, err := loadConfig([]string{"-token", "test-token", "-state-dir", "/custom/pulse-state"}, func(s string) string { return "" })
stateDir := filepath.FromSlash("/custom/pulse-state")
cfg, err := loadConfig([]string{"-token", "test-token", "-state-dir", stateDir}, func(s string) string { return "" })
if err != nil {
t.Fatal(err)
}
if cfg.StateDir != "/custom/pulse-state" {
if cfg.StateDir != stateDir {
t.Errorf("expected explicit state directory, got %q", cfg.StateDir)
}
if cfg.AgentIDFile != "/custom/pulse-state/agent-id" {
if cfg.AgentIDFile != filepath.Join(stateDir, "agent-id") {
t.Errorf("expected agent ID file under explicit state directory, got %q", cfg.AgentIDFile)
}
})

View file

@ -2,9 +2,9 @@ apiVersion: v2
name: pulse
description: Helm chart for deploying the Pulse hub and optional Docker monitoring agent.
type: application
version: 6.1.1
appVersion: "6.1.1"
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.1.1/docs/images/pulse-logo.svg
version: 6.1.2
appVersion: "6.1.2"
icon: https://raw.githubusercontent.com/rcourtman/Pulse/v6.1.2/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.1.1/docs/KUBERNETES.md
url: https://github.com/rcourtman/Pulse/blob/v6.1.2/docs/KUBERNETES.md
- name: Support
url: https://github.com/rcourtman/Pulse/discussions
artifacthub.io/maintainers: |

View file

@ -1,6 +1,6 @@
# pulse
![Version: 6.1.1](https://img.shields.io/badge/Version-6.1.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.1.1](https://img.shields.io/badge/AppVersion-6.1.1-informational?style=flat-square)
![Version: 6.1.2](https://img.shields.io/badge/Version-6.1.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 6.1.2](https://img.shields.io/badge/AppVersion-6.1.2-informational?style=flat-square)
Helm chart for deploying the Pulse hub and optional Docker monitoring agent.

View file

@ -2,7 +2,7 @@ version: '3.8'
services:
pulse:
image: ${PULSE_IMAGE:-rcourtman/pulse:6.1.1}
image: ${PULSE_IMAGE:-rcourtman/pulse:6.1.2}
container_name: pulse
restart: unless-stopped
logging:

View file

@ -7,10 +7,12 @@ For historical v4 notes that previously lived in this repo, see:
`docs/releases/RELEASE_NOTES_v4.md`
For the current stable v6 packet, see:
- `docs/releases/RELEASE_NOTES_v6.1.1.md`
- `docs/releases/V6_CHANGELOG_v6.1.1.md`
- `docs/releases/RELEASE_NOTES_v6.1.2.md`
- `docs/releases/V6_CHANGELOG_v6.1.2.md`
For earlier stable v6.1 packets, see:
- `docs/releases/RELEASE_NOTES_v6.1.1.md`
- `docs/releases/V6_CHANGELOG_v6.1.1.md`
- `docs/releases/RELEASE_NOTES_v6.1.0.md`
- `docs/releases/V6_CHANGELOG_v6.1.0.md`

View file

@ -4,11 +4,13 @@ This guide covers practical upgrade steps for existing Pulse installs moving to
For the current stable v6 packet, see:
- `docs/releases/RELEASE_NOTES_v6.1.1.md`
- `docs/releases/V6_CHANGELOG_v6.1.1.md`
- `docs/releases/RELEASE_NOTES_v6.1.2.md`
- `docs/releases/V6_CHANGELOG_v6.1.2.md`
For earlier stable v6 packets and rollout references, see:
- `docs/releases/RELEASE_NOTES_v6.1.1.md`
- `docs/releases/V6_CHANGELOG_v6.1.1.md`
- `docs/releases/RELEASE_NOTES_v6.1.0.md`
- `docs/releases/V6_CHANGELOG_v6.1.0.md`
- `docs/releases/RELEASE_NOTES_v6.0.5.md`

View file

@ -1080,31 +1080,29 @@ host-local redirect contract as runtime token minting and exchange. Proof input
must reject absolute, scheme-relative, backslash-authority, encoded-separator,
and control-character targets before constructing the handoff request.
The active stable `v6.1.1` cut sets the repo-root `VERSION`, repo-root
The active stable `v6.1.2` 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.1.1` release version. This patch
release uses the stable hotfix path with `rollback_version=v6.1.0`,
Helm chart release metadata to the same `6.1.2` release version. This patch
release uses the stable hotfix path with `rollback_version=v6.1.1`,
`hotfix_exception=true`, a release-owner reason, and no fabricated
same-version RC tag. It fixes manual Unified Agent updates that received a
distribution identifier instead of the canonical Linux platform family and
durable Docker update actions that could remain stuck after a terminal
digest-drift preflight refusal. The exact stable `main` SHA must pass the
integrated release checks and immutable-candidate build before the single-build
workflow crosses its public mutation boundary. The same workflow must finish
Docker, Helm, stable demo, install-smoke, public-health, floating-tag,
paid-runtime, and definitive-verdict lanes before the cut is complete.
The `v6.1.1` release is `existing-mobile-build-compatible`: no production
relay or mobile trust contract changed from `v6.1.0`, the matched mobile-facing
path is test-only expiry branch coverage, and Pulse Mobile `1.0.0` iOS build
`11` and Android versionCode `9` remain the compatible candidate builds without
a companion upload or public store rollout.
The release owner separately approved a `v6.1.1`-only unsigned-Windows
exception because SignPath configuration remains unavailable. Windows assets
must remain exact-SHA and manifest-bound with checksums, detached
`.sig`/`.sshsig` signatures, and published-digest verification; the release
notes must disclose the Unknown Publisher state. Stable `v6.1.2` and later
restore Authenticode unless policy records another explicit version-bound
decision.
same-version RC tag. It consolidates the post-`v6.1.1` Proxmox, TrueNAS,
metrics, availability, agent lifecycle, operator-state, and security fixes.
The exact stable `main` SHA must pass the integrated release checks and
immutable-candidate build before the single-build workflow crosses its public
mutation boundary. The same workflow must finish Docker, Helm, stable demo,
install-smoke, public-health, floating-tag, paid-runtime, and
definitive-verdict lanes before the cut is complete. No mobile-facing path
changed from `v6.1.1`, so the release decision is `no-mobile-impact` and no
companion build or public mobile-store rollout is required. Stable `v6.1.2`
restores mandatory Windows Authenticode signing through SignPath; no unsigned
exception is authorized, and the release candidate must fail closed when the
signing configuration or returned signer proof is unavailable.
The preceding stable `v6.1.1` cut used the stable hotfix path with
`rollback_version=v6.1.0` and an owner-approved, version-bound unsigned Windows
exception. Pulse Mobile `1.0.0` iOS build `11` and Android versionCode `9`
remained the compatible candidate builds without a companion upload or public
store rollout.
The preceding stable `v6.1.0` cut used
`promoted_from_tag=v6.1.0-rc.4`, `rollback_version=v6.0.5`, and the
@ -1218,8 +1216,8 @@ compose image default, standalone installer fallback constant, and packaged
Helm metadata. A draft release workflow failure caused by stale image or chart
pins is a release-packet blocker until the defaults, tests, and evidence
record are refreshed from the new branch head.
For the active stable `v6.1.1` cut, the repo-root compose default and
`scripts/install-docker.sh` fallback must both pin `6.1.1` until the next
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` until the next
governed stable cut moves them forward. The stable promotion guard remains in
force and rejects leftover `-rc.` defaults.
The RC7 packet refresh records `fc10de9b5477613316473267b72b05b6b2b7aaff`

View file

@ -0,0 +1,88 @@
# Pulse v6.1.2 Release Notes
`v6.1.2` is a stable patch release following `v6.1.1`. It consolidates
monitoring, storage, agent-lifecycle, security, and operator-experience fixes
completed since the previous stable cut.
## Highlights
- Proxmox monitoring is more authoritative across cluster membership, guest
sampling, Ceph MON/MGR counts, physical disks, backup posture, and scheduled
multi-guest `vzdump` jobs.
- TrueNAS monitoring uses the supported JSON-RPC transport, restores pool and
replication health classification, and keeps storage topology and SMART
evidence aligned.
- Unified Agent enrollment and update recovery are more resilient. Constrained
QNAP and Unraid installs now fail early on insufficient disk space and use
bounded rotating logs instead of unbounded watchdog output.
- Polling cadence changes apply to live monitors immediately, connection
staleness scales with the configured interval, and PBS health remains owned
by current probe evidence.
- Resource links, availability summaries, organization switching, recovery
posture, and operator overrides behave consistently across the main
infrastructure surfaces.
## Changed
- Evidence-based pool-health alerts preserve the provider's authoritative
topology and health discriminator.
- Scheduled Proxmox backup jobs are projected into stable per-guest task
records, including running, completed, and failed guest outcomes.
- SATA devices behind SAS HBAs retain SATA transport handling for SMART
collection, while native Unraid and TrueNAS topology remains authoritative.
- Metrics persistence reduces SQLite write amplification and keeps bounded
history caches without weakening current-state freshness.
- Local AI provider readiness checks exercise the representative Patrol
runtime path and provide more actionable compatibility results.
## Fixed
- Changing backup and PBS polling intervals now updates active monitors without
requiring a service restart (#1619).
- Proxmox Quincy and Squid Ceph status payloads report the correct MON and MGR
counts (#1626).
- Scheduled multi-guest Proxmox backup jobs no longer disappear from
guest-centric coverage and alert-intent views.
- QNAP and Unraid agent installs detect insufficient temporary or destination
space before downloading, honor `TMPDIR`, and prevent appliance watchdog logs
from growing without bound (#1617).
- Operator-state reads normalize an absent automatic-action policy to
`capabilityNames: []`, preventing expanded resource rows from crashing.
Intentionally-offline and never-auto-remediate controls remain available for
stopped Docker containers (#1621, #1622).
- Proxmox cluster membership, guest CPU rate sampling, physical-disk identity,
agent registration, and workload refresh remain coherent across nodes.
- TrueNAS boot-pool, replication, pool-health, disk, and alert-threshold
evidence no longer falls back to stale or unsupported interpretations.
- Availability summaries no longer write into the live resource registry,
grace-period guests stop generating metrics, and configured discovery
suppression fails closed.
- Webhook URLs and PBS probe failures are redacted from logs and diagnostics,
local-provider requests retain SSRF protections, and legacy RBAC recovery
remains reachable when an old import fails.
- Resource web-interface links are canonicalized consistently, and live queries
refresh after an organization switch.
## Upgrade Notes
Use the normal v6 install or update flow for `v6.1.2`.
Windows Unified Agent binaries in `v6.1.2` are required to be
Authenticode-signed. The governed release workflow fails closed unless the
SignPath configuration is present and every returned executable verifies
against the expected signer.
The rollback target for this patch release is `v6.1.1`. The exact rollback
reinstall command is:
```bash
./scripts/install.sh --version v6.1.1
```
The server/mobile decision is `no-mobile-impact`. No mobile-facing relay,
pairing, onboarding, or trust-contract path changed from `v6.1.1`, so this
release does not upload a companion mobile build or start a public
mobile-store rollout.
Paid Pulse Pro, Relay, and eligible legacy customers should continue to use the
private download page and private runtime image for paid runtime features.

View file

@ -0,0 +1,48 @@
# Pulse v6.1.2
_This changelog describes stable `v6.1.2` compared with stable `v6.1.1`._
## Changed
- Proxmox and PBS monitoring now uses fresher ownership boundaries for cluster,
guest, backup, disk, Ceph, and connection-health state.
- TrueNAS uses its supported JSON-RPC transport and retains authoritative pool,
replication, disk-topology, and SMART evidence.
- Metrics persistence reduces SQLite write amplification, while polling cadence
changes apply immediately to live monitors.
- Evidence-based pool-health alerts and canonical web-interface links are
available across the unified resource surfaces.
- Patrol readiness checks are representative of the configured local-provider
runtime.
## Fixed
- Unified Agent installs on constrained QNAP and Unraid roots preflight free
space and keep agent/watchdog logging bounded (#1617).
- Saved backup and PBS polling intervals take effect without a service restart
(#1619).
- Operator-state payloads keep capability lists iterable, and stopped
containers retain intentionally-offline and never-auto-remediate controls
(#1621, #1622).
- Proxmox Quincy and Squid Ceph schemas report correct monitor and manager
counts (#1626).
- Scheduled multi-guest `vzdump` jobs contribute stable per-guest backup status.
- Proxmox membership, sampling, registration, disk identity, and workload
refresh remain coherent across clusters.
- TrueNAS health, replication, boot-pool, threshold, and disk evidence no
longer falls back to stale interpretations.
- Availability, discovery suppression, RBAC recovery, diagnostic redaction,
and organization-switch refresh paths fail safely and consistently.
## Release Metadata
- Version: `v6.1.2`
- Previous stable: `v6.1.1`
- Rollback target: `v6.1.1`
- Rollback command: `./scripts/install.sh --version v6.1.1`
- Promotion path: stable patch hotfix from `main`, using the integrated
exact-SHA candidate and definitive release verdict
- Windows signing decision: Authenticode is required through SignPath; no
unsigned-Windows exception is authorized for `v6.1.2`
- Mobile decision: `no-mobile-impact`; no companion build upload or public
store rollout is required

View file

@ -5,6 +5,7 @@ import (
"errors"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
@ -218,7 +219,8 @@ func TestBranchcov0724pmWriteSelfTestTokenFile(t *testing.T) {
if err != nil {
t.Fatalf("stat token file: %v", err)
}
if info.Mode().Perm() != 0o600 {
// Windows does not expose POSIX permission bits through os.FileMode.
if runtime.GOOS != "windows" && info.Mode().Perm() != 0o600 {
t.Fatalf("token file mode = %o, want 0600", info.Mode().Perm())
}
})

View file

@ -6,6 +6,7 @@ import (
"io"
"net/netip"
"reflect"
"runtime"
"strings"
"testing"
@ -131,6 +132,9 @@ func TestBuildContainerRecreatePlanNetworkModesAndDesiredConfiguration(t *testin
{name: "host namespace", networkMode: "host"},
} {
t.Run(tc.name, func(t *testing.T) {
if runtime.GOOS == "windows" && tc.networkMode == "host" {
t.Skip("Docker host network mode is unsupported on Windows")
}
inspect := base()
inspect.Config.ExposedPorts = network.PortSet{network.MustParsePort("8080/tcp"): {}}
inspect.HostConfig.PortBindings = network.PortMap{

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.1.1"
CANONICAL_DEFAULT_PULSE_VERSION="6.1.2"
resolve_default_pulse_version() {
if [ -n "${PULSE_IMAGE_VERSION:-}" ]; then

View file

@ -269,23 +269,22 @@ func TestCurrentStablePatchReleasePacketTracksInstallMetadata(t *testing.T) {
assertFileContainsAllNormalized(t, releaseNotesPath,
"`v"+version+"` is a stable patch release",
"`v"+previous+"`",
"Manual Unified Agent updates now use the canonical operating-system family",
"Docker update actions recover their durable terminal receipt",
"Outbound usage telemetry moves to a documented schema v2",
"clusters separate when they reuse the same node names",
"not Authenticode-signed",
"`existing-mobile-build-compatible`",
"Proxmox monitoring is more authoritative",
"TrueNAS monitoring uses the supported JSON-RPC transport",
"QNAP and Unraid installs now fail early",
"Authenticode-signed",
"`no-mobile-impact`",
"rollback target for this patch release is `v"+previous+"`",
)
assertFileContainsAll(t, changelogPath,
"Version: `v"+version+"`",
"Rollback target: `v"+previous+"`",
"Promotion path: stable patch hotfix from `"+releaseBranch+"`",
"canonical runtime",
"telemetry uses schema v2",
"Durable Docker update receipt recovery",
"`v6.1.1`-only release-owner exception",
"Mobile decision: `existing-mobile-build-compatible`",
"Proxmox and PBS monitoring",
"TrueNAS uses its supported JSON-RPC transport",
"Unified Agent installs on constrained QNAP and Unraid roots",
"Authenticode is required through SignPath",
"Mobile decision: `no-mobile-impact`",
)
assertFileContainsAll(t, repoFile("docs", "RELEASE_NOTES.md"),
"docs/releases/RELEASE_NOTES_v"+version+".md",

View file

@ -288,10 +288,9 @@ func TestInstallDockerProofTracksStablePatchReleaseContract(t *testing.T) {
assertFileContainsAllNormalized(t, repoFile("docs", "release-control", "v6", "internal", "subsystems", "deployment-installability.md"),
"The active stable `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.",
"This patch release uses the stable hotfix path with `rollback_version=v"+previous+"`, `hotfix_exception=true`, a release-owner reason, and no fabricated same-version RC tag.",
"manual Unified Agent updates",
"durable Docker update actions",
"`existing-mobile-build-compatible`",
"`v"+version+"`-only unsigned-Windows exception",
"agent lifecycle, operator-state, and security fixes",
"`no-mobile-impact`",
"restores mandatory Windows Authenticode signing through SignPath",
"For the active stable `v"+version+"` cut, the repo-root compose default and `scripts/install-docker.sh` fallback must both pin `"+version+"`",
)
}

View file

@ -78,7 +78,7 @@ async function stabilizeVisualState(page: Page): Promise<void> {
[role="tab"] span[class*="rounded-full"] {
display: none !important;
}
.tabs [role="group"][aria-label="Infrastructure"] [role="tab"]:nth-child(n + 4) {
.tabs [role="group"][aria-label="Infrastructure"] {
visibility: hidden !important;
}
.tabs [role="group"][aria-label="System"] {

View file

@ -57,7 +57,8 @@ const SETTINGS_SHELL_ROUTES = [
{
route: '/settings/system-updates',
title: 'Updates',
description: 'Manage version checks, update channels, and automatic update behavior.',
description:
'Manage Pulse server runtime version checks, update channels, and automatic updates. Agent updates stay under Infrastructure.',
},
{
route: '/settings/system-recovery',

View file

@ -262,7 +262,7 @@ test.describe("TrueNAS connections in the consolidated workspace", () => {
const dialog = await openAddTrueNASDialog(page);
await dialog.getByRole("textbox", { name: "Name" }).fill("Tower NAS");
await dialog.getByRole("textbox", { name: "Name", exact: true }).fill("Tower NAS");
await dialog.getByRole("textbox", { name: "Host" }).fill("tower.local");
await dialog.getByRole("textbox", { name: "API key" }).fill("secret-api-key");
@ -343,7 +343,7 @@ test.describe("TrueNAS connections in the consolidated workspace", () => {
const dialog = await openAddTrueNASDialog(page);
await dialog.getByRole("textbox", { name: "Name" }).fill("Tower NAS");
await dialog.getByRole("textbox", { name: "Name", exact: true }).fill("Tower NAS");
await dialog.getByRole("textbox", { name: "Host" }).fill("tower.local");
await dialog.getByRole("textbox", { name: "API key" }).fill("secret-api-key");

View file

@ -111,9 +111,7 @@ test.beforeEach(async ({ page }) => {
`The authenticated shell was blank at ${page.url()}`,
).not.toBe("");
await expect(
page
.getByRole("tab", { name: /Patrol/ })
.or(page.getByRole("button", { name: "Patrol", exact: true })),
page.getByRole("tab", { name: "Patrol", exact: true }),
).toBeVisible();
});