Commit Graph

679 Commits

Author SHA1 Message Date
rcourtman cce5ca2a7e Supply GNU ln to macOS Linux installer fixtures (#3020)
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.
2026-10-17 10:16:53 +01:00
rcourtman 5c3c7b8baa Make the Upstart teardown harness independent of umask (#3026)
`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.
2026-10-17 03:18:22 +01:00
rcourtman 0a7a9490d5 Fix action-runner setup after collector TLS trust recovery (#3010)
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.
2026-10-17 02:33:07 +01:00
rcourtman 327ef6a100 Make the Upstart teardown harness independent of umask
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.
2026-10-17 01:10:19 +01:00
rcourtman 1591af6c1e Supply GNU ln to macOS Linux installer fixtures
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
2026-10-17 00:36:19 +01:00
rcourtman 6dc43d298c Stage server update archives on the install filesystem, not /tmp
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
2026-10-16 23:49:36 +01:00
rcourtman 630bf897be fix(agent): separate runner trust from collector TLS bypass
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.
2026-10-16 23:22:42 +01:00
rcourtman 14cbaaad9c fix(release): accept cumulative prerelease changelog bases
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.
2026-10-13 23:32:10 +01:00
pulse-triage[bot] 3f63e20216 Refuse incomplete SCALE task activation targets before removal
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
2026-10-12 18:08:19 +01:00
pulse-triage[bot] 59ff0dabcf Retain SCALE agent recovery until every boot task is retired
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
2026-10-12 18:02:24 +01:00
pulse-triage[bot] c253560830 Integrate reviewed DSM Upstart removal safety
Preserve Delivery's complete exact candidate and retained source-p77gpfj5 evidence. Native DSM and whole-agent removal acceptance remain separate.

Change-source: pulse-maintainer
2026-10-12 17:14:17 +01:00
rcourtman 9c28f097b3 Warm the pulse-agent build before the pinned release API shards
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.
2026-10-12 16:28:14 +01:00
pulse-triage[bot] da597d34d6 Give DSM manual temporaries to the existing installer EXIT cleanup
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
2026-10-12 15:15:09 +01:00
pulse-triage[bot] b7b0eb0b01 Handle each DSM override failure before owned temporary cleanup
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
2026-10-12 15:00:47 +01:00
pulse-triage[bot] dc678bfd9d Preserve refused link exits through the confined DSM command double
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
2026-10-12 14:52:56 +01:00
pulse-triage[bot] f472758a37 Observe previous DSM link arguments before judging the restore race
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
2026-10-12 14:23:37 +01:00
pulse-triage[bot] a61f14c823 Compose current prerelease metadata repair with reviewed maintenance
Change-source: pulse-maintainer
2026-10-12 14:22:47 +01:00
pulse-triage[bot] e24bfa79af Exercise concurrent DSM targets inside the actual restore operation
Extend the existing no-clobber method with file and directory creation at restoration, not only before its initial path check. The real private-file operations must retain the original staged inode and every credential, never overwrite or write inside a concurrent target. Product code, method count, deadlines and native obligations are unchanged.

Contract-Neutral: Closed-model race coverage only; no runtime or contract delta.
Change-source: pulse-maintainer
2026-10-12 14:14:17 +01:00
pulse-triage[bot] a7271f5840 Keep DSM config restoration atomically non-clobbering
A pathname absence check followed by mv could overwrite a concurrent root-created job. Use exact-target non-replacement links for override creation and config restoration; directory races also refuse without creating nested artifacts. Confined concurrent-file/directory controls are mandatory. Partial activation is never restarted and conflicting staged recovery is never discarded.

Change-source: pulse-maintainer
2026-10-12 14:11:23 +01:00
pulse-triage[bot] adcc223f55 Bind DSM controls to the actual missing tool and uninstall section
The first exact proof retained two fixture failures: timeout executes an external initctl rather than a shell function, and the source-order assertion unintentionally included later install code. Remove the actual model binary for its missing-tool control and bound the assertion to uninstall. Add an explicit concurrent-config/no-clobber recovery verdict; product code and safety deadlines are unchanged.

Change-source: pulse-maintainer
2026-10-12 13:59:31 +01:00
pulse-triage[bot] a6527d9ae3 Confirm DSM Upstart retirement before erasing agent recovery
Treat DSM6 Upstart and stale DSM7 jobs through their actual manager, not SysV or late best-effort cleanup. Keep trusted job state and credentials on refused, unknown or partial observations; require effective manual activation and observed stopped/absent processes before remote removal and local erasure. Stage the original config until absence is confirmed, restore without restart on failure, and preserve non-clobbering recovery. Mandatory production-function/caller controls retain every refusal and timeout boundary. This is confined source repair, not native Linux/Windows or whole-issue acceptance.

Change-source: pulse-maintainer
2026-10-12 13:55:32 +01:00
rcourtman 7673e8bc10 Teach the pre-release metadata test about a second beta
The second beta's preparation pull request failed Backend tests
(TestCurrentPrereleasePacketTracksInstallMetadata): the changelog the
release metadata tool wrote said it describes the changes since
v6.6.0-beta.1, and the test expected v6.5.0. previousPrereleaseVersion
only understood -rc.N, so for beta.2 it reported no predecessor and the
test fell back to the previous stable. The tool names the predecessor in
the pre-release's own stage, alpha, beta or rc, N-1.

Do the same in the helper: rc.2 follows rc.1 and beta.2 follows beta.1,
while the first of a stage has no predecessor even when an earlier stage
shipped. Pin it with a table test, because beta.1 passed without ever
exercising the second-beta case.

Run against the beta.2 preparation snapshot with this change, the whole
installer test package and the release policy suite pass.

Contract-Neutral: Test helper only; no release, install or customer contract changes.
2026-10-12 13:48:03 +01:00
pulse-triage[bot] 7bda196005 Integrate reviewed recovery-preserving rc.d and SysV removal
Change-source: pulse-maintainer
2026-10-12 13:36:44 +01:00
rcourtman d897fc52ce Retry the release listing so a GitHub 504 cannot fail a release
The first beta's release workflow failed in create_release at "Locate
existing release". That step reads up to 100 releases with every asset,
about 25 MB, in one call, and GitHub answered it with an HTTP 504. The
step has no pipefail, so the failed read reached jq as empty input and
the job ended with exit code 5. Timing the call from a workstation, one
of three tries returned an error body, so this is not rare.

Retry the read up to five times with the same backoff the asset upload
uses, and accept only a complete listing of valid pages. A failed or
truncated read must never look like "no existing release": that answer
would stage a duplicate draft for a tag that already has one. Exhausting
the attempts fails the job with an explicit error. The listing call and
the downstream handling of an existing draft are unchanged.

Run the real step script in the release policy test against doubles for
gh and sleep: two 504s then a complete listing finds the draft after
backoff of 15s and 30s; a read that exits 0 but is truncated every time
fails after five attempts, with the capped backoff and no outputs; and a
complete listing without the tag still means no existing release. Against
the previous step the test fails with the production symptom, exit 5 and
the 504 message. The create-release pin list and the contract record the
retry.

Contract-Neutral: Release workflow reliability only; no published artifact, tag, signing, install or customer contract changes.
2026-10-12 11:27:09 +01:00
pulse-triage[bot] 65f813c497 Keep bounded legacy fixture observations atomic
The retained validation ended with an empty model record, not a product erasure verdict. Use atomic replacement for closed model updates so command interruption cannot truncate the state used to judge actual recovery files; production deadlines and safety assertions are unchanged.

Contract-Neutral: Closed fixture observation writes only; no product runtime or contract change.
Change-source: pulse-maintainer
2026-10-12 10:24:58 +01:00
pulse-triage[bot] ac4c4cebe4 Preserve first service-inventory evidence with upstream fresh-host repair
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
2026-10-12 10:15:52 +01:00
pulse-triage[bot] e1b3a352c5 Preserve truthful partial-stop recovery in legacy controls
An owned activation link may already be disabled before a later observation fails; assert exact service/PID/credential retention without re-enabling it. Require the old installed-script PID-erasure adverse control, keeping that runtime observation distinct from credential erasure and new renderer behaviour.

Contract-Neutral: Verification and precise contract documentation only; the production installer is unchanged.
Change-source: pulse-maintainer
2026-10-12 10:15:03 +01:00
pulse-triage[bot] 6d654f3984 Bind complete rc.d renderer in removal controls
The renderer contains nested functions inside its heredoc; extracting at the first closing brace never exercises its stop function. Bind its full top-level body and inject missing sysrc in the executable model reached by timeout.

Contract-Neutral: Confined test extraction and missing-tool modelling only; no installer or contract behaviour changes.
Change-source: pulse-maintainer
2026-10-12 10:09:43 +01:00
pulse-triage[bot] c3911ea59f Bound legacy removal PID records and required-tool refusal
Retain recovery before unknown or unavailable timeout/sysrc observations, require bounded PID records, and align the existing daemon-supervisor assertion with checked signal delivery rather than ignored errors. Every source control remains mandatory.

Change-source: pulse-maintainer
2026-10-12 10:04:52 +01:00
pulse-triage[bot] bad4845508 Retain rc.d and SysV recovery until removal is observed
Continue #1099: stop and disable the actual legacy manager before remote deregistration or local erasure, recheck after confirmation, and retain PID/service/credential material on refusal or unknown state. Check collector and supervisor absence, exact runlevel ownership and rendered signal failures with confined production-function controls. Appliance bootstrap and native acceptance remain separate.

Change-source: pulse-maintainer
2026-10-12 10:00:09 +01:00
rcourtman 3e3ad9a5bd Keep the real-systemctl controls skipped where systemctl is absent
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
2026-10-12 09:40:57 +01:00
rcourtman d8806e3c20 Treat no matching unit files as an empty service inventory
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
2026-10-12 09:26:34 +01:00
pulse-triage[bot] ce1a28f64d Keep integration safety fixtures aligned with reviewed source
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
2026-10-12 08:51:49 +01:00
pulse-triage[bot] 174f440e2d Retain OpenRC agent recovery until removal is confirmed
Require bounded diagnostic-free service and all-runlevel readback before server removal or local mutation, and reconcile again after remote confirmation while recovery state still exists. Refused stops, disable failures, unknown reads and unexpected init paths retain local material; absent and repeated removal need independent manager/process absence. Add executable confined refusal, ordering, custom-runlevel and timeout-child controls to the registered installer hook and owning contracts. Native OpenRC, Windows, other-manager and installed acceptance remain separate.

Change-source: pulse-maintainer
2026-10-12 06:58:15 +01:00
pulse-triage[bot] 27f9c1de5e Check agent removal against native hosted systemd
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
2026-10-12 06:08:07 +01:00
pulse-triage[bot] 25c84cc5e4 Keep runner revocation controls behind the observed stop boundary
Load the production quiescence helpers in the existing failed-revocation fixture and provide complete closed systemd observations. Continue testing the actual revocation refusal after verified stop/disable rather than replacing or bypassing the new safety boundary.

Change-source: pulse-maintainer
2026-10-12 05:20:06 +01:00
pulse-triage[bot] 4fce9ba024 Retain agent recovery files until systemd teardown is confirmed
Replace ignored stop and disable failures with bounded complete state observations and independent inactive/non-enabled readback. Reconcile helper socket and service together, and quiesce the full uninstall activation set before remote or local mutation. Preserve failed runner revocation material and existing saved-state, token, trust and platform boundaries. Add executable production-shell refusal, reactivation, timeout and owned-cleanup controls.

Change-source: pulse-maintainer
2026-10-12 05:17:04 +01:00
pulse-triage[bot] 8147f39292 Retain caller token files before installer lifecycle confirmation
Non-interactive token-file reading currently erases the supplied credential before permission checks and authenticated removal, including when removal is refused or its result is unknown. Treat token-file ingress as read-only: callers own their bootstrap cleanup, and confirmed removal still deletes only authorised runtime state. Keep token validation, TLS policy, exact record confirmation and credential-free local removal unchanged.

Extend the existing portable removal fixture through the actual parser and token ingress, root refusal and post-confirmation owned cleanup. Parent proof records thirty premature-erasure failures; a separate no-TTY harness control remains distinct. Update both owning contracts and the registered Go inventory hook together. Native Windows, compiled TLS and installed whole-job acceptance remain separate.

Change-source: pulse-maintainer
2026-10-12 03:55:06 +01:00
pulse-triage[bot] b3b368c21b Admit silent unit absence without losing inventory failures
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
2026-10-12 03:15:49 +01:00
pulse-triage[bot] 03b8808c04 Retain Unix agent credentials when the saved server URL is missing
Credential-bearing removal currently short-circuits its confirmation gate when connection recovery leaves no URL, allowing local teardown without durably removing the server record. Check credential presence first and stop with same-instance recovery guidance before any process, service or state teardown. Keep credential-free local-only removal and existing exact-record/path authority intact.

Execute the real admission and initial teardown with private transport/service doubles, including every saved credential form, refusal, unknown response, identity mismatch and authorised cleanup. Register the portable Python controls in the installer inventory and describe the boundary in both owning contracts. Native Windows/TLS and whole installed lifecycle acceptance remain separate.

Change-source: pulse-maintainer
2026-10-12 01:56:17 +01:00
pulse-triage[bot] eead1dce67 Preserve the current upstream Go security toolchain and dependency repair
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
2026-10-11 23:42:21 +01:00
rcourtman 592a1e52f6 Advance the Go toolchain to 1.26.9 and x/net to v0.60.0
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.
2026-10-11 22:37:46 +01:00
pulse-triage[bot] 24bd5689f6 Integrate reviewed startup readback and native compilation controls
Preserve Delivery tip 00680c9c5a exactly in merge ancestry. Resolve the overlapping startup helper to the candidate implementation and retain the canonical diagnostic controls and existing documentation. Frontend test formatting is already present; no runtime UI or workflow changes.

Change-source: pulse-maintainer
2026-10-11 21:58:36 +01:00
pulse-triage[bot] 96afcaf4c6 Incorporate the batch-start upstream beta metadata
Change-source: pulse-maintainer
2026-10-11 21:50:42 +01:00
pulse-triage[bot] a6437f885f Keep native maintenance checks compiling and command facts clean
Completed PR2825 CI reports Windows Unix-type compilation failures and three Prettier fixture failures. Compile the existing Linux-only MSP daemon fixture only on Linux and format the unchanged frontend assertions. Separate successful CLI stdout from retained stderr diagnostics so Compose warnings cannot corrupt container identities, ports or bootstrap facts; add three executable controls without changing deadlines or acceptance gates.

Change-source: pulse-maintainer
2026-10-11 21:09:21 +01:00
rcourtman b1a8b4f866 Let the release metadata tests accept beta versions
The release train now cuts a beta every two days before the release
candidate. Its first beta, v6.6.0-beta.1, failed its preparation checks
twice: the governance test that maps VERSION to its release packet only
matched -rc.N, and two tests required the release index to say "current
v6 release candidate packet" while the metadata tool writes "current v6
beta packet" for a beta.

Accept -beta.N where the packet paths are derived and derive the expected
index wording from the version. Release candidates and stable releases
are checked exactly as before.

Contract-Neutral: Release metadata tests only; no runtime, API, install or customer contract change.
2026-10-11 21:08:44 +01:00
pulse-triage[bot] 00680c9c5a Restore native installer compilation and frontend style gates
The refreshed proposal compiles the Linux-only control-plane startup fixture during Windows installer tests, failing on Unix-only dependencies before lifecycle assertions run. Exclude the fixture and its controls at compile time outside Linux, check Go file selection, and preserve required native Windows/macOS execution. Format only the three test files rejected by the exact containing CI, with Prettier AST-equivalence checks; no assertion, timeout, production source or workflow gate changes.

Change-source: pulse-maintainer
2026-10-11 20:51:38 +01:00
pulse-triage[bot] 347c39bd11 Keep Compose warnings out of MSP startup readback
The exact-head hosted startup check reaches Docker exec with readback taken from CombinedOutput. Separate stdout from successful stderr warnings for identities, endpoints, mount hashes, bootstrap facts and cleanup, while retaining warnings and both failed-command streams. Add real-child-process controls and the matching deployment acceptance contract; hosted Compose acceptance remains required.

Change-source: pulse-maintainer
2026-10-11 20:43:51 +01:00
pulse-triage[bot] f986912ed2 Admit server upgrade changes to the existing native checks
The native Unix inventory tests the root installer, unattended updater and their Python service/readback fixtures, but the workflow's event filters omit those inputs. Include each on push and pull_request, preserving all jobs, tools and deadlines. Decode both events in regression controls and reject removal or exclusion of each input.

Contract-Neutral: Native CI admission coverage only; installer/updater behaviour, agent and Windows lifecycle, service authority and recovery contracts are unchanged.
Change-source: pulse-maintainer
2026-10-11 19:28:37 +01:00
pulse-triage[bot] 5ef1729292 Keep real updater sink checks inside native deadlines
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.
2026-10-11 18:36:24 +01:00