Commit Graph

110 Commits

Author SHA1 Message Date
pulse-triage[bot] d5945f824d Preserve Pulse severity in systemd journal 2026-08-31 11:46:51 +01:00
rcourtman 66eb537522 Fix local subscription CLI service setup 2026-08-23 09:28:44 +01:00
rcourtman e434466269 fix(security): verify SSH hosts during proxy cleanup 2026-08-08 05:18:55 +01:00
rcourtman 5787cea4eb fix(install): use absolute binary path in pct exec instructions
pct exec runs with PATH=/sbin:/bin:/usr/sbin:/usr/bin, which does not
include /usr/local/bin where the pulse binary is linked. Every first-run
instruction that invoked a bare `pulse` through pct exec therefore failed
with exit 127, including the command the installer itself prints at the
end of an LXC install.

Reproduced on Proxmox VE 9.1.9 and 8.3.0:

    pct exec <ctid> -- pulse --version
      -> exit 127, Failed to exec "pulse"
    pct exec <ctid> -- /usr/local/bin/pulse --version
      -> Pulse v6.2.0-rc.8

The installer now prints $BINARY_LINK_PATH and $UPDATE_HELPER_PATH rather
than a bare name, so both stay correct when a custom service name moves
them. The update helper was only affected for custom service names, since
the default /bin/update already sits on the pct exec PATH.

Docs updated in INSTALL.md, FAQ.md and TROUBLESHOOTING.md plus their
frontend-modern/public/docs mirrors. The setup wizard prints the same
broken command and is fixed separately.
2026-08-06 12:08:13 +01:00
rcourtman 9992d98172 Narrow two over-broad triage changes back to scope
Two follow-ups from auditing this morning's triage commits.

2a1bf2839 placed the config-directory guard after safe_systemctl enable, so
a failure to create the directory would have left the update timer enabled
while the installer reported automatic updates as disabled and never wrote
system.json. Move the guard above both the asset install and the timer
enable so a failure leaves nothing half-configured, and pin the ordering
with a test.

419385ccf regenerated the whole bundle-size baseline when only the FilterBar
chunk was over budget. That raised the recorded baseline for 22 other chunks
that were still passing, absorbing about 15 kB of accumulated drift and
resetting their headroom. Restore every other chunk and both totals to their
previous values and bump FilterBar alone, so the drift those chunks have
already accrued stays visible to the next change.

Contract-Neutral: scope corrections to earlier triage commits

Refs #1663
2026-08-03 18:00:04 +01:00
rcourtman 2a1bf28394 Create the config directory before enabling auto-updates
Reinstalling after removing /etc/pulse reaches setup_auto_updates before
setup_directories has recreated the config directory. The system.json
write then failed with "No such file or directory" while the run still
printed that automatic updates were enabled, so the installer reported a
state it had not reached.

mkdir -p the config directory first, and fall back to disabling
auto-updates when it cannot be created.

Contract-Neutral: installer behavioural fix, no contract delta

Refs #1663
2026-08-03 09:47:03 +01:00
rcourtman c8ff52339f Rotate configuration backups instead of growing without bound
Every unattended update created another full config snapshot under
config-backups (or next to the config dir) and nothing ever pruned old
ones, so small root filesystems filled up within days (#1646, reported
on the hardened-unit fallback path where snapshots land under the
install dir). backup_existing now keeps the five newest snapshots and
removes the rest after each successful copy.

