Exercise two provider installations with the same tenant ID, unowned-network collision and cleanup isolation. Gate the release-line push before packet freeze and repeat against digest-verified candidate images during container qualification. Update the deployment-installability contract and pin both workflow gates in the release-asset verification suite.
Change-source: pulse-maintainer
Only remove a managed client network with the expected name and tenant labels. Select attached support containers by role and provider ingress before forced disconnect, avoiding cross-provider mutations on shared Docker hosts. Update both cloud-paid and deployment-installability contracts with the regression boundary.
Change-source: pulse-maintainer
Require exact tenant and runtime labels before adopting an existing network, share that check with support-container recovery, and cover wrong or absent labels. Update the cloud-paid and deployment contracts to state the provisioning boundary.
Change-source: pulse-maintainer
Reconcile PR #2216 with current main and retain the paid-plan contracts. Reattach support containers to active tenant networks at monitor startup and before checks, and restart unhealthy clients rather than stopping them under Docker unless-stopped.
Require exact tenant-network ownership labels and select support containers on the configured provider ingress network to preserve isolation. Cover startup recovery, restart behaviour, missing networks and wrong-owner networks.
Change-source: pulse-maintainer
provider-msp status, which upgrade.sh and the install proof gate on,
runs in a one-off `docker compose run` container attached only to the
provider ingress network. Its health check dialled each client's
container on the client's isolated network, which that container cannot
reach, and an unreachable client counts as unhealthy. So on any install
with a real client, status failed with "unhealthy active workspaces" and
./upgrade.sh --dry-run exited 1: the backup-gated upgrade path refused to
run as soon as a provider onboarded anyone. CI proofs never saw it
because they only ever run against an empty install. Reproduced on a
fresh v6.4.1 provider bundle with one client whose container Docker
reported healthy.
The runtime image declares a HEALTHCHECK that probes the same
/api/health inside the container, and Docker records the result where
any caller with the socket can read it. Use that when it exists and keep
the HTTP probe for images without one. With the change the same install
reports healthy_tenants=1 and upgrade.sh --dry-run plans cleanly.
Eight new branch-coverage tests taking thirty-one previously unreached
functions from zero to covered, with no source or existing test touched.
internal/kubernetesagent: twenty-one pure report helpers, including the pointer
converters proved non-aliasing in both directions, the ingress host and address
collectors across their trim, dedupe and insertion-order arms, the endpoint
slice readiness count where a nil Ready field counts as ready, and the target
role predicate.
internal/agentexec: the sudo long-option value gate over the real option list
including the inline equals form, and the approval grant verification error
unwrapped through errors.Is.
internal/alerts: the alert config alias normalization across the nil config
guard, the empty threshold early return, the blank type-key continue arm and
the legacy-delete versus supported-keep split, asserting both maps stay
independent.
internal/alerts/specs: the resource incident rollup evidence validation, each
failure arm asserted on its concrete error and the check order pinned when
several fields are invalid at once.
internal/cloudcp/docker: the not-found predicate through a wrapped error, the
route host label precedence, and the Traefik host rule parser across quoting
styles, combined matchers, multiple host clauses and malformed input.
internal/cloudcp/portal: the anonymous bootstrap builder, asserting no tenant
or user identity field is ever populated on the anonymous result.
internal/config: the legacy OIDC environment provider, including the arm where
an already-configured provider is present and the redirect derivation from a
public URL with a trailing slash.
internal/dockeragent: the update-all payload decode across wrong-typed and
missing fields, and the docker filter conversion.
Contract-Neutral: test-only branch coverage, no contract surface touched
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.
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.
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.
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.