From c17664b3daa128c7c459dff7d5d489dc5827ca99 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 11 Aug 2026 14:48:01 +0100 Subject: [PATCH] Distinguish MSP evaluation issuance from readiness --- deploy/provider-msp/setup.sh | 2 +- docs/MSP.md | 20 ++++++++----------- .../v6/internal/subsystems/cloud-paid.md | 11 ++++++++-- .../subsystems/deployment-installability.md | 17 ++++++++++++---- frontend-modern/public/docs/MSP.md | 20 ++++++++----------- .../installtests/provider_msp_deploy_test.go | 5 +++-- 6 files changed, 42 insertions(+), 33 deletions(-) diff --git a/deploy/provider-msp/setup.sh b/deploy/provider-msp/setup.sh index 96f04b890..4c102e441 100755 --- a/deploy/provider-msp/setup.sh +++ b/deploy/provider-msp/setup.sh @@ -402,7 +402,7 @@ ensure_eval_license() { --arg public_key "${public_key}" \ --arg email "${PULSE_PROVIDER_MSP_EVAL_EMAIL}" \ --arg signup_source "${PULSE_PROVIDER_MSP_SIGNUP_SOURCE}" \ - '{entitlement_signing_public_key: $public_key} + '{entitlement_signing_public_key: $public_key, setup_stage: "images_ready"} + (if $email == "" then {} else {email: $email} end) + (if $signup_source == "" then {} else {signup_source: $signup_source} end)')" response="$(curl -fsS --max-time 20 \ diff --git a/docs/MSP.md b/docs/MSP.md index 0b6c6dcca..1fc2cb69b 100644 --- a/docs/MSP.md +++ b/docs/MSP.md @@ -293,15 +293,12 @@ sha256sum -c "${PULSE_MSP_BUNDLE}.sha256" tar -xzf "${PULSE_MSP_BUNDLE}" cd "pulse-provider-msp-${PULSE_VERSION}" -export PULSE_PROVIDER_MSP_EVAL_EMAIL=you@example.com -export PULSE_PROVIDER_MSP_SIGNUP_SOURCE=msp_docs sudo -E bash ./setup.sh ``` -`PULSE_PROVIDER_MSP_EVAL_EMAIL` is optional. Set it if you want setup help and -want an eventual paid upgrade matched to this deployment; omit it for an -anonymous evaluation. The signup-source value is a fixed attribution label, -not free-form telemetry. +The v6.2.1 evaluation request is anonymous. If you want setup help, start from +the [Pulse MSP evaluation page](https://pulserelay.pro/msp.html#evaluate) first; +that contact request remains separate from the licence activation. The host needs Ubuntu 24.04 or similar, a domain you can point at it, and ports 80 and 443 free. Install `curl`, `openssh-client`, `coreutils`, and `tar` @@ -311,12 +308,11 @@ Traefik dnsChallenge provider works by setting `ACME_DNS_PROVIDER` in `.env` and putting that provider's credential variables in `dns-credentials.env`. Leave `CP_PROVIDER_MSP_LICENSE_FILE` blank and `setup.sh` self-issues a -2-client evaluation licence after configuration validation succeeds and the -immutable images are reachable. It sends only the public half of the signing -key generated on your host, the optional contact address above, and the fixed -signup-source label. The private key, client inventory, and credentials never -leave the machine. You can then onboard two real clients and confirm the -isolation boundary on your own infrastructure before buying. +2-client evaluation licence. The v6.2.1 bundle sends only the public half of +the signing key generated on your host. The private key, client inventory, +credentials, and contact details never leave the machine. You can then onboard +two real clients and confirm the isolation boundary on your own infrastructure +before buying. The evaluation licence lasts 60 days and re-running `setup.sh` reuses the one already on disk. On an air-gapped host set diff --git a/docs/release-control/v6/internal/subsystems/cloud-paid.md b/docs/release-control/v6/internal/subsystems/cloud-paid.md index e59f18c87..35356c6ba 100644 --- a/docs/release-control/v6/internal/subsystems/cloud-paid.md +++ b/docs/release-control/v6/internal/subsystems/cloud-paid.md @@ -3323,8 +3323,9 @@ The provider-hosted MSP acquisition funnel uses commercial records that already belong to the boundary rather than reviving retired generic browser analytics or adding client-workspace phone-home. An explicit landing-page evaluation start is delivered to the support inbox with the fixed `msp_landing` source; a -persisted `msp_eval` licence is the activation stage and is issued only after -provider setup validates configuration and resolves the immutable images; a +persisted `msp_eval` licence is the evaluation-issuance stage, and becomes a +ready-host activation only when its audit carries the bounded `images_ready` +stage after provider setup validates configuration and resolves the immutable images; a persisted `msp_starter`, `msp_growth`, or `msp_scale` licence is an assisted licence stage, not proof of payment by itself. Administrative issuance must record a bounded reason, and only the first `paid_conversion` reason may enter @@ -3335,6 +3336,12 @@ evaluation licence ID may join an assisted upgrade to its activation, but no stage may collect client inventory, credentials, private signing material, or free-form runtime telemetry. Anonymous evaluation must remain available. +The activation interpretation requires the bounded `setup_stage=images_ready` +audit value. An evaluation without that value, including issuance from the +published v6.2.1 bundle, remains an issued evaluation rather than a proved +ready-host activation. Funnel aggregation must report both values separately +and must not infer readiness from plan version, contactability, or source. + Provider-hosted Starter remains assisted even though the signed evaluation bundle is now public. A generic recurring Stripe checkout is not a valid substitute: the provider licence is a static offline file, so self-service diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index 90353c2e5..85c869d3a 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -3314,10 +3314,11 @@ unlicensed control plane starts, but release-build client runtimes reject its unchained entitlement leases and the client workspaces run without the capabilities being evaluated. -Evaluation issuance happens only after setup has validated the operator -configuration and confirmed that every immutable provider image is reachable. -That ordering makes a stored `msp_eval` issue an activation signal rather than -a download-intent signal created before the install can succeed. Setup may +Current setup code issues an evaluation only after validating the operator +configuration and confirming that every immutable provider image is reachable, +and records the fixed `setup_stage=images_ready` value. That combination makes +the stored `msp_eval` row an activation signal rather than a download-intent +signal created before the install can succeed. Setup may include an optional evaluator email and a fixed signup-source label so support can match an assisted paid upgrade to the deployment. Those fields must remain optional and bounded; the request must never contain client inventory, @@ -3325,6 +3326,14 @@ credentials, private keys, or free-form runtime telemetry. The returned public evaluation licence ID may be carried in the upgrade URL as a non-secret correlation key. +That ordering and attribution contract applies only when the request carries +the fixed `setup_stage=images_ready` marker. The immutable v6.2.1 provider +bundle predates the marker and requests its evaluation earlier, so its stored +`msp_eval` row proves issuance only, not install readiness, and remains +anonymous. Public v6.2.1 guidance must state that exact behavior. It must not +claim automatic contact correlation or post-image-ready activation until a +newer signed bundle containing the contract is published. + Self-issue must degrade rather than block. A missing signing key, an unreachable licence server, or a response carrying no licence leaves the install unlicensed with an explicit warning, and `PULSE_PROVIDER_MSP_SKIP_EVAL_LICENSE` diff --git a/frontend-modern/public/docs/MSP.md b/frontend-modern/public/docs/MSP.md index 0b6c6dcca..1fc2cb69b 100644 --- a/frontend-modern/public/docs/MSP.md +++ b/frontend-modern/public/docs/MSP.md @@ -293,15 +293,12 @@ sha256sum -c "${PULSE_MSP_BUNDLE}.sha256" tar -xzf "${PULSE_MSP_BUNDLE}" cd "pulse-provider-msp-${PULSE_VERSION}" -export PULSE_PROVIDER_MSP_EVAL_EMAIL=you@example.com -export PULSE_PROVIDER_MSP_SIGNUP_SOURCE=msp_docs sudo -E bash ./setup.sh ``` -`PULSE_PROVIDER_MSP_EVAL_EMAIL` is optional. Set it if you want setup help and -want an eventual paid upgrade matched to this deployment; omit it for an -anonymous evaluation. The signup-source value is a fixed attribution label, -not free-form telemetry. +The v6.2.1 evaluation request is anonymous. If you want setup help, start from +the [Pulse MSP evaluation page](https://pulserelay.pro/msp.html#evaluate) first; +that contact request remains separate from the licence activation. The host needs Ubuntu 24.04 or similar, a domain you can point at it, and ports 80 and 443 free. Install `curl`, `openssh-client`, `coreutils`, and `tar` @@ -311,12 +308,11 @@ Traefik dnsChallenge provider works by setting `ACME_DNS_PROVIDER` in `.env` and putting that provider's credential variables in `dns-credentials.env`. Leave `CP_PROVIDER_MSP_LICENSE_FILE` blank and `setup.sh` self-issues a -2-client evaluation licence after configuration validation succeeds and the -immutable images are reachable. It sends only the public half of the signing -key generated on your host, the optional contact address above, and the fixed -signup-source label. The private key, client inventory, and credentials never -leave the machine. You can then onboard two real clients and confirm the -isolation boundary on your own infrastructure before buying. +2-client evaluation licence. The v6.2.1 bundle sends only the public half of +the signing key generated on your host. The private key, client inventory, +credentials, and contact details never leave the machine. You can then onboard +two real clients and confirm the isolation boundary on your own infrastructure +before buying. The evaluation licence lasts 60 days and re-running `setup.sh` reuses the one already on disk. On an air-gapped host set diff --git a/scripts/installtests/provider_msp_deploy_test.go b/scripts/installtests/provider_msp_deploy_test.go index 8fe198e36..05222822f 100644 --- a/scripts/installtests/provider_msp_deploy_test.go +++ b/scripts/installtests/provider_msp_deploy_test.go @@ -363,6 +363,7 @@ func TestProviderMSPSetupScriptSupportsUnlicensedEvaluation(t *testing.T) { "could not reach the license server", "PULSE_PROVIDER_MSP_EVAL_EMAIL", "PULSE_PROVIDER_MSP_SIGNUP_SOURCE", + `setup_stage: "images_ready"`, "eval_license_id=", ) if strings.LastIndex(script, "pull_provider_images\n") > strings.LastIndex(script, "ensure_eval_license\n") { @@ -429,8 +430,8 @@ func TestProviderMSPEvaluationDocsUsePublishedSignedBundle(t *testing.T) { "ssh-keygen -Y verify", `-s "${PULSE_MSP_BUNDLE}.sshsig" < "${PULSE_MSP_BUNDLE}"`, `sha256sum -c "${PULSE_MSP_BUNDLE}.sha256"`, - `PULSE_PROVIDER_MSP_EVAL_EMAIL=you@example.com`, - `PULSE_PROVIDER_MSP_SIGNUP_SOURCE=msp_docs`, + "The v6.2.1 evaluation request is anonymous", + "contact request remains separate from the licence activation", `sudo -E bash ./setup.sh`, ) assertNotContainsAny(t, doc,