Commit Graph

11504 Commits

Author SHA1 Message Date
pulse-triage[bot] 12ffa72a96 fix(provider-msp): leave first-run platform running
Start the provider services after licensed compose validation so the printed bootstrap and portal link have a live control plane. Correct the first-run prompt to name only operator-provided values and retain an install contract regression test.

Change-source: pulse-maintainer
2026-09-24 02:24:21 +01:00
pulse-triage[bot] 08b4fa835c fix(cloudcp): apply shortened provider MSP licence periods
A billing-cycle change can retain the licence ID and plan while moving the signed paid period earlier. Compare the full signed expiry, replace the old token and restart, with regression coverage and the matching cloud-paid contract guarantee.

Change-source: pulse-maintainer
2026-09-24 01:47:02 +01:00
rcourtman a384db5d34 Merge pull request #2215 from rcourtman/claude/msp-portal-purchase
Let a provider-hosted platform buy, renew and see its own plan
2026-09-24 01:23:02 +01:00
rcourtman 5c2b4de313 Apply a plan changed in Manage billing on return
A sandbox run of the purchase path showed that a provider who changed
plan in the Stripe billing portal came back to the portal's Clients page
still on the old plan. It stayed that way until the next six-hourly
refresh, unless they found the "Just paid? Apply my purchase now" button,
whose wording does not fit a plan change. Manage billing now returns to
?provider_msp_checkout=billing, and the Plan panel checks the licence
twice on the way back, which is enough for the plan-change webhook to
land without polling on every visit. In the same run Solo to Starter and
back applied about ten seconds after returning.

The paid panel also dropped "Licence valid to <date>". That date is the
paid period end plus 14 days of grace, so a buyer read it as the date they
had paid until. It now appears only once the licence is inside that grace,
as a warning that the subscription has not renewed.
2026-09-24 00:28:19 +01:00
rcourtman 15ffacab59 Merge remote-tracking branch 'origin/main' into claude/msp-portal-plan-panel
# Conflicts:
#	docs/release-control/v6/internal/subsystems/cloud-paid.md
#	docs/release-control/v6/internal/subsystems/deployment-installability.md
2026-09-23 23:47:42 +01:00
rcourtman edda116236 Show a provider-hosted platform its plan and let it buy one
The portal had no billing surface in provider-hosted mode, so a provider
on the free evaluation could not see when it expired, how many client
slots it had left, or any way to pay. Provider-hosted mode now shows the
billing section as Plan. It lists the evaluation or paid plan, its
expiry and the workspaces in use, and on an evaluation it offers the
plans above the current cap at the licence server's Stripe prices.

Owners and admins get Buy, Manage billing, and "Apply my purchase now";
read-only members are told who can buy. After Stripe returns to the
portal the panel keeps refreshing the licence until the purchase lands,
then polls until the restarted control plane serves the new plan,
without reloading the page.

Exercised on a fresh v6.4.1 provider bundle running this build against
a stand-in licence server: the tab, the evaluation expiry and slot
usage, monthly and annual pricing, Buy reaching checkout with the
platform's licence ID and portal return URL, the periodic refresh
signed with the lab's lease key, and a 390px layout with no horizontal
overflow.
2026-09-23 23:45:28 +01:00
rcourtman 205785b655 Merge pull request #2209 from rcourtman/claude/msp-hosted-install-token
Mint agent install tokens in hosted client workspaces
2026-09-23 23:44:20 +01:00
pulse-triage[bot] 08dfdad4d0 Merge pull request #2211 from rcourtman/maintainer/20260923T215018Z
Guard orphaned-backup opt-out and TrueNAS disk-write boundaries
2026-09-23 22:43:46 +00:00
rcourtman 9ff9387c59 Merge pull request #2210 from rcourtman/claude/msp-status-health
Judge client health from Docker when status runs out of band
2026-09-23 23:39:48 +01:00
rcourtman 185c6ac80b Let a provider-hosted platform buy and renew its own licence
A provider could stand the MSP bundle up and evaluate it without asking
anyone, but paying still meant emailing its public key to Pulse and
copying a licence file onto the host by hand. With the licence server's
self-serve purchase path (pulse-pro license-server/provider_msp_purchase),
the control plane can do the whole thing itself.

