Commit Graph

342 Commits

Author SHA1 Message Date
rcourtman ff93d99fa1 Add Go branch-coverage tests for pure helpers across six packages
Test-only wave, contract-neutral. New *_branchcov0719pm_test.go files cover
previously-uncovered pure value-in/value-out helpers, each verified to move
its target functions from 0% to full coverage:

- internal/agentcontext: formatKubernetesServicePorts (empty/single/cap/overflow
  arms) and addMetricFact (nil-metric, percent/value/ratio arms) now 100%.
- pkg/reporting: reportLogoTypeFromPath 0->100, reportLogoTypeFromData 28.6->100,
  scaledLogoSize 70->90 (extension and aspect branches).
- internal/cloudcp/email: RenderMagicLinkEmail 0->80 (render success path; the
  compile-time template-error arm is unreachable and left uncovered).
- internal/recovery: recoveryDetailString (nil map, missing key, non-string,
  string arms) and recoveryPointObservedAt 40->100.
- internal/ai/tools: ErrStrictResolution/ErrRoutingMismatch ToToolResponse 100.
- internal/ai/providers: every NormalizeCollections receiver 0->100.

No source or existing test modified.
2026-07-19 15:11:02 +01:00
rcourtman 7791b35ae9 Add Go branch-coverage tests for canonical trust and recovery helpers
Cover pure functions the recent canonical Operational Trust and protection
posture work landed with no unit test:

- recovery/model posture: enum Valid, struct Clone/Validate/Payload and the
  normalize/cloneTime/validOutcome/sortedUniqueStrings/compareProviderStates
  helpers (20 funcs, all 0%->100%)
- operationaltrust contracts: EvidencePayloadRef/Acknowledgement/Suppression/
  LifecycleTransition Validate plus LifecycleTransition/NotificationLink Clone
- cloudcp/proxytrust: ClientIP, ExtractRemoteIP, rightMostUntrustedForwardedIP
  and IsTrustedProxyIP forwarded-header and CIDR parsing
- mockmodel: NormalizeBlendWeight, SeriesForTimestamps, seriesForProfile,
  diskIOValue and flatValue deterministic seeded math
- updatesignature: DecodePrivateKey and HasTrustedPublicKeys
- recovery keys: ProxmoxPBSGuestLooseContinuityKey branch guards
- vmware SourceID and truenas availableAppLogContainers formatting

Test-only, contract-neutral. New *_branchcov0719_test.go files only; no source
or existing test touched.
2026-07-19 15:11:01 +01:00
rcourtman ce7702db7a Add Go branch-coverage tests for models, cloudcp and actionlifecycle helpers
New *_branchcov0718_test.go files extend the Go coverage wave to three more
areas of previously-uncovered pure logic. Covered areas include the domain-type
NormalizeCollections normalizers in models.go, the cloud control-plane pure
helpers for hosted-entitlement org-id normalization, magic-link URL redaction
and default MSP backup paths, and the actionlifecycle dispatch-attempt context
round-trip plus the typed-error Error and Unwrap methods.

Test-only, with no runtime or subsystem-contract change. Impure targets that
need a live Docker manager or tenant registry were skipped per the purity gate
and noted in the wave reports. Verified in a clean worktree at HEAD with go vet
and package tests green, gofmt clean, and every covered target moved from 0
percent toward full coverage.
2026-07-19 15:10:09 +01:00
rcourtman 206b68cc94 Clarify commercial offer boundaries 2026-07-14 17:18:14 +01:00
rcourtman 6b18652812 Point the portal operations-guide link at the repository root
The MSP portal support row deep-linked docs/MSP.md at blob/main, which
the repo-docs link-drift guard rightly rejects: a released portal build
must not reference branch-tip docs that can move out from under it.
The row keeps the docs/MSP.md path as text and links the repository
root instead, matching every other runtime surface. Portal dist rebuilt
alongside.
2026-07-11 11:11:58 +01:00
rcourtman 0db74c7eab Retheme the provider portal onto the product design tokens with dark mode
The portal shipped with a GitHub-Primer palette, light-only, while every
"Open client" hands the operator into the slate/blue Pulse product UI
(which most monitoring users run dark). The portal now uses the product's
Tailwind slate/blue semantic tokens so both halves of the MSP workflow
read as one product:

- Light theme mirrors the product exactly: slate-100 page, white surfaces,
  slate borders/inks, blue-600 solid buttons.
- Full dark theme (slate-900/800 surfaces, blue-400 links, product-style
  translucent status chip backgrounds with readable 300-series text),
  applied via prefers-color-scheme by default with a persisted header
  toggle (data-theme override in localStorage, nonce'd no-flash bootstrap
  in the page template).
- Hardcoded badge/status border literals, the toast, the danger-button
  hover, and border-only panels that relied on the old white page are all
  tokenized so every state renders in both themes.
