The installer's discover_rootless_container_runtime only globbed
/run/user/* sockets and never consulted the system Docker daemon, so a
socket-activated rootless Podman API socket (alive only for root's login
session on Debian/OMV) won discovery over a healthy rootful Docker and
pinned PULSE_DOCKER_RUNTIME=podman plus CONTAINER_HOST/PODMAN_HOST/
XDG_RUNTIME_DIR into the agent unit. The env-application block also ran
for explicit --enable-docker installs. Rootless discovery now defers to
system_docker_runtime_is_active (docker info with DOCKER_HOST stripped,
or a live /var/run/docker.sock probe) before touching rootless sockets.
On the agent, detectRuntime short-circuited to podman whenever the
preference said podman, mislabeling connections that actually fell
through to the Docker socket and disabling Swarm collection. The
preference is now an ordering hint: a podman-preferred connection landing
on a docker endpoint reports docker, while unlabeled endpoints with no
runtime signals still honor the pin. When the bound socket disappears
mid-run the agent re-runs runtime discovery after three consecutive
daemon-unavailable collects, swapping the connection behind a
swappableDockerClient so concurrent goroutines keep a stable handle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A host running both PVE and PBS is a deployment the docs call officially
supported, and the agent's RunAll registers each product in turn from the
one install token. The bootstrap grant recorded consumption per TOKEN, so
the PVE leg spent it, the PBS leg came back canRegister=false, the agent
wrote a proxmox-pbs-registration-blocked marker, and install.sh printed an
ERROR banner over a PVE source that had registered perfectly well.
- server: consumption is now recorded per canonical type. One PVE create
and one PBS create per token, each still one-shot — a second create of
the same type takes the same 403. The bounds that are not per type stay
singular: the 24h mint-age clock and the first-use bound_hostname are
shared, so whichever type registers first pins the hostname for both and
the second type cannot be aimed at another machine. The per-type ledger
lives in proxmox_registration_consumed_types; a record carrying only
proxmox_registration_completed=true predates it and still reads as every
type consumed, so upgrading cannot revive a token already spent in the
field. The unsuffixed completion block keeps tracking the most recent
completion, which also leaves an older binary reading the same store
failing closed.
- rollback: the consume-before-persist undo is scoped to the keys one
consumption wrote, so a failed PBS source save restores the PBS grant
without resurrecting the PVE grant that already produced a source.
- agent: RunAll no longer lets one product's failure speak for the host. It
attempts and returns the remaining products, errors only when every
detected product failed, and publishes the detected products in a
proxmox-detected-types state marker.
- installer: report_proxmox_registration_outcome reads that marker, waits
for an outcome from each detected product, and prints a success or denial
line per product instead of one verdict. Agents predating the marker keep
the old first-outcome-wins timing so a single-product host does not wait
out the window. The blocked-marker path now only fires on genuine refusals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Settings > Infrastructure installer mints generic host install
tokens, but install.sh auto-detects Proxmox and the agent presents type
pve/pbs at /api/auto-register. The bootstrap grant required an exact
install_type match, so every generic install on a Proxmox node was
denied source creation and the denial was a single buried journal warn.
Four-part fix (#1644):
- server: extend the one-shot bootstrap grant to host-issued install
tokens presenting a canonical Proxmox type. Typed tokens stay pinned,
the grant keeps its settings-write mint requirement, first-hostname
binding, serialized completion, and single consumption across types.
- agent: a canRegister=false denial now logs at error level, returns a
setup error, and records the operator-facing reason in a
proxmox-<type>-registration-blocked state marker.
- installer: report the Proxmox registration outcome in install output
by reading the registered/blocked markers, and poll the server lookup
for a bounded retry window before warning that registration was not
confirmed (readyz flips before the first report cycle).
- setup script: the auto-register transport now captures the HTTP
status alongside the body (no -f), making the invalid-setup-token
branch reachable via 401/403 instead of a dead server-string grep,
and operator guidance names Settings -> Infrastructure instead of the
retired Nodes page (also updated in docs/PBS.md and the pinned
assertions in contract, setup-script, and repoctl docs tests).
Regression proof: internal/api/issue1644_host_install_token_proxmox_test.go
plus new install.sh proofs for the retry window and blocked-marker
surfacing, and the updated hostagent blocked-registration test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The agent installer (scripts/install.sh) had no free-space preflight, so on
RAM-rooted appliances (QNAP QTS, Unraid) it ran all the way to the download
before dying with an unhelpful ENOSPC. Lift the server installer's disk
headroom check into the agent installer: verify temp and install-dir space
(including the shared-filesystem case) before downloading and in
--preflight-only mode, with a TMPDIR hint in the failure message.
The QNAP and Unraid watchdog loops also shell-appended agent stdout to
/var/log/pulse-agent.log with no rotation, which could fill the RAM root on
its own. Pass --log-file so the agent's rotating writer engages (QNAP: data
volume state dir; Unraid: /var/log/pulse-agent with size-capped rotation),
discard the now-duplicate stdout mirror, and keep the watchdogs' own messages
in a small self-trimming log.
Document the TMPDIR override for constrained roots in docs/UNIFIED_AGENT.md.
Fixes#1617 (space half; CPU half pending reporter diagnostics)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An explicit --report-ip is the user naming the primary address on a
multi-NIC host, but identityFromHost appended it after the auto-detected
interface addresses while every consumer of ResourceIdentity.IPAddresses
treats the first entry as primary, so the override never changed what
the Machines table displayed. Prepend it instead.
The install script also rejected --report-ip as an unknown argument even
though the agent supports the flag, forcing hand edits to the service
unit that a later --update run would drop. Accept the flag, render it
into the service ExecStart, persist it in connection state, and
recognise it during saved-state and arg-stream recovery so updates
preserve it.
Refs #829
Contract-Neutral: behavioral fix: user-specified report-ip leads host identity addresses and the installer passes --report-ip through; no public contract change (#829)
Two agent-install failures from stock NAS shells: QNAP ships no od, so
the ELF header sniff read empty and rejected a valid download (issue
#1572); Synology DSM 7's patched systemd cannot apply the unit's
sandbox directives and killed the service with
status=227/NO_NEW_PRIVILEGES before exec (issue #1578). The header
sniff now falls back od -> hexdump -> xxd and skips with a warning when
none exist (checksum verification still guards integrity), and the
systemd unit omits the sandbox hardening block on DSM.
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
Refs #1515
The install command is generated under the Settings infrastructure
installer, not a "Settings > Agents" tab. Correct the agent log, installer
warning, and Machines tooltip to say "the Pulse UI" so the recovery step is
accurate.
Refs #1515
When an upgraded or restored Pulse server no longer recognises an agent's
API token, the report endpoint returns 401. The agent buffered and retried
that report forever with only a generic warning, and the server kept the
node green at its last known agent version because a Proxmox node stays
online via the PVE API poll even after its agent dies.
- Agent: special-case 401 on /api/agents/agent/report. Drop the report
instead of buffering it and log a throttled, actionable error pointing the
operator at the install command to mint a fresh token.
- Installer: verify_agent_server_registration now tells a rejected token
(401/403) apart from "agent has not reported yet" and prints the recovery
steps at install time instead of a vague soft warning.
- Status layer: resourceFromHost flags a stale agent (its host marked
offline by the staleness evaluator) and carries the agent's own last
report time. Coalescing keeps a node online via the PVE source but the
dead agent stays flagged, so its version is no longer presented as current.
The Machines table renders such versions as "(stale)".
Refs #1515
- restore demo runtime env and verify mock fixtures even when the target version is already installed
- require recovered agent update state to include both URL and token before reporting success
Tighten v5-to-v6 upgrade safety, release installability, provider MSP mode handling, AI cost accounting, metrics flushing, and frontend guardrails for the v6.0.0 GA candidate.
Back-port v5 fix 9fe622b88 to v6's append_qnap_autorun_block. The autorun
waiter now polls for the wrapper every 2s up to a 1800s cap, logging the
wait duration on success and a timeout message if the encrypted data
volume never unlocks — instead of v6's unbounded 'while [ ! -x ]; do sleep
5; done' loop that hangs forever. Runtime counters are escaped so they
evaluate at QNAP boot, not install time; marker-based removal keeps it
uninstall-safe.
Add singleton watchdog with lock dir, pidfile tracking, and signal
traps to prevent multiple pulse-agent instances spawning on QNAP.
Tighten procfs matching to avoid killing unrelated processes.
Add stop_qnap_agents() helper that kills wrapper scripts before binaries
to prevent watchdog respawn, and uses path-based pkill patterns that work
with BusyBox and match agents at both old and new install paths.
- Split configuration table into "Installer flags" and "Agent-only flags"
so users know which flags work with `curl | bash` vs the binary directly
- Add missing --cacert and --env flags to installer docs
- Fix --disable-auto-update example (install script doesn't accept it;
use --env PULSE_DISABLE_AUTO_UPDATE=true instead)
- Add --disable-docker/kubernetes/proxmox and --proxmox-type to
install.sh show_help()
- Fix --enable-docker=false in CENTRALIZED_MANAGEMENT.md
On QNAP, /usr/local/bin is a tiny RAM disk. The installer was downloading
the binary then mv'ing it there, which failed when the RAM disk was full.
The QNAP-specific logic that copies to the persistent data volume only
ran after that mv.
Move QNAP detection before the download step so INSTALL_DIR points to the
persistent data volume (e.g. /share/CACHEDEV1_DATA/.pulse-agent) directly.
The wrapper script still attempts to copy to /usr/local/bin at boot but
falls back to running from persistent storage if that fails.
Also fixes:
- pkill -f pattern in wrapper could match and kill the wrapper itself
(path contains "pulse-agent"); switched to pkill -x for exact match
- Upgrade detection now checks /usr/local/bin for legacy QNAP installs
- Uninstall cleans up /usr/local/bin runtime copy
QNAP wipes /etc/init.d on every reboot, so the agent needs persistent
storage on a data volume and autorun.sh boot persistence via the flash
config partition. Adds detection, install (with watchdog wrapper), and
clean uninstall paths. Flash config mount/umount is fail-safe via
subshell isolation to prevent leaving the partition mounted on write
errors.
Users can now pass --env KEY=VALUE (repeatable) to the install script to
inject custom environment variables into the agent's service file. Useful
for KUBECONFIG and similar paths not auto-detected by the installer.
The Settings UI adds a textarea for entering env vars that get appended
to the generated install command. Both frontend and script validate key
format and reject unsafe value characters.
Two changes to prevent duplicates in Settings > Virtual Environment:
1. Install script: only clear Proxmox state files on fresh installs,
not upgrades. Previously every install forced re-registration.
2. Auto-register dedup: match agent re-registrations by server name
when both the existing entry and new request have Pulse-created
tokens (pulse-monitor@pam!pulse-*). This catches the case where
the agent creates a new token after state files are cleared.
The --cacert flag was only used for curl during installation. On systems
with custom CA certificates (e.g. TrueNAS CORE with certs in
/etc/certificates/CA), the agent process had no way to trust the custom
CA and users had to fall back to --insecure.
Set SSL_CERT_FILE in the agent's runtime environment when --cacert is
provided. Go's crypto/x509 reads this natively, so the agent trusts the
custom CA without any binary changes. All service types are covered:
systemd, upstart, launchd, FreeBSD rc.d, OpenRC, SysV init, and Unraid.
Also validates the --cacert path at install time: directories and missing
paths now fail early with a clear message instead of silently proceeding.
Extends the TrueNAS SCALE installer to also support TrueNAS CORE
(FreeBSD-based). The installer auto-detects the platform and configures
the appropriate service manager: systemd for SCALE, rc.d for CORE.
- Rename is_truenas_scale() to is_truenas() with FreeBSD detection
- Add FreeBSD rc.d service script generation with placeholder substitution
- Add FreeBSD bootstrap script for Init/Shutdown task persistence
- Split install/uninstall paths by OS throughout the TrueNAS block
- Add --cacert <path> flag for custom CA bundles (wired to curl only,
not passed to the agent binary)
- Fix --cacert incorrectly mapping to --insecure in exec args
- Fix missing closing quote on RCSCRIPT_LINK in FreeBSD bootstrap
- Fix unreachable echo after exit 0 in FreeBSD bootstrap
Co-authored-by: wilddev65 <wilddev65@users.noreply.github.com>
(cherry picked from commit affdbaeebaf2b1135431b232593122f464c6bb53)
Two fixes for FreeBSD agent support:
1. The Docker image never built or included FreeBSD agent binaries, causing
404 errors when FreeBSD clients requested the download. Added FreeBSD
amd64/arm64 cross-compilation for both host-agent and unified-agent,
plus COPY statements to include them in the image. Also added bare
FreeBSD binaries to GitHub release assets for the redirect fallback.
2. pfSense does not use the standard FreeBSD rc.d boot system — scripts
in /usr/local/etc/rc.d/ must end in .sh to run at boot. The installer
now detects pfSense and creates a .sh boot wrapper alongside the
standard rc.d script. Also added -r flag to daemon for auto-restart.
Related to #1051
- Fixed --disable-docker not being passed to systemd service file. Related to #1151
- Added init: true requirement to HTTPS/TLS docs for Docker. Related to #1166
- Added --kube-include-all-pods and --kube-include-all-deployments flags
- Added --help support to install.sh
- Moved root check in install.sh to allow viewing help as non-root
- Make URL validation case-insensitive to accept Http://, HTTP://, etc.
- Replace nohup with shell backgrounding for QNAP platform compatibility
- Add disown for SIGHUP protection where available
fix: AI chat mobile responsiveness. Related to #1131
- Use responsive width (full on mobile, 480px on larger screens)
- Add flex-wrap to header for better mobile layout
Implements exponential backoff restart loop in the wrapper script
for Unraid/Slackware installations. When the agent exits unexpectedly,
it will automatically restart with increasing delays (5s -> 60s max).
This improves reliability for users who don't have systemd for
automatic service restarts.