The control plane now signs a challenge with its lease signing key to
fetch the licence its subscription entitles, validates it exactly as
startup does, requires it to bind this platform's key, and keeps it in
the data directory, because the host licence is a read-only secret
mount. Startup prefers that renewed licence while it validates, so a
paying provider keeps starting after the self-issued evaluation on the
host expires. A changed licence restarts the control plane through the
existing graceful shutdown, since the plan version is read at load by
every workspace-limit, backup and status path; client workspaces keep
running. Preflight, proof, recovery and backup accept either signed
source.

The portal gains owner and admin routes for plan state, checkout, the
Stripe billing portal and an immediate licence refresh, and the plan list
comes from the licence server so it shows only what can be bought, at
Stripe's price. Adds the msp_solo plan: 3 client workspaces, the first
paid step above the 2-workspace evaluation.
2026-09-23 23:30:34 +01:00
rcourtman 05dfff8431 Merge remote-tracking branch 'origin/main' into claude/msp-hosted-install-token
# Conflicts:
#	docs/release-control/v6/internal/subsystems/agent-lifecycle.md
#	docs/release-control/v6/internal/subsystems/storage-recovery.md
2026-09-23 22:55:00 +01:00
pulse-triage[bot] 466a43707c Merge commit '1836341eaec489b889155e777891c0e835f52303'
Change-source: pulse-maintainer
2026-09-23 22:51:34 +01:00
rcourtman 1836341eae Merge pull request #2208 from rcourtman/claude/msp-org-owner-settings
Offer org owners the settings their own org's routes serve
2026-09-23 22:48:19 +01:00
rcourtman f338bd4765 Judge client health from Docker when status runs out of band
provider-msp status, which upgrade.sh and the install proof gate on,
runs in a one-off `docker compose run` container attached only to the
provider ingress network. Its health check dialled each client's
container on the client's isolated network, which that container cannot
reach, and an unreachable client counts as unhealthy. So on any install
with a real client, status failed with "unhealthy active workspaces" and
./upgrade.sh --dry-run exited 1: the backup-gated upgrade path refused to
run as soon as a provider onboarded anyone. CI proofs never saw it
because they only ever run against an empty install. Reproduced on a
fresh v6.4.1 provider bundle with one client whose container Docker
reported healthy.

The runtime image declares a HEALTHCHECK that probes the same
/api/health inside the container, and Docker records the result where
any caller with the socket can read it. Use that when it exists and keep
the HTTP probe for images without one. With the change the same install
reports healthy_tenants=1 and upgrade.sh --dry-run plans cleanly.
2026-09-23 22:46:47 +01:00
pulse-triage[bot] 70788d90f5 Merge orphaned-backup alert regression test
Change-source: pulse-maintainer
2026-09-23 22:37:53 +01:00
pulse-triage[bot] 1adfbb1f24 Merge monitoring target-boundary test
Change-source: pulse-maintainer
2026-09-23 22:37:47 +01:00
pulse-triage[bot] bb6a8be6f1 test(alerts): verify orphaned backup opt-out clears canonical alert
The existing regression only rejected legacy backup-age key prefixes, so it could pass while a canonical orphaned alert remained active. Assert a real orphan alert exists first, then disable the option and require every backup-age alert to clear. This guards the opt-out relevant to #1741 without changing runtime policy.

Change-source: pulse-maintainer
2026-09-23 22:31:02 +01:00
pulse-triage[bot] cc510b0675 test(monitoring): pin TrueNAS disk-write target boundary
Extend the existing same-hostname host-agent and TrueNAS fixture to the disk-write series reported in #1966. Assert connection-scoped agent targets, distinct source values, and that TrueNAS pool capacity writes are not disk-write samples. Keep cross-target equality explicitly synthetic pending reporter evidence.

