Go 1.26.10 (20 October) distrusts the compromised eMudhra intermediate
CAs in crypto/x509 (CVE-2026-111943). Binaries built with 1.26.9 still
accept chains through them, so every verified outbound TLS connection
Pulse makes (licence, updates, webhooks, AI providers) stays open to
interception by a holder of those keys. govulncheck does not flag a
trust-store change as a called symbol, so the scheduled scan stayed
green while the gap existed.
Advance the governed toolchain floor and every pin together: go.mod,
the toolchain installer and release-script guards, the source-built
container stages (manifest-list digests for golang:1.26.10-alpine and
the Debian dev container image), the root-pair diagnostic, and their
tests.
Vite 6.4.3 bundles a launch-editor with three advisories
(GHSA-587h-7wmj-h5pm, GHSA-r825-w8mm-5pxw, GHSA-698v-4r3x-3vcv) that
failed the daily advisory watch. 6.4.4 bundles the fixed launch-editor
and 6.4.5 adds the dev-server server.fs query check. Both reach only
the local dev server, not the built product. The lockfile changes only
the vite entry, and dependencySecurity.test.ts now pins the floor.
Script smoke still asserted the removed grouped runner while native
verification used a separate suite. Share the canonical execution controls
so both entrypoints reject lost checks, false passes and changed deadlines.
Compile and discover one exact test binary per native workflow phase, then
execute every discovered root check independently. This removes inventory
growth from another check's ten-minute Go alarm while preserving fixture,
installer and overall CI job deadlines.
Retain exhaustive phase coverage and reject missing completion, failed checks,
unexpected exit zero and timeouts using real compiled Go execution controls.
Register the runner with deployment installability and retain the failed Intel
and advisory benchmark evidence in the existing reliability record.
The NAS suite exhausts its aggregate alarm after fifteen successful
controls on macOS Intel. Run each discovered NAS and updater control
under its existing bound, retaining the native phase's ten-minute alarm
and all installer operation deadlines.
Keep required controls mandatory and reject skipped, absent or failed
execution. Exercise discovery and false-pass rejection with real Python
controls, including a newly added method.
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.
Require the existing troubleshooting jobs and their destinations without
making additional help entries break unrelated safety checks. Keep the
opening backup warning scoped to its actual entry point.
Run closed service and middleware models through the containing fixture's
selected Python interpreter without site startup. Retain every safety
scenario and deadline while qualifying the pending maintenance source.
Refs #2888
Contract-Neutral: Test-only navigation assertions and confined model interpreter selection. Production behavior and safety deadlines are unchanged.
The non-required **Unified Agent Native Verification** workflow has
failed on `main` (and most PRs) in both macOS jobs since 12 Oct (first
failing main run 38285211868 on c3524d373a; last green 38273335159 on
552e838749). The single cause is
`TestInstallSHUpstartTeardownRequiresObservedQuiescence`: 13 Python
subtests in `agent_upstart_teardown.py` fail with `ln: illegal option --
T`.
`scripts/install.sh` uses `ln -T -- <pending> <override>` and `ln -T --
<retired> <conf>` in the DSM Upstart teardown (added in the DSM
retirement series, a7271f5840 / b7b0eb0b01). That path only runs on
Synology DSM (Linux), so the installer is correct. The harness runs
those functions with the host `ln`, which on macOS runners is BSD `ln`
without `-T`.
The workflow already supplies GNU `stat/chmod/chown/mv/tar` (plus Bash
5) for Linux-only installer fixtures on macOS. This adds `ln` to that
set and extends `TestNativeInstallerFixtureToolsAreRealAndStepLocal` to
require it (missing/non-GNU scenarios, PATH binding, and real `ln -T`
no-replacement checks against file and directory targets).
Verification:
- Reproduced locally on macOS: the harness fails 13/15 with BSD `ln` and
passes 15/15 when `ln -T` is available.
- `TestNativeInstallerFixtureToolsAreRealAndStepLocal`: fails against
the old workflow (real tools, missing ln, non-GNU ln) and passes with
this change (Linux).
- The macOS jobs themselves are only exercised by this PR's CI run.
`scripts/installtests/agent_upstart_teardown.py` created its fixture job
directory with a bare `mkdir`. Under a caller umask of 002 (the default
on Ubuntu hosts with user-private groups, pulse-dev included) that
directory came out 775. The installer's `trusted_lifecycle_regular_file`
check then correctly refused the fixture config, and running the harness
directly failed 23 subtests. The Go driver
`TestInstallSHUpstartTeardownRequiresObservedQuiescence` still passed,
because the installtests package already pins umask 022 for its children
(`umask_unix_test.go`). So the bug only shows up when the harness runs
on its own.
The fix gives the job directory an explicit 0755 mode. The sibling
TrueNAS and uninstall-state harnesses already create their trust-checked
directories with explicit modes. The production functions under test
still run with the caller's umask. They `chmod 644` what they write, so
nothing in production depends on umask.
Checked on pulse-dev (python3 3.12, go 1.26.5):
| run | umask 0002 | umask 0022 | umask 0077 |
| --- | --- | --- | --- |
| harness before | FAILED (failures=23, errors=1) | OK (15) | OK (15) |
| harness after | OK (15) | OK (15) | OK (15) |
| Go driver after | ok | ok | — |
The other Python harnesses in `scripts/installtests/` (legacy, OpenRC,
systemd, TrueNAS, uninstall-state) already pass under umask 0002, so
they are unchanged.
Test-only change.
An existing collector can retain its legacy TLS bypass after the host
trusts the Pulse certificate. Updating it with action-runner enrollment
then incorrectly rejects normal system-root trust and enters recovery
before creating the runner service.
Keep collector/download settings separate from verified runner TLS.
HTTPS runner configuration continues to use system roots, a configured
CA or an exact certificate pin and never inherits the collector bypass.
Activation, cancellation, revocation and service recovery retain their
existing boundaries.
Validation: the new executable regression fails against the previous
installer and passes with the fix. It covers recovered insecure state,
private configuration persistence and the real runner credential
lifecycle binary over TLS. System roots, custom CA and exact pin
succeed. Untrusted certificates and incorrect pins receive no
credential-bearing request. The installer Action Runner suite, runner
binary tests and hostagent Action Runner suite pass.
Agent Doctor enrollment also passes in the current built app at 1440,
820 and 390 pixels wide, covering details disclosure, issue
failure/pending/success, private command copying, one-time reveal,
clearing and reload. Browser endpoints use synthetic fixtures. No native
installed-host or published-release acceptance is claimed.
The additional native workflow passes on Linux x64 and ARM64. Its macOS
Intel and ARM64 jobs fail the unchanged Upstart retirement contract
because the host `ln` rejects `-T`. The same failure occurs in [the
earlier main
run](https://github.com/rcourtman/Pulse/actions/runs/38899584939). This
change does not touch that fixture or the exercised retirement
functions.
agent_upstart_teardown.py created its fixture job directory with a bare
mkdir, so under a caller umask of 002 (the default on pulse-dev and other
Ubuntu user-private-group hosts) the directory came out 775. The
installer's trusted_lifecycle_regular_file check correctly refuses a
config whose parent is group-writable, and 23 subtests failed when the
harness ran directly. The Go driver only passed because the
installtests package pins umask 022 for its children.
Give the job directory an explicit 0755 mode, matching the sibling
TrueNAS and uninstall-state harnesses, which already create their
trust-checked directories with explicit modes. The production code
under test still runs with the caller's umask; it sets 644 on the files
it writes, so the harness now passes under 002, 022 and 077.
The DSM Upstart removal path links its manual override and restores a
retired job config with GNU `ln -T --`, a hard link that never replaces
or descends into an existing target. Its Python harness executes those
production functions with the host's ln. On the macOS runners that is
BSD ln, which rejects -T ("illegal option -- T"), so 13 subtests of
TestInstallSHUpstartTeardownRequiresObservedQuiescence have failed on
both macOS jobs since the DSM retirement series landed on 12 Oct.
The production code is right: that path only runs on Synology DSM
(Linux), never on a macOS agent host. The gap is the macOS fixture
toolset, which already supplies GNU stat/chmod/chown/mv/tar for these
Linux-only installer contracts but not ln. Add ln to that step and make
the fixture-tool contract test bind it and exercise real `ln -T`
no-replacement semantics (file and directory targets).
Contract-Neutral: CI fixture tooling only; macOS runners gain the GNU ln the existing DSM Upstart harness already assumes, no installer or lifecycle contract change
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
Legacy updates recover the collector's insecure setting even after the host
trusts the server certificate. Do not reject verified runner system trust
because of that unrelated setting. Keep HTTPS runner verification enabled
and preserve custom CA and exact fingerprint trust.
Exercise recovered configuration and the real credential lifecycle client
against TLS, including untrusted certificates and mismatched pins that must
receive no credential-bearing request.
Require a kernel absence observation for the recorded timeout child instead of treating missing /proc or missing PID evidence as cleanup. Exercise live, reaped, missing and refused observations. Bind repeated stdlib-only process/flash/signal models to the runner's selected isolated Python, preserving every existing adverse control. Record cumulative and per-control timing so the unchanged two-minute suite limit cannot be mistaken for a particular hung case.
Contract-Neutral: Native NAS test harness only; production installer behaviour, process ownership, safety deadlines and public contracts are unchanged.
Change-source: pulse-maintainer
Exact PR2888 jobs 115988808038 and 115991110679 exposed obsolete documentation/workflow markers and formatting in composed tests. Assert the shipped socket-free discovery, confined command boundary and incomplete-inventory limits; verify authenticated immutable release-packet admission instead of an expired live-run requirement. Preserve production source, native deadlines and all accepted history.
Contract-Neutral: Test-only integration and formatting correction; no runtime, documentation, dependencies, qualification deadline or permission changes.
Change-source: pulse-maintainer
Own PR2936's stale pin and download-admission failures at b7e774d4. Refresh all current workflow consumers, including the newer pre-token issue-label job, with their exact static expectations and lifecycle/deployment contracts. Preserve Node 24, scoped lockfile caching, credential isolation and artifact identity/retention; reject old, mutable and arbitrary download pins.
Contract-Neutral: Immutable Actions pin refresh and coupled consumer/trust expectations only; workflow inputs, permissions, cache and credential controls, artifact identities and application/runtime contracts are unchanged.
Change-source: pulse-maintainer
Keep stderr and trailing blank records in service-state and running-PID admission. Incomplete reads must not authorise installation, restart, rollback or update success. Require production caller and real-process controls for both read boundaries; preserve existing recovery, trust and deadlines.
Change-source: pulse-maintainer
Run the locked parser and silent triage policy on their complete input changes in a credential-free Build and Test job. Retain issue-event pre-token checks, existing runtime and strict dependency gates; exercise admission and original install/test failure exits.
Change-source: pulse-maintainer
Keep the original failed transaction command distinct from any later cleanup command. Retain fixed operation labels and require portable controls for command, timeout, assertion, cleanup-only and successful receipts, without changing update transactions or disclosing command output.
Change-source: pulse-maintainer
Require the renamed native process-field assertion in the complete NAS removal oracle; retain all twenty controls and their strict executed verdicts.
Contract-Neutral: Update only the required test identity after the fixture assertion rename; no production or acceptance-contract change.
Change-source: pulse-maintainer
Current exact-head CI exposes outdated PBS proof inventory, omitted bounded-download helper extraction, closed transport rejecting the production HTTPS flags, and Darwin's truncated comm column in Linux NAS fixtures. Preserve exact transport and process ownership, restoration and migration safety assertions while binding the controls to the actual producer.
Contract-Neutral: Test and offline CI-fixture alignment only; no installed runtime, trust, credentials, deadlines, source safety or public contract changes.
Change-source: pulse-maintainer
The accepted beta.2 packet uses the earlier explicit cumulative introduction, while the new upstream general helper recognises the metadata tools standard introduction. Keep that historical packet on its strict existing stable-base introduction and heading checks; later numbered packets use the new incremental-or-cumulative helper. All remaining packet/signing/mobile checks remain shared and unchanged.
Contract-Neutral: Integration test branch reconciliation only; release source, notes, workflow behavior and publication rules are unchanged.
Change-source: pulse-maintainer
Merge exact upstream 1844f6c0ed after completed bounded refresh 4ced96dc6a4e6278a4bba6fb79fb21cf68d954d95d49d67c27d425621a7d9f3f. Preserve every reviewed commit and the specific beta.2 cumulative-list assertions while using the new general incremental/cumulative-base validation for other packets. Only installer release-packet tests change; final frontend bytes remain identical.
Change-source: pulse-maintainer
The release train explicitly compares the full beta range with the previous stable. Accept that supported base as well as the default previous numbered prerelease, while rejecting missing, ambiguous and unrelated comparison bases.
Contract-Neutral: Align release packet checks with the existing metadata contract. No public install or runtime contract delta.
Give each installer and signature transfer a five-second connection and sixty-second total budget, ignore ambient curl settings and preserve HTTPS through redirects. Failed or partial downloads leave the prior install, service and identity unchanged; pinned signature admission and recovery deadlines are unchanged.
Change-source: pulse-maintainer
The private NAS fixture copied /bin/sleep and relied on its renamed process identity. Build a bounded Go collector instead and require its fixed readiness marker through the generated wrapper's retained stderr, as well as exact kernel process ownership. Preserve actual wrappers, teardown/parser, all stop and cleanup assertions, and existing deadlines. This removes a platform-owned executable assumption; it does not diagnose or clear the preceding macOS failures or establish native appliance recovery.
Contract-Neutral: Private fixture executable and readiness proof only; production installer, process ownership, safety deadlines and wire contracts are unchanged.
Change-source: pulse-maintainer
Move the existing generated watchdog and collector contract to an early, fail-closed native CI phase. Discover the complete inventory in both phases, execute every check exactly once, and preserve the remaining group assignments, native runtime tools and safety deadlines. Exercise phase admission, omission, duplicate and failure controls.
Contract-Neutral: Native CI phase ordering and inventory partition only; production installers, runtime behaviour, required checks and safety deadlines are unchanged.
Change-source: pulse-maintainer
Expect the complete reviewed PBS proof list. Collect full-width real process rows in the private NAS watchdog fixture, retaining exact ownership, deadlines and failure evidence instead of truncating long Darwin paths.
Contract-Neutral: Test fixtures only; production installers, process ownership checks, retention, registry policy and runtime behaviour are unchanged.
Change-source: pulse-maintainer
Both required CI and the local gate must treat the new subprocess owner as audit runtime, not an unchanged dependency graph. Carry the actual helper into the isolated local-gate fixture and test positive findings, missing results, large change lists and exact-path lookalikes through both consumers.
Contract-Neutral: Extend the existing strict audit-runtime path classification to its new owned helper; advisory rules, dependency graphs, public runtime, credentials and external permissions are unchanged.
Change-source: pulse-maintainer
Final whole-package proof retained a SCALE caller failure and showed its missing nas_agent_removal_platform definition. Extract the real platform reader through the existing redirected vendor paths and require an ordinary-Linux negative scope read without command-not-found noise. Preserve all sixteen task/process/manager controls and production refusal deadlines; this closure repair does not diagnose the observed service-quiescence failure.
Contract-Neutral: Complete test fixture namespace and negative-scope assertion only; no production installer, service, removal, credential or timeout delta.
Change-source: pulse-maintainer
The full package proof ran all nineteen NAS methods successfully but its Go caller still required the old eighteen-test count. Require the new named control as well as the nineteen-test OK verdict; keep every original case and fail-closed wrapper. The original failed full run remains recorded and the corrected containing source is revalidated.
Contract-Neutral: Test verdict accounting only; no installer, removal, credential, timeout or native acceptance contract change.
Change-source: pulse-maintainer
The new macOS native jobs fail NAS boot-filter positive controls and never recognise the generated private collector. Supply binary-aware GNU awk only to the existing Linux server fixtures and retain real safe/NUL/control-byte probes plus missing/non-GNU-tool refusals. Recognise both Unix comm formats while keeping the exact owned executable argument and cleanup controls. No installer or service source, skipped test or native-appliance acceptance changes.
Contract-Neutral: CI prerequisites and test fixtures only; production install, removal, credential retention and process-owner safety contracts are unchanged.
Change-source: pulse-maintainer
The complete installer suite exposed a stale assertion requiring a beta.1 comparison after the reviewed changelog correctly named its actual v6.5.0 base. Keep the existing packet, signing, installer, rollback and compatibility checks, and require both the truthful stable-based introduction and matching changelog heading.
Contract-Neutral: Test-only correction for the unchanged, already-recorded beta.2 changelog range; no release or runtime contract changes.
Change-source: pulse-maintainer
Bind removal guidance to checked NAS quiescence and boot retirement instead of retired installation markers. Model DSM hard-link semantics through real exclusive link syscalls so macOS contract jobs no longer depend on GNU ln, retaining raced-directory adverse controls and every existing manager/recovery assertion.
Contract-Neutral: Tests only; production removal, documentation and teardown contracts are unchanged.
Change-source: pulse-maintainer
Rebind its old next-platform sentinel and include the production NAS selector. The fixture otherwise reaches unrelated runtime cleanup and correctly refuses a non-fixture path. Keep that filesystem guard and every existing DSM safety assertion unchanged.
Change-source: pulse-maintainer
Require the actual NAS quiescence/retirement caller rather than the removed best-effort autorun cleanup. Preserve Upstart exclusion from SysV while requiring the NAS exclusion too. No manager or process safety control is removed.
Change-source: pulse-maintainer
The closed signal and server doubles record the current boot/process state without changing outcomes. Require exact boot absence before both signals and complete quiescence at the server boundary, so a future ordering regression cannot pass on final state alone.
Change-source: pulse-maintainer
A go shell can hold later launches in memory after its boot file changes. Treat that live mixed-purpose owner as unknown rather than signalling it or inferring quiescence from the edited file. Require the actual-caller refusal control alongside the existing NAS suite.
Change-source: pulse-maintainer
The renderer heredoc contains nested shell functions. Extract through its own EOF and outer closing brace so the real private process control executes the complete producer; retain renderer diagnostics on failure.
Change-source: pulse-maintainer
Retire only exact Unraid and QNAP boot entries before checked watchdog-first process termination and same-instance server removal. Preserve unrelated boot owners and refuse unknown, partial or concurrent state without erasing credentials or restarting. Exercise the complete uninstall caller and generated private watchdogs.
Change-source: pulse-maintainer
A missing SCRIPT path or empty COMMAND target cannot establish that a task is unrelated or absent. Retain recovery before any task or service mutation when those fields are unavailable. Add three actual-caller adverse controls to the mandatory sixteen-method hook and both owning contracts.
Change-source: pulse-maintainer
Disable and independently verify every exact owned middleware activation task before systemd stop and server removal. Close task, unit and process state after the full stop and after task deletion, retaining recovery on refusals or concurrent changes without restarting. Ordinary Linux state directories are not appliance identity. Register all fifteen actual-function/full-caller controls and update both owning contracts in this coherent candidate.
Change-source: pulse-maintainer
The first beta's release workflow failed twice in backend_tests:
TestHostAgentCredentialFileHandoff, added on 2026-10-11 and so new to
every release job, compiles pulse-agent itself and was killed at exactly
180s with "build agent from this source: signal: killed".
The release runner starts each API shard with a small pinned GOMAXPROCS,
and the test's child compiler inherits it. A cold agent build is about
250 CPU-seconds, so under the pin it serializes past the test's fixed
three-minute limit. The release job also has no Go build cache, and the
race-enabled test binary the runner compiles does not warm this plain
build. Pull request jobs keep a warm cache, which is why the test passed
there.
Reproduced on pulse-dev with the shard's conditions, a prebuilt test
binary, GOMAXPROCS=1 and an empty cache: the test fails at 180.02s with
the identical message. With the agent built once beforehand, unpinned, in
99s, the same pinned run passes in 16s.
Build the agent once in the runner, after compiling the test binary and
before any shard starts, with the full CPU count. The runner test pins
that order so the warm-up cannot drift after the shards, and the contract
records why. Against the previous runner the pin fails with the expected
message. The release preflight and policy suites pass.
Contract-Neutral: Release test runner ordering only; no published artifact, tag, signing, install or customer contract changes.
Inline and command-double changes did not establish cleanup on the retained race refusal. Register only the non-credential manual temporary with the existing installer trap and execute that exact trap in the confined caller controls. Never register the credential-bearing staged job backup for automatic deletion. All retention, no-clobber, refusal and native obligations remain intact.
Change-source: pulse-maintainer
The retained race refusals still left manual temporary files after the command-double change. Check write, permission and atomic link operations individually rather than relying on the grouped chain to reach cleanup. Keep concurrent files/directories and all credentials intact; the same mandatory race assertions remain. Matching Go1.26.9 is now actually observed and the complete installer suite remains required.
Change-source: pulse-maintainer
The two race controls retained private manual temporary files because the command double exited before the production failure handler. Exact confined traces show that exit boundary. Capture and return the real external ln exit explicitly, preserving production cleanup and every file/directory race assertion. No product code, safety assertion, method count or deadline changes.
Contract-Neutral: Faithful closed-model external exit observation only; no runtime or contract delta.
Change-source: pulse-maintainer
The retained fourth proof stopped at a test-double argument refusal before the intended prior-source race. Execute both legitimate ln argv forms unchanged inside the private fixture; current file/directory preservation still requires exact-target non-clobbering semantics. No product change, safety assertion, method count or deadline is weakened.
Contract-Neutral: Transparent closed-model command arguments only; no runtime or contract delta.
Change-source: pulse-maintainer