Commit Graph

7881 Commits

Author SHA1 Message Date
rcourtman cc0952e491 Run signed candidate builds during dispatched rehearsals 2026-07-09 22:32:40 +01:00
rcourtman 949ba7ed31 Reconcile concurrent main updates 2026-07-09 22:32:01 +01:00
rcourtman cce58ceb19 Improve platform frontend coherence 2026-07-09 22:27:35 +01:00
rcourtman f5c427a4e1 Improve platform frontend coherence 2026-07-09 22:23:21 +01:00
rcourtman 8dda0b6efa Build releases once and promote verified candidates 2026-07-09 22:21:34 +01:00
rcourtman b26716b3c9 Add unit tests for localStorage, logger, toast utils 2026-07-09 21:47:27 +01:00
rcourtman 9706c4fade Add unit tests for alert override identity, threshold mutation models, patrol autonomy 2026-07-09 21:46:11 +01:00
rcourtman 70161e0079 Record unattended patch release proof 2026-07-09 21:42:26 +01:00
rcourtman ba49c28878 Add unit tests for platformPage, docker drawer, and backup presentation modules 2026-07-09 21:33:56 +01:00
rcourtman 0fa841f66f Improve monitor-first attention states 2026-07-09 21:29:45 +01:00
rcourtman 01b14733cf Fix Docker and Kubernetes agent clock-skew liveness
Stamp LastSeen and rate-tracker samples with server receipt time instead
of the agent-reported timestamp, matching the host agent fix. A drifted
agent clock could otherwise mark a reporting Docker host or Kubernetes
cluster permanently stale and loop offline/recovery alerts.

Refs #1519
2026-07-09 21:21:03 +01:00
rcourtman 112a2a68e9 Remove weak ?raw contract-test assertions that pin nothing
Delete generic substring pins (toContain('Button') next to
ButtonLink/ActionIconButton siblings, 'import {', 'const {') that can
never fail independently, plus the not.toContain('Pro') booby trap on
DataHandlingPanel that the word Proxmox would spuriously trip. Where
sibling coverage was incomplete, replace with a specific pin instead:
ErrorBoundary 'Button' becomes the shared-Button import line, and the
proxmoxVersion 'unknown' pin becomes the actual sentinel check.
2026-07-09 21:08:21 +01:00
rcourtman 958c6dfc6e Remove dead empty-iterable guardrail loops from contract tests 2026-07-09 21:03:16 +01:00
rcourtman 0d89be1c91 Use canonical audit record API in migration test 2026-07-09 20:58:23 +01:00
rcourtman 33b287f515 Add unit tests for alertThresholdDefaults, storageSources, storageSummaryCache utils 2026-07-09 20:42:58 +01:00
rcourtman a040c7d7a6 Add unit tests for agentVersion, proxmoxVersion, resourceSearchMatch, vmwareDisplay utils 2026-07-09 20:40:37 +01:00
rcourtman 7897a9131e Fix FreeBSD agent update recovery
Refs #1546
2026-07-09 20:36:58 +01:00
rcourtman f8bbae2f34 Expose demo tailnet identity diagnostics 2026-07-09 20:32:43 +01:00
rcourtman dc27927fb8 Merge remote-tracking branch 'origin/main' 2026-07-09 20:31:22 +01:00
rcourtman c59af9a501 Fix guest suppression for posture alerts
Refs #1545
2026-07-09 20:28:52 +01:00
Richard Courtman 585761c398 Complete security boundary governance record 2026-07-09 20:22:35 +01:00
rcourtman 910418c3b2 Make stable patch releases unattended 2026-07-09 20:16:13 +01:00
Richard Courtman 1968dc4171 Close remaining CodeQL allocation and cookie gaps 2026-07-09 20:10:08 +01:00
rcourtman 042e7ef966 Harden remaining CodeQL security boundaries 2026-07-09 19:46:40 +01:00
rcourtman e8ad8b2fd3 Use checked int parsing for Proxmox conversions 2026-07-09 17:47:23 +01:00
rcourtman 92524e1c27 Harden CodeQL storage and integer boundaries 2026-07-09 17:37:08 +01:00
rcourtman 24b2e40e92 Harden scanned request and storage boundaries
Harden CodeQL-scanned request, command, path, and frontend sinks across relay proxying, availability probes, connection probing, notification CLI execution, report storage, licensing persistence, preview bootstrapping, tooltip rendering, logging, and test identity generation.
2026-07-09 17:22:29 +01:00
rcourtman 2be167331d Harden demo SSH setup for IP targets 2026-07-09 17:06:52 +01:00
rcourtman 76ced45c3a Harden demo SSH setup for private deploy hosts 2026-07-09 16:51:38 +01:00
rcourtman 584aa10699 Stabilize multi-tenant org switch in release E2E v6.0.5 helm-chart-6.0.5 2026-07-09 14:34:46 +01:00
rcourtman 1a05c715ac Reuse authenticated state in multi-tenant release E2E 2026-07-09 13:40:35 +01:00
rcourtman 5fe6bfde57 Harden release integration diagnostics and login retries 2026-07-09 12:50:55 +01:00
rcourtman fbddd91fcb Add install-script regression tests for the piped and version fixes
Cover the two installer bugs fixed in c98acb9e8 so they cannot regress:

- TestRootInstallScriptSourceGuardSurvivesPipedExecution runs the source guard
  through stdin the way curl ... | bash does and asserts it reaches the
  installer body instead of aborting on BASH_SOURCE[0] unbound. (#1526)

- TestInstallSHAgentVersionMismatchIgnoresVPrefix drives the version check and
  asserts v6.0.4 vs 6.0.4 does not warn while a real 6.0.3 vs 6.0.4 does, plus
  content assertions pinning the normalization in scripts/install.sh. (#1527)

Refs #1526 #1527
2026-07-09 12:04:41 +01:00
rcourtman 6999ca0119 Prepare 6.0.5 release 2026-07-09 11:52:28 +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 847cf98544 Fix activation fingerprint and license period status 2026-07-09 11:30:26 +01:00
rcourtman f850099134 Adopt the agent's preferred host when re-registering a matched node
The resolved-identity match in canonical auto-register always preserved
the stored host, so the route-aware IP preference added to the agent in
022f170df could never take effect for an already-registered node: a
reinstall re-matched the node and kept the stale short-DNS host forever.
Preserve the stored host only when it is absent from the agent's ordered
candidate list (an admin-managed endpoint the agent cannot see); when the
agent itself lists the stored host as a lower-priority candidate, adopt
the newly selected host. Identity continuity (same node record, same
token) is unchanged.

Note: commit c21693489 carries this same message by mistake; it actually
contains concurrent OIDC/SAML mapping work from a parallel session that
was staged when the shared index raced. This commit is the real change.
2026-07-09 09:56:04 +01:00
rcourtman c21693489a Adopt the agent's preferred host when re-registering a matched node
The resolved-identity match in canonical auto-register always preserved
the stored host, so the route-aware IP preference added to the agent in
022f170df could never take effect for an already-registered node: a
reinstall re-matched the node and kept the stale short-DNS host forever.
Preserve the stored host only when it is absent from the agent's ordered
candidate list (an admin-managed endpoint the agent cannot see); when the
agent itself lists the stored host as a lower-priority candidate, adopt
the newly selected host. Identity continuity (same node record, same
token) is unchanged.
2026-07-09 09:52:59 +01:00
rcourtman e240f162e9 Reserve the latest markers for the highest stable release
A maintenance cut of an older line (v5.1.36 after v6 GA, or a future
6.0.x patch after 6.1 ships) was allowed to move Docker/GHCR :latest and
the GitHub latest release marker onto itself, silently downgrading every
install that follows latest. Promote :MAJOR and :MAJOR.MINOR
unconditionally, but :latest and make_latest only when the tag is the
highest stable semver. workflow_dispatch gains force_latest as the
explicit rollback escape hatch.
2026-07-09 09:37:40 +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 313552debc Pin the OAuth Tailscale path in the update-demo workflow test
TestUpdateDemoWorkflowUsesGovernedNetworkPath still asserted the retired
authkey: TS_AUTHKEY line; 0a9a29d63 moved update-demo-server.yml to the
OAuth client (TS_OAUTH_CLIENT_ID / TS_OAUTH_SECRET, tag:infra), so the
test now pins those lines instead.
2026-07-08 23:23:20 +01:00
rcourtman 8222f132a8 Explain the pre-5.1.29 auto-update jump to v6 in the upgrade guide
v5 installs on 5.1.28 and older have no release-line pin, so with
auto-update enabled they silently in-place upgrade to the newest stable
GitHub release, which is now v6. Add an FAQ entry for the operator who
lands on v6 without asking, and point the rollback path at 5.1.36, the
final v5 release (was 5.1.35).
2026-07-08 22:56:57 +01:00
rcourtman ef25600270 Name the activation key in Pro runtime mismatch copy
Settings > Plans told paid users on the community runtime to open the
Pro download page "with your license key", but that page rejects
license keys and demands a ppk_live_ activation key, and migrated v5
customers have never been shown one (support case: lifetime customer
pasted his v5 key, hit the prefix error, and emailed support). The
mismatch copy now names the activation key and points at
pulserelay.pro/retrieve-license, which issues one self-serve.
2026-07-08 21:06:55 +01:00
rcourtman 84844003b7 Repair canonical-copy expectation missed by the trial front door commit
b0792890b added planComparisonTrialActionLabel/Note to the billing
presentation without updating the pinned copy object in
licensePresentation.test.ts (hooks do not run vitest, so it landed red).
2026-07-08 21:06:42 +01:00
rcourtman 5f221941c7 Retire Patrol specs pinned to presentation the workbench simplified
The finding-links spec asserted Open related links into the retired
standalone routes from the pre-workbench findings list; those cross-link
affordances were removed deliberately with platform-first navigation,
and the monitor-first workbench feeds findings through a different
pipeline its stubs never reach. The run-history breakdown spec pinned
per-run type counts that the workbench simplified to plain totals; the
underlying count-separation contract (truenas_checked distinct from
agent hosts) lives in the investigation context model and is unit
covered there.
2026-07-08 17:23:04 +01:00
rcourtman 476e0bccb8 Point the TrueNAS mention spec at the live surface and mark it fixme
The spec entered through a retired settings route with an inert REST
stub (mention candidates come from websocket state) and the old Expand
Pulse Assistant launcher name. Re-pointed at the TrueNAS platform page
and the Ask Pulse Assistant launcher, it exposes a real regression:
Docker app-containers appear in the @-mention autocomplete but TrueNAS
app-containers no longer do, despite identical resource typing in
websocket state. The spec keeps the contract and skips via fixme until
mention targeting is restored.
2026-07-08 17:16:25 +01:00
rcourtman 95a7c7f3f5 Pin TrueNAS alert investigation on the resource incidents panel
The alert-links spec pinned Open related links into the retired
standalone routes, which were removed together with those routes'
cross-link affordances; its stub timestamps had also aged out of the
history window. The stubbed TrueNAS alert now uses fresh timestamps, the
Resource action is scoped to its own row (live mock alerts share the
page), and the contract asserts the surviving canonical handoff: the
resource incidents panel scoped to the alerting resource.
2026-07-08 17:03:56 +01:00
rcourtman 9220bab24e Pin TrueNAS disk history through the storage drawer
The disk-history contract lived on the retired /storage route with REST
stubs the platform pages never read. It now drives the TrueNAS storage
section against the mock dataset: the SMART-failing sdc disk opens the
resource drawer, and the History tab must request the disk series from
the metrics store under a canonical key (serial when available, the
disk:<node>:<device> composite otherwise - the store serves both, which
is what protects serial-less disks).
2026-07-08 16:58:51 +01:00
rcourtman e23505206d Carry the max_users grant claim into instance license limits
GrantClaims and Claims gain a MaxUsers seat limit mirroring MaxGuests:
the grant JWT claim is copied at the activation bridge and surfaced
through EffectiveLimits()["max_users"], which the already-shipped
user-limit enforcement (MaxUsersLimitFromLicense) reads unchanged.
Inert by default: no plan sets max_users yet, so absent claim = 0 =
unlimited for every existing license and grant. The self-hosted
commercial volume scrub strips only max_monitored_systems and
max_guests; tests now pin that max_users survives it from both the
named field and explicit limits, plus the end-to-end proof that a grant
carrying max_users=3 enforces 3 and a grant without it stays unlimited.
Contract shape recorded in cloud-paid Extension Point 26; the grant
wire contract list gains max_users against the relay-server reference
(pulse-pro 95a18bd).
2026-07-08 16:56:07 +01:00
rcourtman eec5f7ec8c Pin TrueNAS thresholds under the platform-first thresholds scope
The thresholds surface replaced the neutral infrastructure/systems/
containers groupings with per-platform scopes, so the TrueNAS routing
contract inverted by design: TrueNAS systems, pools, datasets, and disks
now live under their own scope button. The spec asserts that scope's
sections against the mock dataset instead of the retired neutral pages.
2026-07-08 16:54:30 +01:00