Change-source: pulse-maintainer
2026-09-23 22:30:38 +01:00
rcourtman adf07a370a Mint agent install tokens in hosted client workspaces
Every provider MSP client workspace runs in hosted mode, where users sign
in through the control plane handoff. The install endpoints decided
whether to mint a token from the workspace's own config, which on a fresh
client has no local credential, API token, proxy secret or SSO provider.
So Settings > Infrastructure > Generate token answered 200 with an empty
token and the Proxmox flow built a command without one: a provider could
not install a single agent from the client workspace. Found on a fresh
v6.4.1 provider bundle.

The router's configTransferAuthenticationConfigured already counts hosted
mode as enforced authentication; the agent-lifecycle copy of the rule
never gained that term. Add it. Self-hosted runtimes with no auth keep
the token-less command.
2026-09-23 22:23:20 +01:00
rcourtman 4d47504331 Offer org owners the settings their own org's routes serve
A provider opening a client workspace from the Pulse Account portal
arrives in that client's organization as its owner. ensureAdminSession
admits an org manager to the org-bound settings routes, but the security
status snapshot reported every settings capability as false for any
org-scoped session. Since the Settings navigation started hiding gated
panels in August, the owner saw only General: no Infrastructure page to
connect the client's Proxmox or install agents, and no Reporting page
for the client reports. The portal's handoff to
/settings/infrastructure fell through to General. Found on a fresh
v6.4.1 provider bundle, where /api/config/nodes answered 200 for the
same session that /api/security/status said could reach nothing.

Route and snapshot now share one settings-admin rule,
sessionUserHasSettingsAdminPrivileges. The snapshot keeps it out of
sessionIsAdmin: it widens only infrastructureRead, availabilityRead and
reportingRead, whose handlers read the organization's own config and
monitor state. Instance administration, detailLevel, authUsername and
billingAdmin stay withheld from org-scoped sessions, as the scoped
local session contract requires.
2026-09-23 22:11:03 +01:00
pulse-triage[bot] d272711c85 Merge pull request #2206 from rcourtman/maintainer/20260923T184316Z
Reduce resource-view read contention and verify TrueNAS metric targets
2026-09-23 19:30:49 +00:00
rcourtman 30bb1ed3ed Merge pull request #2204 from rcourtman/claude/stable-promotion-pin-drift
Let stable promotion carry test-only and installer pin changes
2026-09-23 20:15:25 +01:00
rcourtman 71ec1d875b Let stable promotion carry test-only and installer pin changes
Every stable cut re-pins CANONICAL_DEFAULT_PULSE_VERSION in
scripts/install-docker.sh from the RC to the stable version, and the
stable install-metadata test requires it. The drift allowlist omitted
that file, so no release candidate could be promoted without
hotfix_exception. The rule arrived on 2026-09-01, after v6.4.1, so no
stable promotion has run under it yet. v6.4.5 would be the first to fail.

The installer is shipped content, so the resolver accepts it only when
its diff against the candidate changes nothing but that pin line. Any
other installer change still counts as drift.

Go _test.go files are never compiled into a shipped artifact. A stable
cut can need test-only corrections, as v6.4.5 needs for the RC-derived
install-metadata contract, so they no longer count as unsoaked content.

Tests cover the allowed shapes, the refused near misses, and the pin
check against a real git diff.
2026-09-23 19:41:32 +01:00
pulse-triage[bot] 325fd10546 Merge reviewed candidate 20260923T182022Z-core-runtime
Change-source: pulse-maintainer
2026-09-23 19:41:27 +01:00
pulse-triage[bot] 94c7a8543c test(monitoring): cover distinct TrueNAS host metric targets
Exercise same-hostname host-agent and connection-scoped TrueNAS records through canonical resource resolution and unified metric routing. Keep the synthetic equality check explicit so it does not imply the reporter's live samples matched.

Change-source: pulse-maintainer
2026-09-23 19:29:09 +01:00
pulse-triage[bot] 5804f5b906 Merge pull request #2202 from rcourtman/maintainer/20260923T175815Z
Keep release and provider-MSP frontend builders on the updated Node 24 image
2026-09-23 18:27:52 +00:00
pulse-triage[bot] e054b0ef60 Merge #2199 cached-view optimization onto assigned head
Compose the exact optimization and benchmark source onto the current assigned source so correctness and paired costs can be measured without replacing the admitted Node digest candidate.