- Dropped two dead Roboto @font-face blocks and their data-URI payloads
  (nothing referenced the family): portal_app.css shrinks 135KB -> 43KB.

Verified live in both themes: workspace roster, setup queue, client
onboarding panel chips, Access invite panel, signed-out instruction card,
and the error toast; theme choice survives reload and sign-out. Portal
vitest suite 103/103 incl. new theme tests; frontend/dist sync test green.
2026-07-10 11:59:06 +01:00
rcourtman 0e40ec07cb Make the provider MSP portal honest and self-sufficient without an email provider
Exercised the full provider portal as a pilot MSP would and fixed what
made it feel broken:

- The signed-out portal promised "a sign-in link is on the way" even when
  the control plane has no email provider (the bundle default), and team
  invitations silently sent nothing. The portal bootstrap now carries
  email_sign_in_available and provider_hosted_mode; the sign-in page shows
  the host command that actually prints a link, and the invite panel says
  invitation emails are not sent and how to hand over a link instead.
- New "provider-msp portal-link --email" CLI mints a one-time portal link
  for an account member or pending invitee, so teammates can sign in at
  all on email-less installs (bootstrap only covers the owner).
- Portal sessions were fixed at 12h; CP_SESSION_TTL now configures them
  and provider-hosted MSP mode defaults to 7 days.
- Creating a client past the license cap showed a generic "Failed to
  create workspace." toast: the limit error is now a JSON payload with
  current/limit, the API client no longer drops non-JSON error bodies
  (double body read), and the toast explains the license limit.
- Copy polish: provider-mode sign-in intro (no refunds/privacy register),
  least-privilege default invite role, queue tile label matches "Client
  onboarding", softer Support tab with a docs/MSP.md pointer, setup.sh
  summary now prints the bootstrap next step and day-2 sign-in commands,
  .env.example and docs/MSP.md document portal sign-in and sessions.

Contracts updated (cloud-paid, api-contracts, deployment-installability,
security-privacy) with verification pins in tenant_handlers_test,
config_test, magiclink_test, and provider_msp_deploy_test.

Verified live against a dockerless control plane: portal-link for an
invitee redeems, promotes the invitation, and sets a 7-day session;
the at-cap toast shows the license copy; portal vitest suite and
cloudcp/auth/account/installtests Go suites pass.
2026-07-10 11:22:57 +01:00
rcourtman ef895abbd5 portal: replace raw white_label token in onboarding template copy 2026-07-10 00:16:40 +01:00
rcourtman 042e7ef966 Harden remaining CodeQL security boundaries 2026-07-09 19:46:40 +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 b0792890b0 Add the License-panel Pro trial front door (pull-only)
Completes the approved trial flow end to end. The License panel's plan
comparison gains a 'Start 14-day free Pro trial' action (shown only when
unlicensed, trial-eligible, and never-trialed) that rides the existing
purchase-start handoff with trial=1; the handler already forwards
non-reserved params. Pulse Account portal reads the trial arrival flag,
relabels the pro monthly checkout button, shows the card-required note,
and sends trial:true to the checkout session endpoint (server support
deployed in pulse-pro 4c3a28c). Organic portal and public pricing
arrivals see no trial anywhere, per the no-upsell doctrine; retired
local trial-acquisition routes remain 404. Portal dist rebuilt.
2026-07-08 15:29:54 +01:00
rcourtman a37c38f0ec chore(cloudcp/portal): patch dev-dependency advisories
Bump vitest 3.2.4 to 3.2.7 (clears critical GHSA-5xrq-8626-4rwp) and pin
patched transitives via overrides: vite ^7.3.5, esbuild 0.28.1 ($esbuild),
form-data ^4.0.6, postcss ^8.5.10, ws ^8.21.0. Clears 9 Dependabot alerts
(1 critical, 4 high, 3 medium, 1 low) plus a pre-existing ws high-severity
that npm audit surfaced. npm audit now reports 0 vulnerabilities; build,
test, and typecheck green. Refreshes dist build_manifest source_hash for the
new package.json.
2026-07-08 08:25:45 +01:00
rcourtman 4043c466f6 Extend MSP install proof for portal rollups 2026-07-07 20:49:55 +01:00
rcourtman 4382919447 Add MSP report scheduling and alert rollup 2026-07-07 20:37:18 +01:00
rcourtman ccf90bb26d fix: resolve 4 pre-existing test failures blocking clean CI
1. Regenerate pulse-mcp README from canonical manifest (doc drift)
2. Add 31 missing Pulse Intelligence telemetry fields to both PRIVACY.md
   copies to match current Ping struct JSON tags
