Commit Graph

32 Commits

Author SHA1 Message Date
pulse-triage[bot] 8d8a1790ad Refuse incomplete updater manager observations
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
2026-10-14 11:58:09 +01:00
pulse-triage[bot] e1cd6843e9 Preserve native updater failures across cleanup
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
2026-10-14 09:16:19 +01:00
pulse-triage[bot] fd0754d7c3 Keep containing recovery proof faithful to current helpers
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
2026-10-14 07:22:52 +01:00
pulse-triage[bot] c1139b83ec Bound unattended installer downloads before signed execution
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
2026-10-13 22:15:02 +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
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] 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] 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
pulse-triage[bot] 9c18dcec59 Reject drift during native updater executable readback
The current native acceptance oracle can return success without a manager observation after comparing running executable bytes. Bracket the comparison with stable active process identity and compare both executables independently to the authenticated fixture, so a changing PID or equally wrong destinations cannot pass. Add required portable drift/refusal controls without changing installer authority or hosted admission.

Change-source: pulse-maintainer
2026-10-11 18:07:53 +01:00
pulse-triage[bot] e053355179 Keep updater proof controls within their declared scopes
Supply the connected manager double's fixed comparator and keep its simulated process readback explicit. Separate mandatory real-sink controls from service-state deadlines without removing cases or extending either 45-second bound.

Change-source: pulse-maintainer
Contract-Neutral: Test fixture correction only; the running-executable acceptance contract added by the parent is unchanged.
2026-10-11 13:09:02 +01:00
pulse-triage[bot] 7eb12d0bf5 Require running executable proof for unattended update success
An installed target version and active service can conceal an old managed executable. Compare the actual process bytes between complete bounded MainPID observations before accepting an update or restored active service. Retain the backup and current service on uncertainty without an automatic restart or rollback grant. Register real owned-process and conditional transaction controls; keep synthetic/native and customer acceptance distinct.

Change-source: pulse-maintainer
2026-10-11 12:53:23 +01:00
pulse-triage[bot] a8fadc30e4 Retain real unattended updater log-sink failures
Conditional Bash callers hid a failed logger write behind successful stdout. Attempt both sinks, return the first failed sink, and exercise the unchanged draining collector and recovery path with the real log primitive and confined external logger. Preserve the distinction between observed pipeline completion and native or customer recovery.

Change-source: pulse-maintainer
2026-10-11 11:22:21 +01:00
pulse-triage[bot] 236ee7b96a Read native fixture verdicts across their phase output
The exact-source installer suite exposed a result-reader defect: successful unittest methods emit phase markers before their final ok verdict. Retain complete unique method results across that output, reject missing/duplicate/mismatched identities, and keep the required Linux and portable-control verdicts unchanged.

Contract-Neutral: Test-result parsing only; no native fixture, platform admission, signing, installer, updater, recovery or customer contract delta.
Change-source: pulse-maintainer
2026-10-11 10:34:57 +01:00
pulse-triage[bot] cb1f5630f7 Repair native updater signer setup and platform-specific controls
The source-bound hosted receipt stops before any native phase because its new ephemeral signer is mode 0644. Establish owned regular-file and verified 0600 mode before signing, without adopting existing keys or relaxing OpenSSH. Keep Linux ELF execution in both native Linux jobs; macOS still runs the portable signer/admission/recovery controls, and the bridge requires the exact platform verdict instead of staging Mach-O as Linux.

Change-source: pulse-maintainer
2026-10-11 10:09:34 +01:00
pulse-triage[bot] ba2a8f9903 Check unattended updater transactions on native source CI
Exercise real systemd and signed local current-source replacement/recovery in the existing real-embed job for #2785. Preserve service masks, persistent bytes, backups and owned cleanup; source controls do not claim published or LXC recovery.

Change-source: pulse-maintainer
2026-10-10 20:50:35 +01:00
pulse-triage[bot] a04c246adf Keep updater collector failures and final installer evidence
Drain the installer pipe after a failed log write, retain its first observed failure across later successes and collect an unterminated final line. Reuse the guarded restoration path without changing signed-input, service-state, mask or prior-activity safeguards; bind the regression controls to package acceptance.

Change-source: pulse-maintainer
2026-10-10 17:15:39 +01:00
pulse-triage[bot] 74a8d0d05a Record unattended installer and log-collector exits separately
Capture both pipeline statuses before logging overwrites them. Keep recovery, signature and prior-service safeguards unchanged; cover installer, collector, simultaneous and successful observations without attributing a past host interruption.

Change-source: pulse-maintainer
2026-10-10 13:52:14 +01:00
pulse-triage[bot] 9060dbd54d Refuse unattended recovery on uncertain systemd service state
Read bounded, complete unit state before mutation and confirm inactive state before rollback replacement. Keep the verified backup and stop automatic recovery when service state cannot be reconciled; preserve prior-active restoration, masks, signatures and configuration.

Exercise failed/partial reads, deadlines, no-op stops and restoration through confined production-shell fixtures and the existing signed transaction controls. Fix fixture path and manager observations so registered Go controls execute the actual source.