Change-source: pulse-maintainer
2026-09-23 19:01:58 +01:00
pulse-triage[bot] ffee736872 build: refresh Node 24 builder digest
Keep the canonical release and provider-MSP frontend builders on the proposed immutable amd64 Node 24 digest. Bind the same-commit change to the deployment contract, release-build metadata proof and provider-MSP rollout-control proof; hosted build and installed rollout remain separate evidence.

Change-source: pulse-maintainer
2026-09-23 18:46:18 +01:00
pulse-triage[bot] 469b2a0774 Merge #2199 cached-view optimization for proof
Compose the reviewed shared-lock change with the clean-read and dirty-cache comparison benchmarks so correctness and both cache states can be measured on the same candidate.

Change-source: pulse-maintainer
2026-09-23 18:16:47 +01:00
pulse-triage[bot] e01d6461ef test(unifiedresources): benchmark dirty view rebuilds
Separate full-cache rebuild cost after a one-resource invalidation from the clean cached-read benchmark for comparable lock-path evaluation.

Change-source: pulse-maintainer
2026-09-23 18:15:10 +01:00
pulse-triage[bot] d09d8d76e6 test(unifiedresources): benchmark cached view readers
Measure clean VM view-cache access under concurrent readers for a bounded before-and-after comparison.

Change-source: pulse-maintainer
2026-09-23 17:09:32 +01:00
pulse-triage[bot] 72a76ae06e Merge pull request #2177 from jamincollins/fix/disable-proxmox-peer-sensors
feat(agent): allow disabling Proxmox peer sensor collection
2026-09-23 16:35:41 +01:00
Jamin W. Collins 410134450d feat(agent): allow disabling Proxmox peer sensor collection 2026-09-23 08:18:44 -06:00
rcourtman a766cd4188 Merge pull request #2200 from rcourtman/claude/release-policy-train
Make releasing the default and define an achievable soak
2026-09-23 15:18:22 +01:00
rcourtman c03be57f0d docs(release): make releasing the default and define an achievable soak
The 2026-09-05 direction left release timing to open-ended judgment. Since
then no line has been cut from main and no stable has shipped, while every
candidate that passed qualification was withdrawn. Restore a fixed cycle
with a closed list of stop reasons, and point at the maintainer's one-page
release policy.

The clean-soak rule required the stable dogfood instance and demo server to
run the candidate, but both run the current stable release by design, so
no candidate could ever soak clean. Define the soak from open regression,
security and data-loss reports, preview telemetry and the candidate
observation guest where it is operational.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015AHsrZFUgqmohpszCMpaPx
2026-09-23 14:10:42 +00:00
pulse-triage[bot] bab9b8dba0 perf(unifiedresources): share clean view cache reads
Use shared locks on clean cached reads, rebuild only when dirty, and retry after rebuilding so concurrent invalidation cannot return stale views. Add a registry-level regression test that cached reads coexist with another reader. Observable resource behavior is unchanged.

Contract-Neutral: Internal lock-acquisition optimization; cached ReadState values and resource/API behavior remain unchanged.
Change-source: pulse-maintainer
2026-09-23 14:51:36 +01:00
pulse-triage[bot] 0d7f5058e2 Merge pull request #2198 from rcourtman/maintainer/20260923T115045Z
Keep PBS History on its host target when the connection uses an IP or alias
2026-09-23 12:27:50 +00:00
pulse-triage[bot] 4670064c21 Merge upstream main snapshot eef1db8587
Change-source: pulse-maintainer
2026-09-23 12:50:06 +01:00
pulse-triage[bot] eef1db8587 Merge pull request #2195 from rcourtman/maintainer/20260923T103133Z
Make Profile M row-windowing checks reliable under load
2026-09-23 11:20:13 +00:00
pulse-triage[bot] 0b68e5cf3c chore(frontend): bind PBS browser proof to integrated tip
The reviewed core-runtime commit is merged exactly, and the two browser-verified frontend source files remain byte-identical to its receipt. Rebind only the receipt base to the integration merge so cumulative governance checks retain exact parent identity.