Contract-Neutral: installer config backup rotation; shell-only fix, no runtime contract
2026-08-02 18:56:05 +01:00
rcourtman 630d151460 Repair half-removed installations in the installer update paths
A box with the binary still at /opt/pulse/bin/pulse but /etc/pulse, the
systemd unit and the /usr/local/bin symlink deleted by hand takes the
update path ("Reinstalling version ..."), which assumed the previous
install's environment still existed. With auto-updates enabled the run
died at setup_auto_updates writing system.json into the missing config
dir; without them it printed a success completion while systemctl
enable/start had failed with "Unit pulse.service could not be found",
softened into the unprivileged-container note (#1663).

Both the --version and menu update flows now run setup_directories and
recreate the unit file when it is missing (an existing unit stays
untouched so user customizations survive normal updates), and
start_pulse refuses to report success when the unit does not exist at
all. Documented as a deployment-installability contract invariant.
2026-08-01 13:19:59 +01:00
courtmanr@gmail.com 5c1c89a363 Guard auto-update asset staging and migrate stale update units
Adversarial review of 9db25ba60 found four residual defects in the
auto-update asset install, plus a doc line it left contradicting itself.

- install_auto_update_assets copied the bundled helper into the staged
  mktemp file with an unchecked cp, and both call sites invoke the
  function under `if !`, which suppresses errexit for its whole body. A
  failed copy (ENOSPC, EIO) fell through to
  configure_auto_update_script_repo, whose awk emits a lone GITHUB_REPO=
  line for empty input, so a shebang-less one-line stub replaced the
  working helper with a "script" that only ever exits 0 - silently
  disabling unattended updates. Check the cp, and refuse the swap unless
  the staged helper is non-empty and starts with #!.
- Both units were rendered with a bare truncating `cat > "$unit"` whose
  status was never checked, and the function's last statement is
  safe_systemctl daemon-reload, which returns 0 by design. A failing
  write truncated a working unit and still reported success. Render each
  unit to ${path}.tmp and commit it with a checked rename, so a failure
  leaves the installed unit byte-identical.
- The widened ReadWritePaths could not reach deployed boxes: the unit
  that grants the write access is itself the file that has to be
  rewritten, and on an existing install the sandbox running the
  installer excludes /etc/systemd/system and /usr/local/bin (EROFS). The
  Go update pipeline cannot carry it either - pulse.service runs as
  User=pulse with its own ProtectSystem=strict over the install and
  config dirs only. So probe each destination directory up front and,
  when one is blocked, re-exec this already-signature-verified installer
  through systemd-run with a new internal --repair-auto-update-units
  entry point: PID 1 forks the transient unit, so it starts in the host
  mount namespace instead of inheriting the sandbox. The installer is
  copied into the install dir first because the calling unit's
  PrivateTmp=yes hides its /tmp copy from PID 1. The escape needs root
  and systemd-run, and never recurses.
- Keep the ReadWritePaths entries as directory grants: every write now
  commits with a rename from a sibling staging file, and rename needs
  write access on the containing directory, so the file-level entries
  systemd would otherwise accept cannot work. Document the tradeoff in
  the unit and the subsystem contract instead.

The deployment-installability contract still claimed the update sandbox
leaves "only the install dir, config dir and /tmp" writable, which the
paragraph the same file gained in 9db25ba60 contradicts; the same stale
rationale had been copied into two test comments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 11:56:52 +01:00
courtmanr@gmail.com 9db25ba60e Fix doubled auto-update schedule and unfreeze sandboxed refreshes
Residual auto-update defects found while triaging #1643 and #1637
(the primary regression was fixed in 806cbe83d):

- The generated pulse-update.timer carried both OnCalendar=daily and
  OnCalendar=02:00, so with RandomizedDelaySec=4h every box attempted
  two updates per day (00:00-04:00 and 02:00-06:00 windows). Keep the
  single documented 02:00 schedule.
- The generated pulse-update.service sandbox (ProtectSystem=strict)
  excluded the helper and unit directories from ReadWritePaths, so the
  unattended path could never refresh /usr/local/bin/pulse-auto-update.sh
  or rewrite the units - updater fixes only reached boxes via manual
  installs. Grant the sandbox write access to both directories on
  purpose.
- Because the unattended path replaces the helper bash is currently
  executing, stage the new helper next to its destination and swap it
  in with an atomic rename only after repo configuration succeeds. A
  failed download or configure now leaves the previously working helper
  in place instead of rm -f'ing it out from under the enabled timer's
  ExecStart.
- Delete scripts/systemd/pulse-update.{service,timer}: orphaned
  reference copies that had drifted from the units install.sh actually
  generates and were referenced by nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 11:13:37 +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 dbca44937b Add privacy-safe telemetry lifecycle and outcome signals 2026-07-23 01:10:38 +01:00
rcourtman c5bf3adebf Fail closed instead of auto-downgrading, and back up config to a writable path under the hardened update unit
Customer report against v6.0.5 (2026-07-13):
- resolve_target_release fell back to hardcoded v4.5.1 when the GitHub API
  and the /releases/latest redirect both failed, silently downgrading a
  v5.0.17 install two major versions. Now: fail with --version guidance,
  and refuse any auto-resolved target older than the installed version.
- backup_existing wrote /etc/pulse.backup.<ts>, which is read-only under
  pulse-update.service (ProtectSystem=strict, ReadWritePaths=/opt/pulse
  /etc/pulse /tmp), so every unattended update on systemd/LXC failed at
  the backup step. Now: fall back to $INSTALL_DIR/config-backups when the
  config parent is not writable.
2026-07-13 16:13:29 +01:00
rcourtman 5a6871813f Grant CAP_NET_RAW to the installed systemd unit for ICMP probes
The installer's unit hardens with NoNewPrivileges=true, which strips the
ping binary's setuid/file capabilities, so ICMP availability probes could
never work on a systemd install. Grant the capability ambiently instead,
document the systemctl edit override for existing units, and pin the
hardening block with an install test and contract clause.

Addresses #1554 (discussion)
2026-07-11 18:15:22 +01:00
rcourtman c98acb9e85 Harden the installers against piped execution and v-prefixed versions
Two installer bugs reported after the v5 to v6 upgrades:

- The server installer (install.sh) guarded its "am I being sourced?" check
  with a bare ${BASH_SOURCE[0]}. When piped to bash (curl ... | bash) there is
  no source file, so under `set -u` the run aborted with "BASH_SOURCE[0]:
  unbound variable" before the installer body. Default the lookup and only
  early-return on a genuine source. Regression of the v5 fix in #1396. (#1526)

- The agent installer (scripts/install.sh) compared the downloaded binary's
  version ("v6.0.4") against the server /api/version value ("6.0.4") verbatim,
  raising a spurious mismatch warning on matching versions. Strip a leading "v"
  from both sides before comparing so only a genuine difference warns. (#1527)

Refs #1526 #1527
2026-07-09 11:31:43 +01:00
rcourtman 3dd2ecd198 Add Proxmox token smoke diagnostics
Refs #1476
2026-07-07 10:10:16 +01:00
rcourtman d52231855d Fix Proxmox token preservation regressions
Refs #1470, #1476, #1511
2026-07-07 09:49:26 +01:00
rcourtman dc5f0a35c0 Harden installer and demo backup handling 2026-07-04 15:28:42 +01:00
rcourtman 28cee7c467 Prefer PVE 9 guest-agent privileges in setup
Refs #1466
2026-07-03 11:15:51 +01:00
rcourtman d796928969 Install signature verifier dependencies 2026-06-28 03:26:13 +01:00
rcourtman 5e78825f54 fix(install): make unattended auto-updates actually run after install and upgrade
Two GA-blocking bugs in the auto-update install path:

The pulse-update.service unit was rendered through an unquoted heredoc
containing $${PULSE_SERVICE_NAME}, which bash expands to the installer's
PID, so the unit shipped with ExecCondition checking a service like
"24757{PULSE_SERVICE_NAME}". The condition always failed and systemd
silently skipped every scheduled run, for fresh installs and upgrades
alike. The heredoc now renders the detected service name directly:
ExecCondition=/bin/sh -c 'systemctl is-active --quiet pulse'.

Upgrades also never refreshed the updater assets: a v5 box with
auto-updates enabled keeps pulse-update.timer and autoUpdateEnabled=true,
so the update/reinstall flows never re-ran setup_auto_updates and the
v5.1-pinned /usr/local/bin/pulse-auto-update.sh stayed in place, logging
"Already running latest version" forever instead of selecting v6
releases. The asset-install half of setup_auto_updates is now a shared
install_auto_update_assets(), and a new refresh_auto_updates() rewrites
the helper script and units unconditionally whenever the timer already
exists, without touching system.json or the timer's enabled state. All
five install flows (update, reinstall, --version, --source, fresh) are
wired.

Tests now render the real unit and execute the rendered ExecCondition
against a recording systemctl stub instead of asserting source-text
fragments, pin refresh_auto_updates behavior (stale helper replaced,
system.json and enablement untouched), and pin the call-site wiring.
The deployment-installability contract records both invariants.

Note: rewritten message only — the original local commit carried a
parallel agent's licensing commit message due to a shared-index race;
the tree is byte-identical to the hook-verified original.
2026-06-10 22:43:08 +01:00
rcourtman 16f791a656 Harden install-time PVE token extraction with JSON-first parsing (#44/#1312)
The install-time auto-register path (auto_register_pve_node) parsed the API
token secret out of pveum's box-drawing table output with a fragile awk
column-split. The web-setup render path was already hardened to request
'pveum ... --output-format json' first and parse the value field, but this
secondary install.sh path was never ported.

auto_register_pve_node now requests --output-format json first (falling back
to the bare --privsep 1 form only when an older pveum rejects the JSON flag,
which keeps the secure-installer contract pin on that form satisfied) and
extracts the secret via a new extract_pve_token_value helper: JSON value-field
parse first, then a locale-independent box-drawing table fallback (normalizes
the column separator to a plain pipe byte-wise before splitting, so it works
regardless of host locale). This mirrors the hardened render path and removes
the silent-failure / mis-parse risk when pveum table formatting drifts.

Functional + contract tests in root_install_sh_test.go; deployment-installability
contract documents the deterministic extraction. The host-agent path
(internal/hostagent/proxmox_setup.go setupPVEToken) carries an agent-lifecycle
token-permission proof obligation and is left for a governed lane.
2026-06-09 15:47:12 +01:00
rcourtman 2ff2b18c74 Wire legacy sensor-proxy cleanup into install.sh --uninstall (#34)
A Proxmox host upgraded from v5 may still carry the legacy pulse-sensor-proxy
footprint (binary, systemd units, runtime/state dirs, dedicated service user,
and managed SSH keys in root's authorized_keys). install.sh --uninstall removed
everything for the Pulse server itself but left that legacy footprint behind,
so a 'complete uninstall' was not complete -- most notably it left SSH key
entries in /root/.ssh/authorized_keys.

uninstall_pulse now calls cleanup_local_sensor_proxy, which removes the LOCAL
footprint only: stop/disable the units, remove the binary/units/runtime/state
dirs, strip the '# pulse-managed-key' / '# pulse-proxy-key' entries, and remove
the service user/group. It is presence-gated (silent no-op when no proxy was
installed). The aggressive cluster-wide authorized_keys removal and
pulse-monitor@pam API-user deletion stay behind the explicit standalone
scripts/uninstall-sensor-proxy.sh, which we print a pointer to.

Functional + contract tests in scripts/installtests/root_install_sh_test.go;
deployment-installability contract documents the new uninstall removal scope.
2026-06-09 15:25:08 +01:00
rcourtman 1a42011724 Verify Pulse restarts after an interactive update; don't leave it silently stopped (#1323)
Finishes the deferred half of the v5->v6 parity fix for #1323 (the
pulse-auto-update.sh half landed in 672e81985). The interactive install.sh
update/reinstall flow stopped a running Pulse then called start_pulse, which
tolerates a silent start failure (common on unprivileged LXC) by printing a note
and returning 0 — leaving Pulse stopped under an "installation completed!" message.

- stop_pulse_for_update records whether Pulse was running before the update.
- start_pulse, only when Pulse was running before (PULSE_WAS_ACTIVE), no longer
  accepts a silent start failure: it verifies the service became active
  (wait_for_service_active, 20s), retries one explicit start, and surfaces a clear
  error + diagnostics if it still will not come up. Fresh installs are unchanged
  (the flag stays false, so the reassuring container note is kept).
- Wired into all three update/reinstall sites; added a BASH_SOURCE guard so the
  installer's functions can be unit-tested without running the installer.

Scope: fixes the #1323 'restart silently failed' case. Does NOT add a binary
rollback (download_pulse deletes bin/pulse.old right after the swap) — a
bad-release rollback is a separate concern.

Test: scripts/tests/test-install-update-resilience.sh (sources install.sh, stubs
systemctl, asserts was-active capture + retry + clear error). Go installtests +
bash -n confirm the guard does not change installer execution.
2026-06-09 15:05:08 +01:00
rcourtman 7b2cac08bd Fix /install.sh agent wizard on LXC/systemd installs (issue #1470)
The running server serves the unified AGENT installer at its /install.sh
endpoint, which the "Install on Linux" wizard fetches with --url/--token-file.
For published releases handleDownloadInstallScriptCommon only serves the local
/opt/pulse/scripts/install.sh when its .sig and .sshsig sidecars sit next to it;
otherwise it proxies the top-level GitHub install.sh asset. Since 49412357a that
asset is the SERVER installer (LXC/systemd quickstart, --version), so the proxy
now hands the agent wizard a server installer that rejects --url with
"Unknown option".

deploy_agent_scripts copied only the .sh/.ps1 files, never the sidecars, so every
published-release LXC/systemd install always hit the proxy and served the wrong
script. The Docker image already deploys both sidecars (Dockerfile); LXC/systemd
did not. This was latent across rc.1-rc.5 (the proxied asset was the agent
installer then) and surfaced in rc.6 once the asset flipped to the server
installer.

Deploy the .sig/.sshsig sidecars alongside each script so the endpoint serves
the local agent installer and never falls back to the server-installer asset.
Add a behavioral guard that runs deploy_agent_scripts and asserts the sidecars
land next to the served script, and document the served-endpoint sidecar
requirement in the deployment-installability contract.
2026-05-29 13:49:14 +01:00
rcourtman 75d1a0477b Enforce upgrade readiness before v6 updates
Refs #1476
2026-05-28 17:22:01 +01:00
rcourtman 52fd847ac6 Let install.sh smoke harness opt past Docker-environment refusal
install.sh refuses to run inside Docker (correct behavior for end
users), but the install-sh-smoke gate runs the documented systemd
install path inside a privileged systemd-in-Docker container — the
one legitimate bypass case. Added PULSE_INSTALL_ALLOW_DOCKER=1
escape hatch in check_docker_environment() and set it on the docker
exec in the smoke workflow. Takes effect on the next RC; v6.0.0-rc.6's
published install.sh predates this and is signed-frozen, so its
post-publish smoke gate will remain red until the next prerelease.
2026-05-27 22:12:17 +01:00
rcourtman 1dfc4ee6d3 Require .sshsig sidecar for install.sh --archive path
The --archive PATH flag (and the Proxmox LXC bootstrap which
propagates --archive into the container) reached install_pulse_archive
without going through download_release_archive, which is the only
place that previously verified the cryptographic signature on a
Pulse release tarball. That made --archive a clean signature-
verification bypass: anyone who could swap a local tarball
between download and install (shared /tmp on a Proxmox host, a
maintainer testing a swapped local file, etc.) would get
arbitrary root code execution at extract time.

install.sh:
- install_pulse_archive now requires ${archive_path}.sshsig and
  verifies it via the existing verify_release_signature helper
  before tar runs. Missing or invalid signature is a hard
  fail-closed.
- download_release_archive now keeps the .sshsig alongside the
  archive at ${archive_path}.sshsig instead of stashing it in a
  mktemp file that got deleted post-verify. install_pulse_archive
  re-verifies it; no behavior change for the curl|bash path beyond
  the extra integrity check at extract time.
- Proxmox LXC bootstrap now pct push'es ${container_archive_source}.sshsig
  alongside the archive into the container so the in-container
  install_pulse_archive run can verify. Missing sidecar fails the
  bootstrap (cleanup_on_error tears down the half-provisioned
  container).
- Cleanup paths that remove the temp archive on success/failure
  now also remove the sidecar.
- --help advertises the .sshsig requirement.

install-sh-smoke.yml:
- Smoke test now pulls ${tarball}.sshsig alongside the tarball
  before running install.sh --archive inside the test container.

Trade-off: maintainer flows that did `install.sh --archive
/path/to/local.tar.gz` against an unsigned local tarball will now
fail with a clear "Required signature sidecar not found" error.
Maintainers can either pull the corresponding .sshsig from the
release alongside the tarball, or skip --archive in favour of the
canonical download path.
2026-05-21 16:13:57 +01:00
rcourtman 2c51890d01 Harden installer extraction and demo workflow inputs
install.sh:
- find_pulse_binary_in_dir now rejects symlinks, so a malicious
  tarball that smuggled a symlink at the expected binary path
  cannot redirect the cp to $INSTALL_DIR/bin/pulse onto an
  arbitrary file outside the extract dir.
- tar -xzf calls in install_pulse_archive now pass
  --no-same-owner --no-overwrite-dir, defending against archive
  entries that try to chown extracted files or rewrite existing
  directory metadata.

Demo deploy workflows:
- deploy-demo-server.yml routes inputs.target through env: so the
  workflow_dispatch input cannot break out of the case statement,
  even though type: choice constrains it client-side.
- update-demo-server.yml routes inputs.tag, inputs.target, and
  github.event.release.tag_name through env: for the same reason.

All defense-in-depth: download_release_archive still signature-
verifies the tarball, and the demo workflows are gated to write-
access dispatchers. The argument-vs-env distinction matters once
the trigger surface or trust boundary changes.
2026-05-21 15:04:35 +01:00
rcourtman 0f747781fb Support private Pro archive installs 2026-05-07 09:28:38 +01:00
rcourtman d6e96ebeca Fix v6 demo release signing key deployment 2026-05-05 21:40:14 +01:00
rcourtman ce7b459aa7 Harden runtime Proxmox token ACLs 2026-05-05 14:42:05 +01:00
rcourtman c27814d190 Fix stable installer prerelease selection
Refs #1435
2026-05-03 15:20:18 +01:00
rcourtman 87aba32540 Port installer disk preflight from v5 2026-05-01 20:28:11 +01:00
rcourtman 411e8daa4d Port installer bundle fallback fix from v5 2026-05-01 20:28:11 +01:00
rcourtman af8a5f0740 Port RC3 maintenance fixes from v5
Refs #1440, #1444, #1451
2026-05-01 20:28:11 +01:00
rcourtman a60fa03d7f Route operator updates through the local signed helper 2026-04-22 16:18:16 +01:00
rcourtman ce95ef1fc6 Require signed server installer updates 2026-04-22 15:41:54 +01:00
rcourtman 4711d11163 Fix fresh Proxmox LXC installs defaulting to RC 2026-04-20 23:11:46 +01:00
rcourtman fb7b9b01fc Update pinned Go toolchain to 1.25.9 2026-04-18 10:04:34 +01:00
rcourtman e512bb62dc Add explicit auto-update disable install mode 2026-04-10 11:34:58 +01:00
rcourtman 58ad221d47 Add governed v6 preview demo targets 2026-04-10 10:53:17 +01:00
rcourtman d7d757bddd fix(installer): support local release archives on v6 2026-04-09 11:55:03 +01:00
rcourtman a3f99a271b Curate demo-facing mock data across platform views 2026-03-31 18:05:55 +01:00
rcourtman 0b98a0d3e0 fix(deploy): pin docs links to release refs 2026-03-28 21:32:11 +00:00
rcourtman 4b462dff22 Polish prerelease installer help 2026-03-25 11:27:55 +00:00
rcourtman 2c1b90cc4e Reframe prerelease update copy 2026-03-25 11:25:41 +00:00
rcourtman 778a2577b6 feat: Pulse v6 release 2026-03-18 16:06:30 +00:00
rcourtman 572520ebc6 Promote guest-agent /proc/meminfo fallback for accurate VM memory (#1270)
Move the guest-agent file-read of /proc/meminfo earlier in the memory
fallback chain so it runs before RRD, giving real-time MemAvailable that
correctly excludes reclaimable buff/cache on Linux VMs. Also add
VM.GuestAgent.FileRead permission for PVE 9 and fix install.sh to use
comma-separated privilege strings.
2026-03-09 10:04:28 +00:00
rcourtman c0b3a0e665 Restart Pulse service after failed auto-update (#1323)
The auto-update flow stops the Pulse service before applying updates.
If the update fails, the rollback path restored files but never
restarted the service. Since the main unit was explicitly stopped
(not crashed), systemd's Restart=always didn't rescue it.

Add restart-on-failure guards to both pulse-auto-update.sh and
install.sh so Pulse is always restarted after a failed update attempt.
2026-03-07 10:46:19 +00:00