Refs: #2785
Change-source: pulse-maintainer
2026-10-10 12:06:24 +01:00
pulse-triage[bot] 4e79e047f4 Bind unattended updater service discovery before installer handoff
The helper selects and passes an explicit service name, bypassing installer discovery. Apply the same bounded exact-unit admission there and preserve its error before any prior-active query, backup, installer call or restart trap. Execute both producers and the real helper handoff in the registered unattended updater proof artifact.

Refs: #2785
Change-source: pulse-maintainer
2026-10-10 11:14:58 +01:00
pulse-triage[bot] 18df4c1b46 Require executable version evidence after unattended updates
Reject successful installers whose replacement cannot identify itself as the target community runtime. VERSION metadata and failed or unrecognised probes cannot establish acceptance; retain the existing verified rollback and prior-active restoration. Exercise the complete locally signed installer transaction and bounded executable read.

Change-source: pulse-maintainer
2026-10-07 20:04:44 +01:00
pulse-triage[bot] 01b910fcda Bind unattended updates to complete stable release objects
Replace line-oriented release admission with object-bound JSON maturity checks. Refuse missing flags, mixed sibling fields, incomplete payloads and redirect-only discovery; bound both API reads without ambient curl configuration. Preserve highest-stable selection, confirmed latest fallback and all existing installation/rollback gates.

Change-source: pulse-maintainer
2026-10-07 17:42:05 +01:00
pulse-triage[bot] 2dbee5639a Stop unidentified editions entering community auto-updates
Require a successful bounded executable identity probe before release discovery. Keep Pro and unknown runtime editions untouched even when VERSION offers a usable community version, and do not defer a broken primary binary to a legacy one. Exercise the real shell classifier and main boundary with adverse controls and a TERM-resistant deadline.

Change-source: pulse-maintainer
2026-10-07 16:39:25 +01:00
pulse-triage[bot] 3e69c6706c Preserve archive metadata during verified updater recovery
Keep the original archive-copy metadata semantics while checking every snapshot and restore. A content-only copy would lose extended attributes. Exercise that recovery and reject a non-regular restored destination before byte comparison or activation; qualify the restart backstop comments for incomplete recovery.

Change-source: pulse-maintainer
2026-10-06 03:35:00 +01:00
pulse-triage[bot] 505e837a43 Use the actual OpenSSH signature output in updater transaction controls
The first connected attempt failed before installer execution because the fixture expected .sshsig while ssh-keygen creates .sig. Require that real signed output and serve it under the simulated release URL. Production signature verification is unchanged; retain the original failed proof.

Contract-Neutral: Correct the test fixture signature filename only; production updater and trust contracts are unchanged.
Change-source: pulse-maintainer
2026-10-06 03:11:58 +01:00
pulse-triage[bot] 79d6ad642a Verify restored destinations before completing updater recovery
A successful rename is not by itself the recovered-byte postcondition. Keep the snapshot and suppress activation when the recorded destination is still a link or no longer matches the saved bytes; connected no-op and changed-destination controls exercise that boundary.

Change-source: pulse-maintainer
2026-10-06 03:09:38 +01:00
pulse-triage[bot] cf92426ec1 Keep rollback replacement portable and model its stop boundary
Reject directory destinations before the same-directory file rename, use portable mode inspection in the connected transaction tests, and teach the existing service fakes that stopping and failed activation are separate operations. The recorded first candidate proof remains bound to its own source.

Contract-Neutral: Preserve the reviewed backup and rollback semantics; portable utility invocation and faithful test boundary only.
Change-source: pulse-maintainer
2026-10-06 03:07:35 +01:00
pulse-triage[bot] 329f2b00c4 Keep a verified recovery path through unattended update failures
The conditional updater caller disables errexit, but binary and VERSION backups and restore copies were best-effort and their only backup was deleted even when recovery failed. Require a private verified snapshot before installer execution, consolidate the three rollback branches, stage exact original paths and absence, and retain the backup without automatic activation when recovery is incomplete. Exercise the complete signed offline path and prior-active-only service recovery.

Change-source: pulse-maintainer
2026-10-06 03:05:00 +01:00
pulse-triage[bot] bb25e3ec03 chore(governance): record auto-update RETURN-trap contract and proof
Follow-up to 564c235e32 (fix(update): disarm perform_update RETURN trap after
it runs). That runtime change touches the deployment-installability subsystem,
so this commit adds the matching substantive contract section in
docs/release-control/v6/internal/subsystems/deployment-installability.md and
the registry-accepted deployment-script-runtime proof in
scripts/installtests/pulse_auto_update_test.go
(TestPerformUpdateDoesNotLeakReturnTrap). The pair is registered in
scripts/release_control/canonical_completion_history.json by the next commit.

Change-source: pulse-maintainer
2026-09-20 17:49:20 +01:00
courtmanr@gmail.com 806cbe83dc Fix auto-update leaving service down after installer failure
Two bugs from #1630 that combined to take installs down silently:

1. perform_update()'s install-failed rollback branch restored the backup
   but never restarted pulse.service. Since the generated
   pulse-update.service gates on ExecCondition=systemctl is-active,
   every later timer run was then skipped and the install stayed down
   until manual intervention. Restart is now guaranteed by a
   service_was_active-guarded restart in that branch plus an
   ensure_service_restarted RETURN trap so no exit path can miss it
   (re-fix of #1323, originally c0b3a0e66, lost in 778a2577b and only
   partially restored in 672e81985).

2. install.sh aborted under errexit when writing the /bin/update helper
   on a read-only filesystem - after the new binary was installed and
   the service stopped, landing in bug 1's no-restart branch. The stock
   pulse-update.service uses ProtectSystem=strict, so /bin and
   /usr/local/bin are read-only on stock unattended updates; transient
   read-only remounts hit the same path. The helper write, PATH
   appends, and the /usr/local/bin/pulse symlink are now idempotent and
   non-fatal with a warning (install_binary_symlink).

Contract: deployment-installability now pins fail-closed service
availability for unattended updates and non-fatal writes outside the
hardened unit's writable set, with proofs in pulse_auto_update_test.go
and root_install_sh_test.go plus shell regression coverage in
scripts/tests/test-pulse-auto-update.sh (installer-exits-nonzero path)
and scripts/tests/test-install-update-resilience.sh (read-only helper
and symlink paths, verified under set -e, root-safe via ENOTDIR).

Fixes #1630

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 09:38:37 +01:00
rcourtman 6351fd6526 fix(updates): select releases by highest version, not GitHub list order
Production telemetry (7d, 2026-07-17) shows a persistent tail of live
installs on 6.0.0-rc.x and 6.0.3/6.0.4 while 6.0.5 has been stable since
2026-07-09. Root cause for the reachable stranding class: release
selection trusted GitHub's created_at ordering, and this repo interleaves
v5-line maintenance releases with v6 releases (v5.1.36 was created the
day before v6.0.5). Whenever a v5.1.x release is the most recently
created stable:

- stable channel (getLatestReleaseForChannel) returned the first
  non-prerelease in list order -> "no update" for every v6 install
  until the next v6 release ships
- the RSS rate-limit fallback returned the first feed entry matching the
  channel, same failure, both channels
- the unattended updater trusted /releases/latest, which GitHub defines
  as most-recently-created stable -> timer no-ops in the same window

All three now select the highest eligible version: the API path tracks
newest stable + newest prerelease in one pass (prerelease = GitHub flag
OR tag shape), the feed fallback picks the max matching the channel, and
pulse-auto-update.sh scans /releases pairing each tag with its own
draft/prerelease flags before the fail-closed shape filter, keeping
/releases/latest as fallback. helm-chart-* tags fail semver parsing and
are dropped everywhere.

Verified not broken (pinned by new tests): rc-channel installs are
offered the newer stable (6.0.0-rc.6 -> 6.0.5) and move onto the next rc
line when one opens (-> 6.1.0-rc.2); the auto-update prerelease filter
(fail-closed is_prerelease_tag, metadata-flag refusal) still refuses
prerelease targets on stable. Go tests fail on the pre-fix code
(returned 5.1.37 / "no update").

Contract: deployment-installability now pins version-max release
selection with proofs in manager_stranded_upgrade_test.go and
pulse_auto_update_test.go.
2026-07-17 23:21:49 +01:00
rcourtman 93bccaff18 Update the Pro binary in-app from the license server download broker
The in-app updater and the unattended timer both target the public
rcourtman/Pulse community assets, so a Pro install that used them was
silently downgraded to community. Guard 2 (983a89326) blocked in-app
apply on the Pro edition, which stopped the downgrade but left Pro
installs with no update path except manual portal downloads. That
friction is a plausible driver of the runtime split: as of 2026-07-08
only 10 of 66 active paid licenses have any Pro-runtime install.

Root fix: the compiled Pro binary now checks and applies updates
through the license server download broker (GET /v1/downloads/pulse-pro
with the installation token and instance fingerprint). The check
compares against the broker's pinned private release instead of GitHub,
respecting the stable/rc channel guard. Apply re-resolves fresh signed
R2 URLs at apply time, verifies the archive against the same pinned
pulse-installer SSHSIG key plus the broker manifest sha256, and refuses
GitHub-shaped download URLs outright. An unactivated Pro binary still
refuses with the portal fallback. The community edition path is
unchanged.

The update banner restores in-app apply for auto-updatable Pro
deployments and keeps the portal instructions for deployments the
updater cannot drive (Docker). scripts/pulse-auto-update.sh now skips
when the installed binary reports Pulse Pro so the unattended timer can
never reinstall community over Pro.

Note: internal/updates/pro_update.go and manager_pro_update_test.go for
this change landed one commit early inside 313552deb via a parallel
session committing a shared staged index; this commit completes the
wiring they belong to.
2026-07-08 23:39:37 +01:00
rcourtman a2c101379a Guard stable updater from prerelease tags
Refuse prerelease-shaped tags and explicit GitHub prerelease responses in the unattended stable updater before installer invocation.

Add installability tests and proof routing for the auto-update prerelease refusal guard.
2026-04-25 23:49:27 +01:00