Change-source: pulse-maintainer
2026-09-23 11:46:21 +01:00
pulse-triage[bot] 4f11deddc7 Merge reviewed candidate 20260923T101610Z-core-runtime (pulse e137630fdf)
Change-source: pulse-maintainer
2026-09-23 11:45:54 +01:00
pulse-triage[bot] e137630fdf fix(unifiedresources): correlate PBS host agent by reported node name
The PBS API reports the node hostname about itself, but host-agent correlation only consulted the configured connection label and endpoint. When a connection is configured by IP or a DNS alias the agent never reports, the host agent could not be linked to its PBS connection, so the Backups drawer lost its persisted host history target between snapshots and fell back to the PBS service key, which has no host series (#1723).

Use the reported node name in pbsInstanceCorroboratesHost and expose it on the PBS resource (pbs.nodeName). The Proxmox Backups identity correlation now includes it so the host row stays correlated when the configured endpoint is not a machine-identity token.

Rebuilt on the current canonical pulse tip e994c41c20, which already carries the web-product retention change (5d831b8765). The offline browser fixture now combines both failure modes: the connection is configured by 10.0.0.5 and linked only by the reported pbs.nodeName, and a live refresh can transiently omit the correlated host row. The check passes 12/12 across pbs-only, side-by-side and guest topologies, stable and reordered snapshots, at 1280x900 and 390x844, and never requests the PBS service target.

Contract-Neutral: Behavioral machine-identity correlation fix plus an additive optional pbs.nodeName field; no breaking contract, public API removal, or canonical-ownership change.
Change-source: pulse-maintainer
2026-09-23 11:29:27 +01:00
pulse-triage[bot] 85f48c2cea Merge reviewed candidate 20260923T101610Z-web-product (pulse df9467cace)
test(frontend): bound Profile M windowing waits like their siblings

Change-source: pulse-maintainer
2026-09-23 11:29:14 +01:00
pulse-triage[bot] df9467cace test(frontend): bound Profile M windowing waits like their siblings
The two Profile M row-windowing cases still waited at vitest/testing-library's 1000ms default with no per-test timeout, so under the release qualification VM's load they could abort the same way Profile L did for v6.4.5-rc.3 (packet 20260923T082318Z-release). Give both the declared 15s waitFor budget on the row-cap assertions and a 30s per-test timeout, matching the adjacent Profile L and grouped/flat contracts. Assertions are unchanged.

Change-source: pulse-maintainer
2026-09-23 11:23:57 +01:00
pulse-triage[bot] 4aaa9fe8c4 Merge pull request #2194 from rcourtman/maintainer/20260923T094510Z
Keep the Backups History chart on its host series (#1723)
2026-09-23 11:23:03 +01:00
pulse-triage[bot] 496340868c Merge pull request #2188 from rcourtman/dependabot/docker/dot-devcontainer/docker-e75168a720
Bump golang in /.devcontainer
2026-09-23 11:21:01 +01:00
pulse-triage[bot] e994c41c20 chore(frontend): bind browser proof to integrated candidate
The integration merges preserve the exact browser-tested frontend bytes. Rebind the content-bound receipt to the integration tip so the canonical cumulative range passes the parent-identity guard.

Change-source: pulse-maintainer
2026-09-23 10:42:15 +01:00
pulse-triage[bot] 92edc05662 Merge reviewed candidate 20260923T090028Z-web-product (pulse 5d831b8765)
Change-source: pulse-maintainer
2026-09-23 10:42:02 +01:00
pulse-triage[bot] 51693f73db Merge reviewed candidate 20260923T090013Z-delivery-trust (pulse 854bcb86fa)
Change-source: pulse-maintainer
2026-09-23 10:41:58 +01:00
pulse-triage[bot] 6997a53046 Merge origin/main 6a294d9fcd into the reviewed integration line (additive Dependabot action-pin config)
Change-source: pulse-maintainer
2026-09-23 10:41:54 +01:00