3. Rebuild portal frontend bundle to update build_manifest.json hash
4. Update action execution contract test to match current code structure
   (handler wrapped with withExternalAgentCapabilityActivity, error codes
   referenced via agentcapabilities constants instead of literal strings)

Full Go test suite now passes clean: 126 packages, 0 failures.
2026-06-26 13:30:00 +01:00
rcourtman ee8a24e14a backend and governance: MCP contract, agent capabilities, API, and release-control
Manifest-backed MCP tools, prompts, and resources with surface affordance contracts; agent capability manifest and governance projection; API contract tests and capability route projection; operations-loop and intelligence-funnel telemetry; release-control subsystem documentation, registry, and tooling; licensing and configuration.
2026-06-23 17:26:15 +01:00
rcourtman a17882458a Rename CP_TRIAL_ACTIVATION_PRIVATE_KEY to CP_ENTITLEMENT_SIGNING_PRIVATE_KEY in the provider MSP bundle
The variable signs hosted entitlement leases; the trial-activation name is
left over from the retired trial era and reads as trial machinery to an
operator generating their licensing root key. The provider MSP bundle has
no installed base yet, so the canonical rename is free today and frozen
the moment the first design partner installs.

- Control plane reads CP_ENTITLEMENT_SIGNING_PRIVATE_KEY first and falls
  back to CP_TRIAL_ACTIVATION_PRIVATE_KEY, so existing Pulse-hosted cloud
  deployments (deploy/cloud, hibernated snapshot) keep working unchanged.
- deploy/provider-msp (.env.example, compose, setup.sh), MSP.md, and the
  install-test pins use the canonical name; error messages name it too.
- deploy/cloud intentionally keeps the legacy name: that stack historically
  signed hosted trial activations, and its snapshot predates the rename.
2026-06-10 14:08:52 +01:00
rcourtman d25b99cbcc Make provider-MSP client runtimes verifiably licensed via chained entitlement leases
Provider-hosted MSP client workspaces previously sat at Community tier
forever: the runtime refreshed leases against the built-in Pulse Cloud URL
(hibernated, 522) instead of the provider control plane, and release-build
images verify leases only against the embedded Pulse key, which an
operator-generated CP_TRIAL_ACTIVATION_PRIVATE_KEY can never satisfy.

- Inject PULSE_PRO_TRIAL_SIGNUP_URL=CP_BASE_URL into client containers so
  lease refresh targets the provider control plane.
- Chain trust through the Pulse-signed provider MSP license: the license
  binds the provider's lease signing public key
  (entitlement_signing_public_key claim); the control plane embeds the
  license in every lease (provider_license claim); release-build runtimes
  verify embedded Pulse root -> provider license -> lease signature.
- Cap chain-verified leases at ProviderChainedLeaseCapabilities: MSP tier
  plus white_label (branded per-client reports), minus Pulse-service-backed
  relay/mobile_app/push_notifications, which otherwise loop doomed
  registrations against Pulse's relay.
- Fail fast at control-plane startup when the license does not bind the
  configured signing key, instead of provisioning silently unlicensed
  client workspaces.

