mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 10:33:29 +00:00
Two mandatory round-trips stood between an interested MSP and their first screen, and neither was technical. setup.sh required four image digests shipped as literal <pin> placeholders, so the only way to get them was to ask. All four images are publicly readable, so there was never anything to hand out. setup.sh now resolves each blank pin to an immutable digest from its published tag via buildx imagetools and writes it back to .env; hand-set values are left alone. setup.sh then died outright without a licence file, so nobody could start the stack, create a workspace, or see the portal until a human minted a licence for them. The control plane already ran unlicensed via ProviderMSPPlanSourceEnvFallback; only the installer refused. A licence path that is set but missing is still a hard failure, since that is a misconfiguration rather than a choice. Unlicensed now means evaluation rather than the cheapest paid tier. The env fallback defaulted to msp_starter, handing every unlicensed deployment the full 5-client Starter allowance and leaving no boundary between evaluating and buying. Adds msp_eval at 2 workspaces: same capabilities, smaller cap, not purchasable, not on the public ladder. An isolation guarantee is the one claim a provider cannot evaluate from a screenshot, and both MSP leads this year went quiet at exactly this step. Contracts: cloud-paid records the unlicensed plan rule and the strictly-below-paid invariant; deployment-installability records credential-free, correspondence-free installability. Verification: TestMSPEvalCapStaysBelowCheapestPaidTier, TestCanonicalizePlanVersion_MSPEval, TestProviderMSPSetupScriptSupportsUnlicensedEvaluation. The last was negative-tested by reintroducing a <pin> placeholder and confirming it fails. ensure_image_pins exercised against the live registries. licensing, cloudcp, control-plane and installtests all green.
149 lines
6.3 KiB
Text
149 lines
6.3 KiB
Text
# Domain
|
|
# Client runtimes are routed as https://<client-id>.${DOMAIN}/.
|
|
# Provider-hosted MSP: use the MSP's own domain, for example pulse.example-msp.com.
|
|
# Pulse-hosted MSP: use a Pulse-operated provider namespace, for example example-msp.msp.pulserelay.pro, and set CP_CONTROL_PLANE_MODE=pulse_hosted_msp.
|
|
DOMAIN=msp.example.com
|
|
ACME_EMAIL=admin@example.com
|
|
|
|
# Cloudflare DNS-01 wildcard TLS
|
|
CF_DNS_API_TOKEN=
|
|
|
|
# Image pins. Leave blank and setup.sh resolves each one to an immutable
|
|
# digest from its published tag, then writes the digest back here. All four
|
|
# images are public, so this needs no registry credentials.
|
|
#
|
|
# Pin them by hand instead if you want to hold a specific build; setup.sh
|
|
# leaves any value you set alone.
|
|
TRAEFIK_IMAGE=
|
|
DOCKER_SOCKET_PROXY_IMAGE=
|
|
CONTROL_PLANE_IMAGE=
|
|
CP_PULSE_IMAGE=
|
|
|
|
# Control plane
|
|
CP_ENV=production
|
|
CP_CONTROL_PLANE_MODE=provider_hosted_msp
|
|
CP_ADMIN_KEY=
|
|
PULSE_PROVIDER_MSP_DATA_DIR=/data
|
|
PULSE_PROVIDER_MSP_DOCKER_NETWORK=pulse-provider-msp
|
|
PULSE_PROVIDER_MSP_DOCKER_SUBNET=172.30.0.0/24
|
|
PULSE_PROVIDER_MSP_DOCKER_SOCKET=/var/run/docker.sock
|
|
PULSE_PROVIDER_MSP_ROOT_SPACECHECK_DIR=/var/lib/pulse-provider-msp/spacecheck/root
|
|
PULSE_PROVIDER_MSP_DOCKER_SPACECHECK_DIR=/var/lib/docker/.pulse-provider-msp-spacecheck
|
|
CP_TRUSTED_PROXY_CIDRS=172.30.0.0/24
|
|
|
|
# Licence file. Leave BLANK to evaluate: the control plane runs on msp_eval,
|
|
# which is the full product capped at 2 client workspaces, so you can stand
|
|
# this up and onboard two real clients without talking to anyone first.
|
|
#
|
|
# Set it to your licence path once you buy. Paid client caps come from the
|
|
# licence, never from this file.
|
|
CP_PROVIDER_MSP_LICENSE_FILE=
|
|
# Entitlement lease signing key. setup.sh generates this; the private key
|
|
# never leaves this host. Your provider MSP license must bind the derived
|
|
# PUBLIC key (entitlement_signing_public_key); print it with
|
|
# `./setup.sh --print-lease-signing-public-key` and include it in your
|
|
# license request. The control plane refuses to start if license and key
|
|
# do not match.
|
|
CP_ENTITLEMENT_SIGNING_PRIVATE_KEY=
|
|
CP_TENANT_MEMORY_LIMIT=536870912
|
|
CP_ALLOW_DOCKERLESS_PROVISIONING=false
|
|
CP_STORAGE_GUARDRAILS_ENABLED=true
|
|
CP_STORAGE_MIN_ROOT_AVAILABLE=10GiB
|
|
CP_STORAGE_MIN_DATA_AVAILABLE=5GiB
|
|
CP_STORAGE_MIN_DOCKER_AVAILABLE=10GiB
|
|
CP_STORAGE_MAX_DOCKER_BUILD_CACHE=2GiB
|
|
CP_PROOF_TENANT_MAX_AGE=24h
|
|
CP_PROOF_TENANT_MATCHERS=proof,canary,rehearsal,msp_prod,ownerseed,owner_seed
|
|
|
|
# Email is optional. Without RESEND_API_KEY the portal cannot send sign-in
|
|
# links or invitation emails; instead, print one-time links on this host:
|
|
# docker compose run --rm control-plane provider-msp bootstrap \
|
|
# --account-name "Example MSP" --owner-email owner@example.com # owner link
|
|
# docker compose run --rm control-plane provider-msp portal-link \
|
|
# --email teammate@example.com # teammate link
|
|
# The portal sign-in page shows the same instructions in this state.
|
|
# Portal sessions last 7 days (CP_SESSION_TTL to change).
|
|
# Set CP_REQUIRE_EMAIL_PROVIDER=true once transactional email is configured.
|
|
CP_REQUIRE_EMAIL_PROVIDER=false
|
|
RESEND_API_KEY=
|
|
PULSE_EMAIL_FROM=noreply@example.com
|
|
PULSE_EMAIL_REPLY_TO=support@example.com
|
|
|
|
# Fresh host setup:
|
|
# sudo -E ./setup.sh
|
|
#
|
|
# Optional one-command setup proof after .env is filled:
|
|
# sudo -E PULSE_PROVIDER_MSP_ACCOUNT_NAME="Example MSP" \
|
|
# PULSE_PROVIDER_MSP_OWNER_EMAIL=owner@example.com \
|
|
# ./setup.sh
|
|
#
|
|
# First owner bootstrap:
|
|
# docker compose run --rm control-plane provider-msp bootstrap \
|
|
# --account-name "Example MSP" \
|
|
# --owner-email owner@example.com
|
|
#
|
|
# Install readiness check:
|
|
# docker compose run --rm control-plane provider-msp preflight
|
|
#
|
|
# Non-mutating operational status:
|
|
# docker compose run --rm control-plane provider-msp status
|
|
#
|
|
# Pre-upgrade / pre-maintenance status gate. This fails unless a verified
|
|
# provider MSP backup archive is available:
|
|
# docker compose run --rm control-plane provider-msp status --require-backup
|
|
#
|
|
# Pre-upgrade / pre-maintenance plan. This is non-mutating and does not pull
|
|
# images:
|
|
# ./upgrade.sh --dry-run
|
|
#
|
|
# Apply a provider control-plane upgrade after updating the image pins in this
|
|
# file. The runner creates and verifies a fresh backup, dry-runs restore into a
|
|
# separate target data dir, pulls provider images, starts Traefik/control-plane,
|
|
# and prints the tenant runtime rollout plan for CP_PULSE_IMAGE:
|
|
# ./upgrade.sh
|
|
#
|
|
# Also roll all client runtimes to the configured CP_PULSE_IMAGE line after the
|
|
# provider services are updated:
|
|
# ./upgrade.sh --rollout-tenants
|
|
#
|
|
# Fresh-install provider proof. This bootstraps the owner account, checks install
|
|
# readiness, creates two proof client workspaces, verifies tenant-local agent
|
|
# ingest and portal handoff, creates and verifies a backup, dry-runs restore and
|
|
# recovery, then removes the proof workspaces.
|
|
# ./run-install-proof.sh \
|
|
# --account-name "Example MSP" \
|
|
# --owner-email owner@example.com
|
|
#
|
|
# Equivalent one-off command:
|
|
# docker compose run --rm control-plane provider-msp install-proof \
|
|
# --account-name "Example MSP" \
|
|
# --owner-email owner@example.com
|
|
#
|
|
# Plan recovery for failed, stuck provisioning, or unhealthy client workspaces:
|
|
# docker compose run --rm control-plane provider-msp recover --all-degraded --dry-run
|
|
#
|
|
# Execute recovery after reviewing the dry-run:
|
|
# docker compose run --rm control-plane provider-msp recover --all-degraded
|
|
#
|
|
# Recovery backup before upgrades or recovery drills:
|
|
# docker compose run --rm control-plane provider-msp backup create
|
|
#
|
|
# Verify a recovery archive before relying on it:
|
|
# docker compose run --rm control-plane provider-msp backup verify \
|
|
# /data/backups/provider-msp/provider-msp-backup-YYYYMMDDTHHMMSSZ.tar.gz
|
|
#
|
|
# Recovery dry-run on a fresh target data directory:
|
|
# docker compose run --rm control-plane provider-msp backup restore \
|
|
# /data/backups/provider-msp/provider-msp-backup-YYYYMMDDTHHMMSSZ.tar.gz \
|
|
# --target-data-dir /data-restore-drill \
|
|
# --dry-run
|
|
#
|
|
# Workspace/runtime proof. This creates two proof client workspaces, generates
|
|
# client-bound agent install tokens, verifies tenant-local agent report ingest,
|
|
# and exercises the portal handoff path. The broader install-proof command above
|
|
# wraps this with preflight, backup, restore dry-run, recovery dry-run, cleanup,
|
|
# and status checks.
|
|
# docker compose run --rm control-plane provider-msp proof \
|
|
# --account-name "Example MSP" \
|
|
# --owner-email owner@example.com \
|
|
# --cleanup
|