diff --git a/README.md b/README.md index d0247a1c6f..f5a27de174 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Power-user shortcuts: ### AI-Powered - **Chat Assistant (BYOK)**: Ask questions about your infrastructure in natural language -- **Patrol (BYOK)**: Background health checks that generate findings on a schedule +- **Patrol**: Background health checks that generate findings on a schedule. New workspaces get 25 Patrol quickstart runs with no API key; ongoing self-hosted use is BYOK. - **Alert Analysis (Pro/Pro+/Cloud)**: Optional AI analysis when alerts fire - **Cost Tracking**: Track usage and costs per provider/model @@ -150,6 +150,10 @@ Self-hosted tiers: Pulse counts top-level monitored systems once no matter how they are collected. VMs, containers, pods, disks, backups, and other child resources under that system are included rather than counted separately. +Community includes Patrol quickstart for first-run activation: every new workspace gets 25 +Patrol runs with no API key. After that, self-hosted Patrol continues with your configured +provider. Relay remains the convenience tier, and Pro/Pro+ are the paid operations tiers. + Runtime-aligned capability summary: | Capability | Community | Relay | Pro | Pro+ | Cloud | @@ -175,7 +179,10 @@ Pulse Patrol runs on your schedule (every 10 minutes to every 7 days, default 6 - Clock drift across cluster nodes - Container health check failures -On self-hosted installs, Pulse Patrol uses your configured provider (BYOK) and runs entirely on your server. +On self-hosted installs, Pulse Patrol's steady-state path uses your configured provider +(BYOK) from your Pulse server. The one hosted exception is Patrol quickstart: every new +workspace gets 25 Patrol runs through the managed quickstart proxy so you can try Patrol +before adding an API key. Chat Assistant remains BYOK. Technical highlights: - Cross-system context (nodes, VMs, backups, containers, and metrics history) diff --git a/docs/PULSE_PRO.md b/docs/PULSE_PRO.md index 1cbd8726b7..0fa70be663 100644 --- a/docs/PULSE_PRO.md +++ b/docs/PULSE_PRO.md @@ -73,7 +73,7 @@ This matrix is derived from the canonical table in `docs/architecture/ENTITLEMEN | Constant | Capability Key | Display Name | Community | Relay | Pro | Pro+ | Cloud | Primary Gating Mechanism / Notes | |---|---|---|:---:|:---:|:---:|:---:|:---:|---| -| `FeatureAIPatrol` | `ai_patrol` | Pulse Patrol (Background Health Checks) | Y | Y | Y | Y | Y | Patrol itself is available on Community with BYOK. Higher-autonomy outcomes and fix execution are separately gated. | +| `FeatureAIPatrol` | `ai_patrol` | Pulse Patrol (Background Health Checks) | Y | Y | Y | Y | Y | Patrol itself is available on Community with BYOK, and new workspaces get 25 Patrol quickstart runs with no API key for first-run activation. Higher-autonomy outcomes and fix execution are separately gated. | | `FeatureRelay` | `relay` | Remote Access (Mobile Relay) | N | Y | Y | Y | Y | API route gating via `RequireLicenseFeature(..., relay, ...)` for relay settings and onboarding endpoints. | | `FeatureAIAlerts` | `ai_alerts` | Alert Analysis | N | N | Y | Y | Y | API route gating via `RequireLicenseFeature(..., ai_alerts, ...)`. | | `FeatureAIAutoFix` | `ai_autofix` | Pulse Patrol Auto-Fix | N | N | Y | Y | Y | Required for fix execution and higher-autonomy actions. | @@ -108,6 +108,8 @@ Patrol and the Assistant support tiered autonomy: - Core monitoring for up to 5 monitored systems. - 7-day history. - Pulse Patrol with BYOK. +- Patrol quickstart for new workspaces: 25 Patrol runs with no API key. +- Quickstart is Patrol-only activation support, not a general hosted chat entitlement. - Basic SSO and update alerts. ### Relay diff --git a/docs/architecture/v6-pricing-and-tiering.md b/docs/architecture/v6-pricing-and-tiering.md new file mode 100644 index 0000000000..99752f083b --- /dev/null +++ b/docs/architecture/v6-pricing-and-tiering.md @@ -0,0 +1,539 @@ +# Pulse v6 Pricing & Tiering (Canonical) + +> Consolidation Notice (2026-02-27): +> Primary v6 execution authority is `docs/release-control/v6/internal/SOURCE_OF_TRUTH.md` (+ `docs/release-control/v6/internal/status.json`). +> This file remains the detailed pricing evidence/spec and must stay aligned with the release-control source. + +> **Status:** APPROVED — Final structure for v6 launch. +> **Date:** 2026-02-25 +> **Replaces:** All previous pricing documents and v5 pricing structure. + +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. + +--- + +## Design Principles + +1. **Free attracts, paid converts.** The free tier must be good enough to get users in the + door, but constrained enough that serious users feel upgrade pressure naturally. +2. **Gate on action, not information.** Self-hosted AI Patrol is BYOK in steady state, + with one Patrol-only quickstart allowance for first-run activation. We never cap how + many times users can run Patrol through their own API keys. The paid gate is on + auto-execution of fixes, not on analysis or suggestions. +3. **Smooth upgrade ladder.** No large price gaps. Every step up has a clear reason. +4. **Simple to understand.** A homelabber should know which tier is right for them in + under 10 seconds. + +--- + +## Counted Unit + +**Rule:** Pulse sells monitored coverage. The counted unit is a **monitored system**, not +an installed agent. + +**Counts as one monitored system:** +- Proxmox PVE node +- PBS / PMG server +- Standalone Linux, Windows, or macOS host +- Docker host +- TrueNAS / Unraid system +- Kubernetes cluster +- Any other top-level infrastructure system Pulse actively monitors as a first-class root + +**Does NOT count separately:** +- VMs +- containers +- pods +- services +- disks +- pools +- datastores +- backup jobs +- any child resource discovered under a counted top-level system + +**Collection path does not matter:** +- Agent-backed monitoring and API-backed monitoring consume the same cap +- If the same underlying system is seen by both an agent and an API connection, it counts once +- Deduplication must follow canonical unified-resource identity rather than transport-specific state + +**Why this model:** charging by monitored systems matches the product Pulse is actually +becoming. It closes the API loophole, keeps the commercial model honest, and still lets +Pulse include all child resources under the counted system for free. + +**Definition (shown on pricing page, add-system UI, and docs):** +> "A counted system is a top-level machine or cluster Pulse actively monitors. Each monitored +> system counts once toward your plan limit, no matter how Pulse collects it. Everything under +> that system — VMs, containers, pods, disks, backups, and services — is included." + +**Counting stability:** a monitored system should only begin consuming the cap after it is +stable enough to appear as a durable monitored root. Existing offline systems should release +their slot only after a deliberate grace period, not on transient disconnects. + +**Transparent ledger:** in-product UI must show the exact counted systems, their collection +path, and their first-seen / last-seen state so users can understand why they are at X/Y. + +**Implementation transition note:** the current runtime still enforces `max_agents` and +agent-backed counting in some paths. That is a transitional compatibility boundary, not the +long-term commercial model. The canonical v6 destination is monitored-system counting. + +**Examples:** +- A 3-node Proxmox cluster monitored node-by-node counts as **3 monitored systems** +- A 3-node Proxmox cluster plus 1 PBS server plus 1 TrueNAS system counts as **5 monitored systems** +- One Docker host with 15 containers counts as **1 monitored system** +- One Kubernetes cluster with 40 pods counts as **1 monitored system** +- The same host connected by agent and API still counts as **1 monitored system** + +--- + +## Self-Hosted Tiers + +### Community (Free) — $0 + +| Element | Value | +|---|---| +| Monitored systems | **5** | +| Onboarding overflow | +1 monitored system for 14 days (one-time per workspace) | +| Monitoring | Full (Proxmox, Docker, K8s, agents) | +| Alerts | Threshold-based | +| SSO | Basic OIDC | +| Update notifications | Yes | +| Metrics history | **7 days** | +| AI Patrol | Monitor + root-cause summaries + fix suggestions (BYOK, with Patrol quickstart for new workspaces) | +| AI Quickstart Credits | **25 hosted Patrol runs** (no API key needed, Patrol only, powered by MiniMax 2.5M) | +| AI Auto-Fix | **No** (must apply fixes manually) | +| AI Alert Analysis | No | +| Relay | No | +| Push notifications | No | +| Custom URL | No | +| RBAC | No | +| Audit logging | No | +| SAML SSO | No | +| Agent profiles | No | +| PDF/CSV reporting | No | + +**AI Patrol in free tier:** Two modes of operation: + +1. **Quickstart Credits (Patrol only, no API key needed):** Every new workspace gets 25 + hosted Patrol runs powered by MiniMax 2.5M. This gives users a bounded way to try + Patrol before adding a provider. After credits are exhausted, users connect their own + API key to continue self-hosted Patrol. Quickstart is activation support only: it is + not a general hosted chat entitlement and it does not replace BYOK for ongoing + self-hosted AI use. + +2. **BYOK (after credits or by choice):** Users provide their own API key + (OpenAI/Anthropic/etc.). No commercial quota — it's their API key, their money. Only + technical guardrails (anti-loop, rate protection). + +In both modes, the gate is on AUTO-EXECUTION: free users see the full analysis and fix +suggestions but must apply fixes manually. Pro users click "Apply Fix." + +**Quickstart credit economics:** +- Model: MiniMax 2.5M (selected for cost-effectiveness) +- Estimated cost: $0.002–0.01 per Patrol run +- Budget per 10,000 users (all 25 credits used): $500–2,500 +- This is customer acquisition cost, not ongoing expense +- Cost can be further reduced with: caching repeated analyses, strict context/token caps, + model cascade (cheap triage first, deeper analysis only when needed) + +### Relay — $4.99/month or $39/year + +| Element | Value | +|---|---| +| Monitored systems | **8** | +| Everything in Free | Yes | +| Relay remote access | **Yes** | +| Mobile app access | **Yes** | +| Push notifications | **Yes** | +| Custom URL | **yourlab.pulserelay.pro** | +| Metrics history | **14 days** | +| AI Auto-Fix | No | +| AI Alert Analysis | No | +| RBAC/Audit/SAML | No | +| Reporting | No | + +**Positioning:** The low-friction headroom tier. It should feel cheap enough to buy on the +spot when someone is just over the Community boundary and wants remote access, mobile, and +push at the same time. + +### Pro — $8.99/month or $79/year + +| Element | Value | +|---|---| +| Monitored systems | **15** | +| Everything in Relay | Yes | +| AI Auto-Fix | **Yes** (one-click execution, safety preflight, rollback) | +| AI Alert Analysis | **Yes** | +| Kubernetes AI Analysis | **Yes** | +| Scheduled remediations | **Yes** | +| Execution audit trail | **Yes** | +| Metrics history | **90 days** | +| RBAC | **Yes** | +| Audit logging | **Yes** | +| SAML SSO | **Yes** | +| Agent profiles | **Yes** | +| PDF/CSV reporting | **Yes** | +| Trial | **14-day, no credit card** | + +**Positioning:** For serious homelabbers who want AI to manage their infrastructure and +want full history. The marketing pitch focuses on three things: +1. "AI that fixes your infrastructure" (Patrol auto-fix) +2. "Monitor from anywhere" (Relay, inherited) +3. "See your full history" (90-day metrics) + +Enterprise features (RBAC, audit, SAML) are included but positioned as bonus value, not +the headline. + +### Pro+ — $14.99/month or $129/year + +| Element | Value | +|---|---| +| Monitored systems | **50** | +| Everything in Pro | Yes | +| Trial | **14-day, no credit card** | + +**Positioning:** For power users with large homelabs (15+ agents). Simple $50/yr bump from +Pro. Anyone with 50+ agents should contact us (likely MSP or business). + +--- + +## Cloud Tiers (Hosted — separate page) + +All Cloud tiers include everything in Pro + managed hosting + daily automated backups. +Cloud launches alongside v6 (not behind a waitlist). + +### Cloud Starter — $29/month or $249/year + +| Element | Value | +|---|---| +| Agents | **10** | +| All Pro features | Yes | +| Managed hosting | Yes | +| Daily backups | Yes | +| Support | Community | +| Founding rate | **$19/month** (first 100 signups, locked while subscription active) | +| Trial | **14-day, no credit card** | + +### Cloud Power — $49/month or $449/year + +| Element | Value | +|---|---| +| Agents | **30** | +| All Pro features | Yes | +| Managed hosting | Yes | +| Daily backups | Yes | +| Support | **Priority** | +| Trial | **14-day, no credit card** | + +### Cloud Max — $79/month or $699/year + +| Element | Value | +|---|---| +| Agents | **75** | +| All Pro features | Yes | +| Managed hosting | Yes | +| Daily backups | Yes | +| Support | **Priority** | +| Trial | **14-day, no credit card** | + +75+ agents: Contact us. + +--- + +## MSP Tiers (Multi-Tenant — separate page) + +All MSP tiers include everything in Pro + multi-tenant management UI + port separation +(agent vs web UI) + webhook templates. Annual pricing is 2 months free (~17% savings). + +### MSP Starter — $149/month or $1,490/year + +| Element | Value | +|---|---| +| Clients | Up to **10** | +| Host pool | **50** | +| Multi-tenant UI | Yes | +| Port separation | Yes | +| Webhook templates | Yes | +| White-label | Future | + +### MSP Growth — $249/month or $2,490/year + +| Element | Value | +|---|---| +| Clients | Up to **25** | +| Host pool | **150** | +| Multi-tenant UI | Yes | +| Port separation | Yes | +| Webhook templates | Yes | +| White-label | Future | + +### MSP Scale — $399/month or $3,990/year + +| Element | Value | +|---|---| +| Clients | Up to **50** | +| Host pool | **400** | +| Multi-tenant UI | Yes | +| Port separation | Yes | +| Webhook templates | Yes | +| White-label | Future | + +### MSP Enterprise — Custom + +| Element | Value | +|---|---| +| Clients | **50+** | +| Host pool | Custom | +| White-label | **Yes** | +| Pricing | Negotiated | + +--- + +## Conversion Mechanics + +### 1. Monitored-system cap ladder (5 → 8 → 15 → 50) +Graduated upgrade pressure as infrastructure grows. In-product UI shows "5/5 monitored systems" +with an upgrade CTA before hard block. The free tier must still fit one real small homelab end +to end, while paid tiers make boundary crossings feel easy and fair. + +### 2. AI fix previews (strongest lever) +Free/Relay users see exactly what Patrol found and how to fix it (specific commands), but +must apply manually. Pro users see the same information with an "Apply Fix" button. Every +Patrol finding is a conversion moment. + +### 3. AI Quickstart Credits (25 hosted Patrol runs) +Every new workspace gets 25 Patrol runs powered by MiniMax 2.5M with no API key setup. +This gives users one bounded Patrol-first activation path. After credits run out, self-hosted +users connect their own key (BYOK, stays free). Upgrading later unlocks auto-fix, alert +analysis, and other paid operations features; it does not replace BYOK for self-hosted AI +runtime. Cost: ~$0.002–0.01 per run. + +### 4. Relay as impulse buy ($39/yr) +Fills the $0 → Pro gap. Relay is not the automation tier; it is the cheap convenience and +headroom tier. Remote access + mobile + push notifications + custom URL + three extra counted +systems should make it an easy purchase for users sitting on the Community boundary. + +### 5. Contextual trial triggers +14-day Pro trial offered at moments of maximum desire: +- Patrol finds a fixable issue → "Apply this fix automatically? Start your free trial" +- User hits monitored-system cap → "Need more room? Start your free trial" +- User taps 30-day chart range → "See your full history — start your free trial" +- User tries Relay from free tier → "Monitor from anywhere — upgrade to Relay ($49/yr) or + start a Pro trial" +- 7+ days of active use → proactive "Experience the full power" nudge + +### 6. Onboarding overflow (+1 monitored system for 14 days) +New free users get a temporary 6th counted-system slot for their first 14 days. Prevents hard-wall +frustration during initial setup. One-time per workspace. + +### 7. Transparent counted-system ledger +Always visible in the UI: "5/5 monitored systems." Link to the counted-system ledger showing +exactly what's counted, which collection path is being used, and what is included under each +system. Upgrade CTA appears before hard block, not +after. + +### 8. Upsell snooze (7-day, not permanent) +Users can snooze upgrade prompts for 7 days. No permanent mute option. Power users who +use the product heavily are highest-potential converters — don't let them opt out forever. + +### 9. Cloud launches with v6 +Not behind a waitlist. Real pricing, real signup. Captures convenience buyers who don't +want to self-host. + +--- + +## v5 Customer Migration + +- Existing Pro customers keep their current recurring price until they cancel +- Auto-exchange to v6 license format on binary upgrade +- If subscription lapses and they return, they come back at v6 rates +- Exchange endpoint: `POST /v1/licenses/exchange` +- Once migrated: renewal emails suppressed, legacy JWT disabled + +## Free-Tier Cap Migration + +- Existing free users who end up above the new counted-system cap must not be hard-broken on rollout day +- Existing monitored coverage should continue during a defined grace period +- During grace, Pulse should block only new counted-system additions until the user reduces usage or upgrades +- The UI must explain the new counting model clearly and show the exact systems consuming the cap + +--- + +## Trial System + +- **Duration:** 14 days +- **Credit card:** Not required +- **Available on:** Pro, Pro+, Cloud (all tiers) +- **Not available on:** Relay (cheap enough to just buy) +- **Features during trial:** Full Pro capabilities +- **Activation paths:** + - Self-hosted: POST `/api/license/trial/start` initiates hosted signup and returns a control-plane `action_url` + - Self-hosted completion: hosted control plane returns a signed token to `/auth/trial-activate` + - Cloud: Stripe checkout with trial period +- **Restrictions:** + - One trial per workspace + - Rate limited: 1 trial initiation attempt per org per 24 hours + - Cannot start if already has active Pro license + +--- + +## Pricing Page Layout + +### Main pricing page (self-hosted — 4 columns) + +``` + Free Relay Pro Pro+ + $0 $39/yr $79/yr $129/yr + 5 systems 8 systems 15 systems 50 systems + + [Get Started] [Buy Relay] [Start Trial] [Start Trial] +``` + +Below the table: +- "Need managed hosting? → See Cloud plans" +- "Managing clients? → See MSP plans" +- "Need 50+ systems? → Contact us" + +### Cloud page (3 columns) + +``` + Starter Power Max + $29/mo $49/mo $79/mo + 10 agents 30 agents 75 agents +``` + +### MSP page (3 columns + Enterprise) + +``` + Starter Growth Scale Enterprise + $149/mo $249/mo $399/mo Custom + 10 clients 25 clients 50 clients 50+ +``` + +--- + +## Canonical User-Facing Copy + +### Counted-unit explainer + +Use this exact idea everywhere pricing or cap enforcement is shown: + +> "Pulse counts monitored systems, not everything underneath them. Each top-level machine or +> cluster counts once, no matter how Pulse collects it. VMs, containers, pods, disks, backups, +> and services under that system are included." + +### Plan taglines + +- **Community:** Monitor up to 5 systems for free. +- **Relay:** Get a bit more room and monitor from anywhere. +- **Pro:** Pulse does not just watch your infrastructure. It helps operate it. +- **Pro+:** Everything in Pro, with more room for larger labs. + +### Boundary-upgrade copy + +- **Community → Relay:** Need a little more room? Upgrade to Relay for 3 extra monitored systems plus remote access, mobile, and push notifications. +- **Relay → Pro:** Want Pulse to do more than alert? Upgrade to Pro for AI investigation, auto-fix, and 90-day history. +- **Free over cap grace:** Your existing monitoring will keep working for now, but new systems will not be added until you remove one or upgrade. + +--- + +## Stripe Price IDs + +> Updated 2026-02-28 with all v6 price IDs (Self-Hosted, Cloud, MSP). + +### Self-Hosted + +> 2026-03-17 decision: the previous self-hosted v6 public prices are superseded. +> New live Stripe prices still need to be created for the locked $4.99 / $8.99 / $14.99 +> monthly bands and their annual counterparts before public checkout is cut over. + +- Relay Monthly: pending new live Stripe price ($4.99/mo) +- Relay Annual: pending new live Stripe price ($39/yr) +- Pro Monthly: pending new live Stripe price ($8.99/mo) +- Pro Annual: pending new live Stripe price ($79/yr) +- Pro+ Monthly: pending new live Stripe price ($14.99/mo) +- Pro+ Annual: pending new live Stripe price ($129/yr) + +### Cloud (created 2026-02-28) +- Cloud Starter Monthly: `price_1T5kflBrHBocJIGHUqPv1dzV` ($29/mo) +- Cloud Starter Annual: `price_1T5kfmBrHBocJIGHTS3ymKxM` ($249/yr) +- Cloud Starter Founding: `price_1T5kfnBrHBocJIGHATQJr79D` ($19/mo — first 100 signups) +- Cloud Power Monthly: `price_1T5kg2BrHBocJIGHmkoF0zXY` ($49/mo) +- Cloud Power Annual: `price_1T5kg3BrHBocJIGH2EtzKofV` ($449/yr) +- Cloud Max Monthly: `price_1T5kg4BrHBocJIGHHa8Ecqho` ($79/mo) +- Cloud Max Annual: `price_1T5kg5BrHBocJIGH5AIJ4nVc` ($699/yr) + +### MSP (created 2026-02-28) +- MSP Starter Monthly: `price_1T5kgTBrHBocJIGHjOs15LI2` ($149/mo) +- MSP Starter Annual: `price_1T5kgUBrHBocJIGHT6PiOn6x` ($1,490/yr) +- MSP Growth Monthly: `price_1T5kgVBrHBocJIGHulNsCTb1` ($249/mo) +- MSP Growth Annual: `price_1T5kgWBrHBocJIGHTuaNjnJ2` ($2,490/yr) +- MSP Scale Monthly: `price_1T5kgWBrHBocJIGHo40iFeRd` ($399/mo) +- MSP Scale Annual: `price_1T5kgXBrHBocJIGHWlOgTyGV` ($3,990/yr) + +### Grandfathered (v5) +- v5 Pro Monthly: `price_1ShIsdBrHBocJIGH71yQusLG` ($9/mo) +- v5 Pro Annual: `price_1ShIsnBrHBocJIGHBKkzsZ3T` ($79/yr) + +--- + +## Implementation Slices Required + +### Pulse runtime + +- [ ] Replace agent-only commercial enforcement with monitored-system counting derived from canonical unified-resource roots +- [ ] Deduplicate agent-backed and API-backed monitoring of the same system into one counted unit +- [ ] Preserve child-resource inclusion semantics (VMs, containers, pods, disks, backups do not count separately) +- [ ] Introduce a migration-safe compatibility boundary from `max_agents` to a canonical counted-system limit +- [ ] Add a free-tier grace path for existing installs that end up above the new cap + +### Frontend + +- [ ] Rename user-facing language from agents to monitored systems where the copy is commercial rather than technical +- [ ] Replace the installed-agent ledger with a counted-system ledger +- [ ] Update pricing, paywall, and upgrade copy to the locked $0 / $39 / $79 / $129 annual ladder +- [ ] Keep the upgrade pressure points, but make the counted unit obvious and fair + +### License server / checkout / landing pages (`pulse-pro`) + +- [ ] Create new self-hosted Stripe prices for Relay / Pro / Pro+ at the locked public bands +- [ ] Update plan mappings, checkout flows, and renewal-safe migration logic without disturbing grandfathered v5 continuity +- [ ] Cut the landing page, checkout copy, and purchase surfaces over to monitored-system language and the new price bands + +### Cloud / MSP + +- [ ] Keep current Cloud/MSP list pricing unchanged for now unless a separate decision explicitly revises it +- [ ] Continue differentiating Cloud / MSP limits via plan-specific license claims rather than self-hosted static bands + +--- + +## Review Checkpoints + +### 30-day post-launch review +- Free → Relay conversion rate +- Free → Pro trial start rate +- Trial → paid conversion rate +- Relay → Pro upgrade rate +- Which paywall surfaces fire most (agent cap vs AI fix vs Relay vs history) +- Support load per tier + +### 60-day post-launch review +- Churn by tier +- Revenue per user (actual vs projected) +- Cloud margin analysis +- MSP pipeline health +- Pricing adjustment decisions (if needed) + +--- + +## Revision History + +| Date | Change | Author | +|---|---|---| +| 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 | +| 2026-02-26 | Updated Code Changes Required section — marked Pulse core backend and frontend as DONE, narrowed remaining work to license server (pulse-pro) and cloud control plane. | Claude | +| 2026-02-26 | Marked license server work as DONE (Stripe products created, max_nodes→max_agents complete, plan definitions configured, tests added). Only Cloud control plane Stripe products remain. | Claude | +| 2026-02-27 | Frontend gating audit completed. Backend enforcement is correct for all features. 5 frontend UX gaps identified (ReportingPanel missing license check, several panels missing upgrade links/trial buttons). Details in `feature-capability-audit-2026-02.md` section 9 and `ENTITLEMENT_MATRIX.md` "Frontend Gating Status" section. | Claude | diff --git a/docs/release-control/v6/internal/subsystems/ai-runtime.md b/docs/release-control/v6/internal/subsystems/ai-runtime.md index 2c51e616f8..a5638f2c86 100644 --- a/docs/release-control/v6/internal/subsystems/ai-runtime.md +++ b/docs/release-control/v6/internal/subsystems/ai-runtime.md @@ -75,6 +75,11 @@ runtime cost control, and shared AI transport surfaces. platform-native reads or writes must extend the shared Assistant tool contracts, and read-only or augmentation-only platforms must stay explicit there instead of drifting into provider-local tools. +7. Keep self-hosted Patrol quickstart messaging aligned with backend runtime + truth: the governed quickstart contract is Patrol-only first-run + acceleration with server-authoritative run inventory, not a general hosted + chat entitlement or a replacement for BYOK once Patrol leaves the + quickstart path. ## Current State @@ -126,6 +131,9 @@ sync `credits_remaining` / `credits_total` back into that cache on every server response, and invalidate the cached token on auth rejection instead of reviving local counter truth. Explicit BYOK provider credentials still outrank Patrol quickstart whenever both are present. +Public-facing copy that reflects those runtime fields must therefore speak in +Patrol quickstart runs and Patrol-only no-key activation, not in generic AI +credits or a promise of full hosted chat. That same provider-model contract applies to the chat explore pre-pass in `internal/ai/chat/service_explore.go`: any runtime model that is valid for the main chat execution path, including `quickstart:minimax-2.5m`, must resolve diff --git a/docs/release-control/v6/internal/subsystems/api-contracts.md b/docs/release-control/v6/internal/subsystems/api-contracts.md index ec8adf07e0..19e8c0aece 100644 --- a/docs/release-control/v6/internal/subsystems/api-contracts.md +++ b/docs/release-control/v6/internal/subsystems/api-contracts.md @@ -238,6 +238,9 @@ Own canonical runtime payload shapes between backend and frontend. than from local grant counters, and shared handlers must not invent client-authored commercial identity or synthetic credits when the quickstart server is unavailable + and the public interpretation rule, so those fields describe Patrol-only + quickstart inventory and active runtime source rather than a generic hosted + AI quota or full-chat entitlement 7. Keep Patrol summary payload consumers aligned on one assessment hierarchy: transport-driven Patrol summary surfaces may show supporting counts and outcomes, but the canonical assessment and verification states must remain singular and not be repeated as a second compact verdict strip 8. Keep Patrol verification and activity facts unified on one transport-backed secondary status area: when frontend consumers combine Patrol status payloads (`runtime_state`, `last_patrol_at`, `last_activity_at`, `trigger_status`) with run-history transport, the latest run result, activity mix, scoped-trigger state, and circuit-breaker context must read as one supporting explanation beneath the primary assessment instead of being re-expanded into a separate full-width status strip plus duplicate summary layers and the main Patrol page composition boundary, so once that governed @@ -1811,6 +1814,10 @@ That quickstart transport contract must also preserve the distinction between credit inventory and live runtime path: zero remaining credits alone must not force a blocked or exhausted operator presentation while Patrol is active on a configured non-quickstart provider path. +Those same transport fields now also define the only public quickstart promise: +when pricing, README, or Patrol header copy references them, it must describe +Patrol-only quickstart runs and no-key Patrol activation rather than generic +AI credits or hosted-chat access. Hosted billing-state payloads now also carry the canonical quickstart grant metadata used by hosted bootstrap and refresh flows. Billing reads and contract proofs must preserve `quickstart_credits_granted`, diff --git a/docs/release-control/v6/internal/subsystems/cloud-paid.md b/docs/release-control/v6/internal/subsystems/cloud-paid.md index a07a1a1372..896152d3af 100644 --- a/docs/release-control/v6/internal/subsystems/cloud-paid.md +++ b/docs/release-control/v6/internal/subsystems/cloud-paid.md @@ -127,6 +127,10 @@ agreement, and cloud-specific enforcement rules. 1. Update this contract when cloud plan semantics change 2. Update runtime and frontend tests together when plan/limit rules move 3. Add or tighten drift tests when a pricing/runtime mismatch is fixed +4. Keep self-hosted pricing and public docs on runtime-backed commercial truth: + Patrol quickstart may be presented only as Patrol-only first-run activation + support backed by the license server, while Relay and Pro remain the + canonical commercial story. ## Current State @@ -246,6 +250,11 @@ installs authenticate that bootstrap with the installation token, community installs send a stable `client_installation_id`, and local runtime cache files may memoize the returned token and counts but may not treat those cached counts as commercial authority. +That quickstart allowance is therefore activation support, not the main +commercial pitch: self-hosted pricing and docs may promise Patrol-only +quickstart runs with no API key for first use, but they must not market that +bootstrap as a general hosted chat plan while Relay and Pro carry the paid +story. The self-hosted commercial counted unit is now also locked to monitored systems rather than agent installs. `max_monitored_systems` is the live runtime and UI contract, while legacy `max_agents` / `max_nodes` aliases are diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 305d31b50c..3f6deb99b8 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -258,6 +258,11 @@ connections` visible as the API-backed alternative for Proxmox and route strings or provider-local link builders inside feature panels. 15. Keep shared summary-card emphasis coherent. When shared summary primitives enter an `inactive` state, `SummaryMetricCard`, `InteractiveSparkline`, and `DensityMap` must all demote background context together so storage, infrastructure, and workloads read as one interaction model instead of mixing page-local opacity, sticky-shell, or highlight rules. 16. Keep density-map summaries overview-first. When a shared summary density map receives row focus or chart-hover emphasis, `frontend-modern/src/components/shared/DensityMap.tsx`, `frontend-modern/src/components/shared/useDensityMapState.ts`, and `frontend-modern/src/components/shared/densityMapModel.ts` must preserve the multi-entity overview rows and keep focused-entity detail in the hover tooltip instead of swapping the card into a single-series chart, dimming the rest of the map into unusable background noise, duplicating cursor-value tooltip copy, or adding persistent card chrome that steals heatmap space. The card body must stay overview-first; the tooltip may carry the active entity identity, current value, and peak, shared tooltip shells must follow semantic surface tokens instead of forcing a dark palette in light mode, the tooltip header must let long entity names consume the available width before truncating rather than clipping against an arbitrary fixed label cap, numeric metric readouts such as `16.9 MB/s` or `37.4 MB/s` must stay single-line instead of wrapping the unit onto a second row, and density-map detail that cannot fit cleanly inside the canonical tooltip shell must be omitted rather than introducing tooltip-specific chrome or a secondary chart inside the hover surface. +17. Keep shared commercial and Patrol quickstart presenters on runtime-backed + wording. Reusable shells and helper-driven badges must describe quickstart + as Patrol runs or Patrol-only activation support when that is the governed + backend truth, and must not drift back to generic hosted-chat or generic + AI-credit claims. 17. Keep sparkline scrubbing source-local and sibling-sync timestamp-based. The chart a user is actively scrubbing in `frontend-modern/src/components/shared/InteractiveSparkline.tsx` and `frontend-modern/src/components/shared/useInteractiveSparklineState.ts` must keep its dashed hover cursor on the real local mouse `x`, while sibling cards may map the shared hover timestamp onto their own timelines. Shared cursor sync must not snap the source chart back onto the nearest sample timestamp, the rendered SVG/canvas hover cursor must bind to the actual numeric cursor coordinate rather than a boolean guard state, the time cursor must span the chart viewport instead of collapsing to the series height, and the hover tooltip must track the pointer instead of anchoring to the chart top edge while following the active theme rather than a hardcoded dark shell. 18. Keep shared contextual focus canonical after adoption. Once a summary or table surface enters route-backed contextual focus, future additions must extend `frontend-modern/src/components/shared/contextualFocus.ts` and its guardrail tests rather than forking another helper for workload IDs, resource IDs, or scroll-preserving same-route selection. 19. Keep shared infrastructure/resource selectors on the canonical agent-facet @@ -498,6 +503,10 @@ Those same feature-owned header badges must also stay aligned to the owning runtime state instead of surfacing stale auxiliary counters as primary status; an exhausted quickstart-credit badge cannot override an otherwise active Patrol runtime unless quickstart exhaustion is the active blocker. +When those shared helpers surface quickstart state, the wording must stay +Patrol-scoped as well: the badge copy should talk about Patrol quickstart runs +or Patrol quickstart exhaustion rather than generic AI credits or broad hosted +AI availability. That same route-owned presentation rule also governs Patrol findings empty states: shared section shells under `frontend-modern/src/features/patrol/` must not render a green healthy empty state from `0 active findings` alone diff --git a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md index 1a046f1cc2..af8acff8ac 100644 --- a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md +++ b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md @@ -75,6 +75,10 @@ Patrol-specific presentation helpers. 1. Update Patrol page, state, presentation helpers, and proof files together when Patrol UX semantics change 2. Keep Patrol-specific copy and badge logic inside the governed Patrol presentation helpers instead of page-local branches 3. Update this contract whenever a new Patrol-specific page, store, helper, or presentation component becomes canonical runtime surface area +4. Keep Patrol header quickstart copy Patrol-scoped and runtime-backed: the + Patrol header may promise only the server-authoritative quickstart + inventory, phrase availability as Patrol runs with no API key, and avoid + implying broader hosted chat or generic AI credits. ## Current State @@ -397,6 +401,10 @@ Patrol may only show the quickstart exhausted warning when `using_quickstart` is true or when the Patrol runtime is explicitly blocked by quickstart exhaustion, not merely because the stored credit counter reached zero while a configured provider path keeps Patrol active. +That same header copy must stay Patrol-only in operator-facing language: +available inventory should read as free Patrol quickstart runs with no API +key for Patrol, and exhaustion should direct the operator toward BYOK for +Patrol rather than implying a broader hosted AI allowance. `frontend-modern/src/utils/remediationPresentation.ts` is now also the canonical owner for remediation result badge copy and success/failure styling used by `frontend-modern/src/components/patrol/RemediationStatus.tsx`, so diff --git a/frontend-modern/src/api/__tests__/patrol.test.ts b/frontend-modern/src/api/__tests__/patrol.test.ts index 0a5b35e0f5..bd5835ce97 100644 --- a/frontend-modern/src/api/__tests__/patrol.test.ts +++ b/frontend-modern/src/api/__tests__/patrol.test.ts @@ -75,6 +75,24 @@ describe('patrol api', () => { }); }); + it('preserves the canonical patrol quickstart transport fields', async () => { + apiFetchJSONMock.mockResolvedValueOnce({ + runtime_state: 'active', + healthy: true, + quickstart_credits_total: 25, + quickstart_credits_remaining: 12, + using_quickstart: true, + } as any); + + await expect(getPatrolStatus()).resolves.toMatchObject({ + runtime_state: 'active', + healthy: true, + quickstart_credits_total: 25, + quickstart_credits_remaining: 12, + using_quickstart: true, + }); + }); + it('preserves split patrol recency transport fields', async () => { apiFetchJSONMock.mockResolvedValueOnce({ runtime_state: 'active', diff --git a/frontend-modern/src/api/patrol.ts b/frontend-modern/src/api/patrol.ts index 0b2feeb29c..69b9e8fcdf 100644 --- a/frontend-modern/src/api/patrol.ts +++ b/frontend-modern/src/api/patrol.ts @@ -171,7 +171,7 @@ export interface PatrolStatus { license_status?: LicenseStatus; upgrade_url?: string; summary: FindingsSummary; - // Quickstart credits + // Server-authoritative Patrol quickstart inventory and runtime source. quickstart_credits_remaining?: number; quickstart_credits_total?: number; using_quickstart?: boolean; diff --git a/frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx b/frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx index a195816bcd..98bdb738cb 100644 --- a/frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx +++ b/frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx @@ -142,6 +142,7 @@ export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState
{ }, predictions_count: 0, recent_changes_count: 0, + recent_changes: [], learning: { resources_with_knowledge: 0, total_notes: 0, @@ -759,10 +760,64 @@ describe('AIIntelligence entitlement gating', () => { expect(screen.getByText('Patrol enabled')).toBeInTheDocument(); }); - expect(screen.queryByText('Credits exhausted — connect API key')).not.toBeInTheDocument(); + expect(screen.queryByText('Patrol quickstart exhausted')).not.toBeInTheDocument(); expect(screen.getByText(/Health A · 100\/100/)).toBeInTheDocument(); }); + it('shows the active Patrol quickstart chip only when runtime is actually using quickstart', async () => { + hasFeatureMock.mockReturnValue(true); + licenseStatusMock.mockReturnValue({ subscription_state: 'active' }); + getPatrolStatusMock.mockResolvedValue( + defaultPatrolStatus({ + runtime_state: 'active', + using_quickstart: true, + quickstart_credits_total: 25, + quickstart_credits_remaining: 12, + last_patrol_at: '2026-03-12T09:57:00Z', + }), + ); + intelligenceState.summary = { + timestamp: '2026-03-12T10:00:00Z', + overall_health: { + score: 100, + grade: 'A', + trend: 'stable', + factors: [], + prediction: 'Infrastructure is healthy with no significant issues detected.', + }, + findings_count: { + critical: 0, + warning: 0, + watch: 0, + info: 0, + total: 0, + }, + predictions_count: 0, + recent_changes_count: 0, + learning: { + resources_with_knowledge: 0, + total_notes: 0, + resources_with_baselines: 0, + patterns_detected: 0, + correlations_learned: 0, + incidents_tracked: 0, + }, + }; + + render(() => ); + + await waitFor(() => { + expect(getPatrolStatusMock).toHaveBeenCalled(); + expect( + screen.getByTitle( + '12 of 25 free Patrol quickstart runs remaining. No API key needed for Patrol.', + ), + ).toBeInTheDocument(); + }); + + expect(screen.getByText('Patrol quickstart: 12/25 runs left')).toBeInTheDocument(); + }); + it('surfaces coverage incomplete as the primary patrol assessment instead of no-issues copy', async () => { hasFeatureMock.mockReturnValue(true); licenseStatusMock.mockReturnValue({ subscription_state: 'active' }); diff --git a/frontend-modern/src/pages/__tests__/PricingV6.test.tsx b/frontend-modern/src/pages/__tests__/PricingV6.test.tsx index 7de52ba90e..4d63122502 100644 --- a/frontend-modern/src/pages/__tests__/PricingV6.test.tsx +++ b/frontend-modern/src/pages/__tests__/PricingV6.test.tsx @@ -76,6 +76,13 @@ describe('PricingV6', () => { expect(screen.getAllByText('Relay').length).toBeGreaterThan(0); expect(screen.getAllByText('Pro').length).toBeGreaterThan(0); expect(screen.getAllByText('Pro+').length).toBeGreaterThan(0); + expect( + screen.getByText( + 'Core monitoring stays free. Relay adds remote access, and Pro unlocks AI operations.', + ), + ).toBeInTheDocument(); + expect(screen.getByText('AI Patrol (BYOK)')).toBeInTheDocument(); + expect(screen.getByText('Patrol quickstart: 25 runs, no API key')).toBeInTheDocument(); expect(screen.getByText('8 monitored systems · 14-day history')).toBeInTheDocument(); expect(screen.getByText('15 monitored systems')).toBeInTheDocument(); expect(screen.getByText('50 monitored systems')).toBeInTheDocument(); diff --git a/frontend-modern/src/types/ai.ts b/frontend-modern/src/types/ai.ts index 99b542cb09..fd666e5ee8 100644 --- a/frontend-modern/src/types/ai.ts +++ b/frontend-modern/src/types/ai.ts @@ -54,12 +54,12 @@ export interface AISettings { discovery_enabled?: boolean; discovery_interval_hours?: number; - // Quickstart credits + // Server-authoritative Patrol quickstart inventory surfaced in AI settings. quickstart_credits_total?: number; quickstart_credits_used?: number; quickstart_credits_remaining?: number; quickstart_credits_available?: boolean; - using_quickstart?: boolean; + using_quickstart?: boolean; // true when Patrol is currently using the quickstart provider } export interface AISettingsUpdateRequest { diff --git a/frontend-modern/src/utils/__tests__/aiQuickstartPresentation.test.ts b/frontend-modern/src/utils/__tests__/aiQuickstartPresentation.test.ts index d2564304ae..eb62c98660 100644 --- a/frontend-modern/src/utils/__tests__/aiQuickstartPresentation.test.ts +++ b/frontend-modern/src/utils/__tests__/aiQuickstartPresentation.test.ts @@ -6,8 +6,8 @@ describe('getAIQuickstartCreditsPresentation', () => { expect(getAIQuickstartCreditsPresentation(3, 10)).toEqual({ className: 'bg-blue-50 dark:bg-blue-950 border-blue-200 dark:border-blue-800 text-blue-700 dark:text-blue-300', - summary: '3/10 quickstart credits', - title: '3/10 free quickstart patrol runs remaining. No API key needed.', + summary: 'Patrol quickstart: 3/10 runs left', + title: '3 of 10 free Patrol quickstart runs remaining. No API key needed for Patrol.', }); }); @@ -15,8 +15,8 @@ describe('getAIQuickstartCreditsPresentation', () => { expect(getAIQuickstartCreditsPresentation(0, 10)).toEqual({ className: 'bg-amber-50 dark:bg-amber-950 border-amber-200 dark:border-amber-800 text-amber-700 dark:text-amber-300', - summary: 'Credits exhausted — connect API key', - title: 'Quickstart credits exhausted. Connect your API key to continue using AI Patrol.', + summary: 'Patrol quickstart exhausted', + title: 'Patrol quickstart is exhausted. Connect your API key to continue using AI Patrol.', }); }); }); diff --git a/frontend-modern/src/utils/__tests__/selfHostedPlans.test.ts b/frontend-modern/src/utils/__tests__/selfHostedPlans.test.ts index d3af43f03c..cd1d29677c 100644 --- a/frontend-modern/src/utils/__tests__/selfHostedPlans.test.ts +++ b/frontend-modern/src/utils/__tests__/selfHostedPlans.test.ts @@ -35,7 +35,8 @@ describe('selfHostedPlans', () => { it('keeps shared self-hosted commercial copy in the common contract', () => { expect(SELF_HOSTED_COMMERCIAL_PRESENTATION).toEqual({ pageTitle: 'Pricing', - pageDescription: 'Compare tiers and choose what fits.', + pageDescription: + 'Core monitoring stays free. Relay adds remote access, and Pro unlocks AI operations.', mostPopularBadge: 'Most Popular', currentPlanLabel: 'Current Plan', includedLabel: 'Included', @@ -64,4 +65,10 @@ describe('selfHostedPlans', () => { ], }); }); + + it('keeps Community quickstart copy scoped to Patrol activation support', () => { + expect(SELF_HOSTED_PLAN_BY_TIER.community.highlights).toEqual( + expect.arrayContaining(['AI Patrol (BYOK)', 'Patrol quickstart: 25 runs, no API key']), + ); + }); }); diff --git a/frontend-modern/src/utils/aiQuickstartPresentation.ts b/frontend-modern/src/utils/aiQuickstartPresentation.ts index 38b3eeb483..46efa36aaa 100644 --- a/frontend-modern/src/utils/aiQuickstartPresentation.ts +++ b/frontend-modern/src/utils/aiQuickstartPresentation.ts @@ -12,15 +12,15 @@ export function getAIQuickstartCreditsPresentation( return { className: 'bg-blue-50 dark:bg-blue-950 border-blue-200 dark:border-blue-800 text-blue-700 dark:text-blue-300', - summary: `${remaining}/${total} quickstart credits`, - title: `${remaining}/${total} free quickstart patrol runs remaining. No API key needed.`, + summary: `Patrol quickstart: ${remaining}/${total} runs left`, + title: `${remaining} of ${total} free Patrol quickstart runs remaining. No API key needed for Patrol.`, }; } return { className: 'bg-amber-50 dark:bg-amber-950 border-amber-200 dark:border-amber-800 text-amber-700 dark:text-amber-300', - summary: 'Credits exhausted — connect API key', - title: 'Quickstart credits exhausted. Connect your API key to continue using AI Patrol.', + summary: 'Patrol quickstart exhausted', + title: 'Patrol quickstart is exhausted. Connect your API key to continue using AI Patrol.', }; } diff --git a/frontend-modern/src/utils/selfHostedPlans.ts b/frontend-modern/src/utils/selfHostedPlans.ts index 8cb357fe48..fa37ec5971 100644 --- a/frontend-modern/src/utils/selfHostedPlans.ts +++ b/frontend-modern/src/utils/selfHostedPlans.ts @@ -51,7 +51,8 @@ export const SELF_HOSTED_PLAN_DEFINITIONS: readonly SelfHostedPlanDefinition[] = highlights: [ 'Real-time monitoring', '7-day metric history', - 'AI Patrol (BYOK + 25 quickstart credits)', + 'AI Patrol (BYOK)', + 'Patrol quickstart: 25 runs, no API key', 'Update alerts', 'Basic SSO (OIDC)', 'Community support', @@ -111,7 +112,7 @@ export const SELF_HOSTED_PLAN_BY_TIER: Record