Verified live on a Colima harness: release-tagged tenant image with test
embedded root, Traefik TLS, full provider-msp proof, tenant reports
valid=true plan_version=msp_growth with white_label and zero relay
failures.
2026-06-10 13:58:52 +01:00
rcourtman cbd0311055 Inject workspace display name into hosted tenant runtime env
Hosted tenant containers received PULSE_TENANT_ID but not
PULSE_TENANT_NAME, so alert webhook payloads from provider-hosted client
runtimes fell back to the raw tenant ID instead of a human-readable
workspace label. Resolve the display name from the tenant registry at
container-create time via a ManagerConfig resolver and stamp it
alongside the tenant ID. Display-name changes after creation apply on
the next runtime rollout, which recreates the container with freshly
resolved env.
2026-06-10 11:13:37 +01:00
rcourtman bd6f77e093 Prepare v6.0.0 release candidate
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.
2026-06-04 14:07:14 +01:00
rcourtman faefe6edc8 Remove 198 unreachable Go functions
Dead-code sweep. Functions flagged unreachable by golang.org/x/tools/cmd/deadcode
and confirmed unused across pulse, pulse-enterprise, pulse-pro and pulse-mobile by
adversarial cross-repo verification. Cross-module reachability was checked
explicitly (only pkg/ exported symbols are importable by other modules; internal/
packages and _test.go files are not). go build, go vet and test-compile all pass.
2026-06-03 12:29:37 +01:00
rcourtman d8f5519eed Fix provider MSP tenant rootless startup 2026-06-02 21:31:17 +01:00
rcourtman c7e50d5602 Harden provider-hosted MSP isolation
Broker provider control-plane Docker access through a socket proxy, remove broad host mounts, align audit and rate-limit proxy trust, harden tenant runtime containers, restrict workspace report logo paths, and update provider deploy guardrails.
2026-06-02 21:10:13 +01:00
rcourtman 68e3a015c0 Reconcile provider-hosted MSP copy 2026-06-02 19:14:18 +01:00
rcourtman d6964832a0 Add entitlement-gated report branding 2026-06-02 18:11:25 +01:00
rcourtman d729461cad Harden provider MSP tenant isolation
- isolate provider MSP tenant runtimes onto per-client Docker networks
- add adversarial proofs for workspace cap races, handoff retargeting, and org-bound agent report retargeting
- pin provider deploy proof to start Traefik before tenant network creation
2026-06-02 17:34:10 +01:00
rcourtman 473a0ddee6 Add provider MSP tenant rollout upgrade proof 2026-06-02 16:45:03 +01:00
rcourtman e5a7300aff Add provider MSP workspace recovery 2026-06-02 14:28:16 +01:00
rcourtman a2e860dc8c Add provider MSP backup restore 2026-06-02 14:14:07 +01:00
rcourtman c9f84c5192 Add provider MSP backup command 2026-06-02 14:03:19 +01:00
rcourtman f128c0b8cd Add provider MSP status command 2026-06-02 13:33:27 +01:00
rcourtman c9c415d7a6 Require license-backed provider MSP proof 2026-06-02 13:22:15 +01:00
rcourtman e99ca62c81 Add provider MSP preflight readiness checks 2026-06-02 13:02:13 +01:00
rcourtman 3f20c85e86 Add provider MSP proof command
Adds a provider-hosted MSP proof command that exercises provider bootstrap, workspace creation, hosted tenant install-token generation, handoff exchange, setup-facts visibility, and cross-tenant token isolation.
2026-06-02 12:45:48 +01:00
rcourtman 5134e36c28 Add provider-hosted MSP install path 2026-06-02 12:11:35 +01:00
rcourtman d90352ea89 Add provider-hosted MSP control-plane mode
Add a Stripe-free provider-hosted MSP mode with local MSP plan workspace limits and portal behavior that hides hosted billing surfaces when no billing record exists.
2026-06-02 11:37:29 +01:00
rcourtman 57486cee88 Harden MSP onboarding proof gaps 2026-06-02 10:15:35 +01:00
rcourtman 2d1ad0db4c Make MSP onboarding client-first 2026-06-02 09:36:48 +01:00
rcourtman 0c1bd50460 Harden MSP onboarding flow proof 2026-06-02 09:17:26 +01:00
rcourtman fe06747418 Improve MSP onboarding guidance 2026-06-02 08:55:40 +01:00
rcourtman d425f02661 Improve MSP workspace setup onboarding 2026-06-02 00:27:19 +01:00
rcourtman dfb9bdcd18 Make MSP workspace setup flow first-class 2026-06-01 23:49:30 +01:00
rcourtman d92820cf53 Align MSP signup route tests 2026-06-01 22:56:02 +01:00
rcourtman 00f6ee9daa Make MSP workspace onboarding actionable 2026-06-01 22:41:10 +01:00
rcourtman 914465e67f Tighten MSP workspace tier limits 2026-06-01 16:55:23 +01:00
rcourtman e450c6a4da Lock MSP Starter self-serve checkout 2026-06-01 16:19:24 +01:00
rcourtman b8e47ab7fa Remove MSP signup trial checkout
Make the public MSP signup path create immediate subscription checkout sessions instead of inheriting the individual Cloud trial helper. Preserve the existing individual Cloud trial path, mark checkout billing mode in metadata, and seed MSP checkout-created Stripe account mappings as active.
2026-06-01 00:24:02 +01:00
rcourtman 87604edb21 Add hosted MSP signup front door to the cloud control plane
Adds public self-serve signup for the hosted MSP offering alongside the
existing individual Cloud signup page. New handlers serve /cloud/msp/signup,
/cloud/msp/signup/complete, and /api/public/msp/signup, gated behind the same
PublicCloudSignupEnabled flag. Per-tier Stripe prices
(CP_MSP_STARTER_PRICE_ID, CP_MSP_GROWTH_PRICE_ID, CP_MSP_SCALE_PRICE_ID) are
validated against the canonical msp_starter/growth/scale plan versions; the
page renders an explicit "not open yet" notice when no MSP price is
configured. Checkout sessions carry account_kind=msp metadata so the
provisioner seeds an isolated operator workspace. The cloud and MSP JSON
signup endpoints now share one checkout skeleton parameterized per path.
2026-05-28 22:38:15 +01:00
rcourtman 5513781193 Ensure cloud emails use support reply-to 2026-05-13 12:10:02 +01:00
rcourtman f16aa8a65c Parse stored subscription states for entitlement refresh 2026-05-12 15:17:53 +01:00