Prove v6 paid feature claims

This commit is contained in:
rcourtman 2026-04-29 17:37:38 +01:00
parent 9695ae5fc7
commit df3ab06174
11 changed files with 76 additions and 24 deletions

View file

@ -161,7 +161,7 @@ Patrol and the Assistant support tiered autonomy:
- Everything in Community, plus:
- 14-day history.
- Remote access via Relay.
- Mobile app access and push notifications.
- Pulse Mobile pairing for handoff and push notifications.
### Pro
- Everything in Relay, plus:

View file

@ -10,7 +10,8 @@
This document is the single source of truth for Pulse v6 pricing, tiering, feature
allocation, and conversion mechanics. All code, UI, marketing, and documentation must
align with this document. If there is a conflict, this document wins.
align with this document. If there is a conflict with release-control state, the
release-control source wins and this file must be corrected.
---
@ -110,7 +111,7 @@ than sold by monitored-system volume.
| Alert-triggered root-cause analysis | No |
| Relay | No |
| Push notifications | No |
| Custom URL | No |
| Customer-specific Relay URL | No |
| RBAC | No |
| Audit logging | No |
| SAML SSO | No |
@ -133,9 +134,9 @@ remediation actions through Pulse.
| Monitoring scope | **Core self-hosted monitoring included** |
| Everything in Free | Yes |
| Relay remote access | **Yes** |
| Mobile app access | **Yes** |
| Pulse Mobile pairing | **Yes** (handoff and push notifications) |
| Push notifications | **Yes** |
| Custom URL | **yourlab.pulserelay.pro** |
| Customer-specific Relay URL | No; Relay uses the standard outbound relay service for v6 GA |
| Metrics history | **14 days** |
| Safe remediation workflows | No |
| Alert-triggered root-cause analysis | No |
@ -143,7 +144,7 @@ remediation actions through Pulse.
| Reporting | No |
**Positioning:** The convenience tier. It should feel cheap enough to buy on the spot when
someone wants secure remote access, mobile checks, push notifications, and longer history
someone wants secure remote access, mobile pairing, push notifications, and longer history
without changing their self-hosted monitoring scope.
### Pro — $8.99/month or $79/year
@ -160,7 +161,7 @@ without changing their self-hosted monitoring scope.
| SAML SSO | **Yes** |
| Agent profiles | **Yes** |
| PDF/CSV reporting | **Yes** |
| Trial | **14-day, no credit card** |
| Self-hosted trial acquisition | No; local trial CTAs are retired for v6 GA |
**Positioning:** For serious self-hosted operators who want Pulse to move from monitoring
into operations. The marketing pitch focuses on three things:
@ -345,7 +346,7 @@ want to self-host.
## Self-Hosted Cap Migration
- There is no v6 self-hosted monitored-system cap migration for Community, Relay, Pro, or Pro+
- There is no v6 self-hosted monitored-system capacity migration for Community, Relay, Pro, or Pro+
- Existing self-hosted users keep their monitored coverage through the v6 rollout
- Hosted Cloud and MSP capacity limits remain plan-specific license claims, not self-hosted static tier defaults
- The UI may still explain monitored-system identity, but it must not frame self-hosted growth as a capacity upsell
@ -479,7 +480,7 @@ explain monitored-system identity:
### Frontend
- [x] Remove self-hosted monitored-system cap pressure from billing and pricing surfaces
- [x] Remove self-hosted monitored-system capacity pressure from billing and pricing surfaces
- [x] Present the public self-hosted ladder as Community / Relay / Pro
- [ ] Keep ledger and inventory language focused on what Pulse monitors, not paid capacity pressure
- [ ] Keep paid prompts out of ordinary self-hosted runtime surfaces; commercial copy belongs in explicit pricing, activation, recovery, hosted, or entitlement-aware paths
@ -521,7 +522,7 @@ explain monitored-system identity:
| Date | Change | Author |
|---|---|---|
| 2026-04-29 | Replaced stale capacity-style monitoring phrasing with core-monitoring-included language across active v6 docs and upgrade-return copy so Community does not read like a former capacity upsell. | Codex |
| 2026-04-23 | Removed stale self-hosted monitored-system cap and Pro+ public-checkout language. Reaffirmed Community / Relay / Pro as current public self-hosted tiers, with Pro+ as continuity only and Pro value centered on operations, history, and admin controls. | Codex |
| 2026-04-23 | Removed stale self-hosted monitored-system capacity and Pro+ public-checkout language. Reaffirmed Community / Relay / Pro as current public self-hosted tiers, with Pro+ as continuity only and Pro value centered on operations, history, and admin controls. | Codex |
| 2026-03-17 | Re-locked the self-hosted commercial model around monitored systems rather than installed agents. New self-hosted public pricing: Relay $4.99/$39, Pro $8.99/$79, Pro+ $14.99/$129. Added free-tier grace policy and marked the monitored-system counting migration as still required in code. | Codex + Richard |
| 2026-02-25 | Initial v6 pricing structure finalized | Richard + Claude + Codex |
| 2026-02-25 | Changed counting to agents-only model. Only installed Pulse Unified Agents count toward limits. PVE/PBS/PMG/Docker/K8s connections and discovered resources don't count. This makes limits much more generous in practice (5 agents can monitor an entire multi-node cluster). | Richard + Claude |

View file

@ -18,6 +18,7 @@ python3 scripts/release_control/paid_feature_claims_proof.py --json
- Relay claims only remote web access, mobile pairing, push notifications, and 14-day metric history.
- Pro preserves Relay capabilities and adds operator extras: root-cause analysis, safe remediation workflows, 90-day metric history, RBAC, audit logging, reporting, SAML SSO, and agent profiles.
- Public app, docs, and site copy must not reintroduce old self-hosted monitoring-limit, higher-limit, default-trial, hosted-model-credit, or bundled-Patrol-credit claims.
- Relay must not be marketed as including a customer-specific `*.pulserelay.pro` URL until that product surface exists; v6 GA Relay is the standard outbound relay service.
- History claims are enforced by the runtime metrics-history API, not only shown in copy.
- Pro admin extras are backed by concrete core and enterprise runtime tests for RBAC, audit logging, reporting, SAML SSO, and agent profile behavior.
- Public pricing, checkout, download, and relay-server entitlement behavior remain consistent with those claims.

View file

@ -1542,7 +1542,13 @@ it as a current public Pulse Pro+ package. When direct plan-selection intent
opens the explicit self-hosted comparison surface, the shared presentation
helpers must show Pro's operations, admin, and reporting extras together while
still framing Community, Relay, and Pro as core monitoring included in every
self-hosted tier rather than monitored-system capacity tiers. The same factual
self-hosted tier rather than monitored-system capacity tiers. Relay copy in
that shared owner must describe the current v6 GA product as standard Relay
remote access, supported Pulse Mobile pairing, push notifications, and 14-day
history, and it must not market a customer-specific Relay URL until that
routing surface exists as a backed product capability. Cloud common-inclusion
copy follows the same mobile-language rule: describe supported Pulse Mobile
pairing and push notifications rather than generic mobile app access. The same factual
plan surface must keep inactive-state copy neutral: default Community installs
must describe the instance as ready to use rather than as missing an activation
key, and they must not foreground paid self-hosted activation, v5-era "No Pro

View file

@ -546,7 +546,7 @@ describe('ProLicensePanel', () => {
expect(
screen.getByText(
'Relay is active on this instance. Remote web access, mobile pairing, push notifications, and longer history are available right now.',
'Relay is active on this instance. Remote web access, Pulse Mobile pairing, push notifications, and longer history are available right now.',
),
).toBeInTheDocument();
expect(screen.queryByText('Optional extras')).not.toBeInTheDocument();

View file

@ -40,7 +40,7 @@ describe('cloudPlans', () => {
'Managed hosting',
'Daily backups',
'Secure agent connectivity via Relay',
'Mobile app access and push notifications',
'Pulse Mobile pairing and push notifications',
'Dedicated workspace URL',
],
setupHeading: 'How it works',

View file

@ -426,7 +426,7 @@ describe('licensePresentation', () => {
cards: [
{
title: 'What Relay adds',
body: 'Reach your Pulse web UI securely from anywhere, pair the mobile app for handoff and push notifications, and keep 14 days of history.',
body: 'Reach your Pulse web UI securely from anywhere, pair supported Pulse Mobile clients for handoff and push notifications, and keep 14 days of history.',
highlights: [
'Pulse Relay (Remote Access)',
'Mobile App Pairing',

View file

@ -64,7 +64,7 @@ describe('selfHostedPlans', () => {
expect(SELF_HOSTED_COMMERCIAL_PRESENTATION).toEqual({
pageTitle: 'Pricing',
pageDescription:
'Self-hosted Pulse includes core monitoring for free. Relay adds secure remote access to the Pulse web UI, mobile pairing, and push notifications, while Pro adds root-cause analysis, safe remediation workflows, 90-day history, and admin/reporting extras.',
'Self-hosted Pulse includes core monitoring for free. Relay adds secure remote access to the Pulse web UI, Pulse Mobile pairing, and push notifications, while Pro adds root-cause analysis, safe remediation workflows, 90-day history, and admin/reporting extras.',
mostPopularBadge: 'Most Popular',
currentPlanLabel: 'Current Plan',
includedLabel: 'Included',
@ -109,7 +109,7 @@ describe('selfHostedPlans', () => {
'Remote web access, mobile pairing, and push',
);
expect(SELF_HOSTED_PLAN_BY_TIER.relay.entitlementSummary).toContain(
'Remote web access, mobile pairing, push notifications, and longer history are available right now.',
'Remote web access, Pulse Mobile pairing, push notifications, and longer history are available right now.',
);
expect(SELF_HOSTED_PLAN_BY_TIER.relay.comparisonSummary).toContain(
'Reach your Pulse web UI securely from anywhere',
@ -121,6 +121,11 @@ describe('selfHostedPlans', () => {
'14-day metric history',
]);
expect(SELF_HOSTED_PLAN_BY_TIER.relay.includedExtras).toEqual([]);
expect(SELF_HOSTED_PLAN_BY_TIER.relay.highlights).toContain('Pulse Mobile pairing');
expect(SELF_HOSTED_PLAN_BY_TIER.relay.highlights).toContain('No inbound ports required');
expect(SELF_HOSTED_PLAN_BY_TIER.relay.highlights.join('\n')).not.toMatch(
/yourlab\.pulserelay\.pro|custom\s+(?:url|subdomain|domain)/i,
);
expect(SELF_HOSTED_PLAN_BY_TIER.pro.billingExtrasSummary).toBe(
'Analysis, remediation, and admin controls',
);
@ -245,6 +250,9 @@ describe('selfHostedPlans', () => {
expect(paidClaimText).not.toMatch(
/unlimited|monitoring capacity|guest capacity|hosted quickstart|trial/i,
);
expect(paidClaimText).not.toMatch(
/yourlab\.pulserelay\.pro|custom\s+(?:url|subdomain|domain)/i,
);
});
it('maps current billing tiers back to the shared self-hosted plan definitions', () => {

View file

@ -112,7 +112,7 @@ export const CLOUD_COMMERCIAL_PRESENTATION: CloudCommercialPresentation = {
'Managed hosting',
'Daily backups',
'Secure agent connectivity via Relay',
'Mobile app access and push notifications',
'Pulse Mobile pairing and push notifications',
'Dedicated workspace URL',
],
setupHeading: 'How it works',

View file

@ -82,7 +82,7 @@ export function getSelfHostedPlanEntitlementSummary(
case 'community':
return `${planLabel} is active on this instance. It includes self-hosted monitoring, 7-day metric history, Pulse Patrol (BYOK), and update alerts.`;
case 'relay':
return `${planLabel} is active on this instance. Remote web access, mobile pairing, push notifications, and longer history are available right now.`;
return `${planLabel} is active on this instance. Remote web access, Pulse Mobile pairing, push notifications, and longer history are available right now.`;
case 'pro':
return `${planLabel} is active on this instance. Root-cause analysis, safe remediation workflows, 90-day history, and admin/reporting extras are available right now.`;
}
@ -135,13 +135,13 @@ export const SELF_HOSTED_PLAN_DEFINITIONS: readonly SelfHostedPlanDefinition[] =
entitlementHighlights: getTierEntitlementHighlights('relay', 14),
includedExtras: [],
comparisonSummary:
'Reach your Pulse web UI securely from anywhere, pair the mobile app for handoff and push notifications, and keep 14 days of history.',
'Reach your Pulse web UI securely from anywhere, pair supported Pulse Mobile clients for handoff and push notifications, and keep 14 days of history.',
highlights: [
'Everything in Community',
'Remote web access via Relay',
'Mobile app pairing',
'Pulse Mobile pairing',
'Push notifications',
'Custom URL (yourlab.pulserelay.pro)',
'No inbound ports required',
'14-day metric history',
],
},
@ -196,7 +196,7 @@ export function getSelfHostedPlanDefinitionForBillingTier(
export const SELF_HOSTED_COMMERCIAL_PRESENTATION: SelfHostedCommercialPresentation = {
pageTitle: 'Pricing',
pageDescription:
'Self-hosted Pulse includes core monitoring for free. Relay adds secure remote access to the Pulse web UI, mobile pairing, and push notifications, while Pro adds root-cause analysis, safe remediation workflows, 90-day history, and admin/reporting extras.',
'Self-hosted Pulse includes core monitoring for free. Relay adds secure remote access to the Pulse web UI, Pulse Mobile pairing, and push notifications, while Pro adds root-cause analysis, safe remediation workflows, 90-day history, and admin/reporting extras.',
mostPopularBadge: 'Most Popular',
currentPlanLabel: 'Current Plan',
includedLabel: 'Included',

View file

@ -48,6 +48,10 @@ FORBIDDEN_PUBLIC_COPY_PATTERNS: tuple[tuple[str, str], ...] = (
("hosted model credits", r"\b(?:ai|model)\s+credits?\b"),
("hosted Patrol quickstart", r"\bhosted\s+(?:patrol\s+)?quickstart\b"),
("bundled Patrol run allowance", r"\b25\s+runs,\s+no\s+api\s+key\b"),
(
"customer-specific Relay URL promise",
r"\byourlab\.pulserelay\.pro\b|\ba\s+custom\s+url\b|custom\s+url\s*\([^)]*pulserelay",
),
)
@ -82,6 +86,17 @@ PUBLIC_COPY_AUDIT_FILES: tuple[CopyAuditFileSpec, ...] = (
r"does not expose a general in-app trial",
),
),
CopyAuditFileSpec(
repo="pulse",
relative_path="docs/architecture/v6-pricing-and-tiering.md",
required_patterns=(
r"monitored systems are not the paid\s+gate",
r"Customer-specific Relay URL.*standard outbound relay service",
r"Self-hosted trial acquisition.*No",
r"Metrics history.*14 days",
r"Metrics history.*90 days",
),
),
CopyAuditFileSpec(
repo="pulse",
relative_path="frontend-modern/src/utils/selfHostedPlans.ts",
@ -93,12 +108,22 @@ PUBLIC_COPY_AUDIT_FILES: tuple[CopyAuditFileSpec, ...] = (
r"admin/reporting extras",
),
),
CopyAuditFileSpec(
repo="pulse-pro",
relative_path="MONETIZATION.md",
required_patterns=(
r"Relay Tier.*14-day history",
r"Pulse Relay.*secure remote access",
r"No inbound firewall ports",
r"Self-hosted Pro should be sold on root-cause analysis",
),
),
CopyAuditFileSpec(
repo="pulse-pro",
relative_path="landing-page/index.html",
required_patterns=(
r"Community keeps core monitoring free",
r"mobile app pairing, push notifications, and 14-day history",
r"(?:Pulse Mobile|mobile app) pairing, push notifications, and 14-day history",
r"root-cause analysis, safe remediation workflows, and 90-day history",
r"Do Relay or Pro charge by server\?",
r"Existing Pulse Pro and legacy Pro\+ holders keep their paid runtime access",
@ -109,11 +134,21 @@ PUBLIC_COPY_AUDIT_FILES: tuple[CopyAuditFileSpec, ...] = (
relative_path="license-server/public_pricing.go",
required_patterns=(
r"Community keeps core monitoring free",
r"mobile app pairing, push notifications, and 14-day history",
r"(?:Pulse Mobile|mobile app) pairing, push notifications, and 14-day history",
r"root-cause analysis, safe remediation workflows, team controls, and 90-day history",
r"not as the self-hosted paid gate",
),
),
CopyAuditFileSpec(
repo="pulse-pro",
relative_path="license-server/scripts/create-v6-stripe-prices.sh",
required_patterns=(
r"Pulse Relay",
r"Pulse Mobile pairing",
r"no inbound firewall ports",
r"14-day history",
),
),
)
@ -234,6 +269,7 @@ def build_command_specs(args: argparse.Namespace) -> list[CommandSpec]:
"test",
"--",
"src/utils/__tests__/selfHostedPlans.test.ts",
"src/utils/__tests__/cloudPlans.test.ts",
"src/utils/__tests__/commercialBillingModel.test.ts",
"src/utils/__tests__/licensePresentation.test.ts",
"src/stores/__tests__/license.test.ts",