Status: 104 capabilities — 20 GA, 84 Beta. Enforcement is monitor-only by default. Honest status →

USCP Go-Live Runbook

The single, repeatable path from "we have the package" to "production, signed off." Follow it top to bottom. Each phase has an explicit exit gate — do not proceed until the gate passes.

Audience: the customer's platform/infra team deploying USCP on their own cloud or on-prem estate. Everything here is self-contained: no dependency on any DoubleLogic-hosted service in the default (air-gap, self-signed license) mode.


Phase 0 — Decisions (10 minutes)

Decision Options Default / recommendation
EnvironmentAWS · Azure · GCP · on-prem VM (VMware/Proxmox/bare) · Kubernetes · air-gappedPick one guide in this folder
ProvisioningTerraform (deploy/terraform/) · one-command deploy/install.sh · Helm (deploy/helm/uscp)Terraform for cloud/on-prem VM; Helm for a cluster
Databasecontainer (bundled) · native package · external managed (RDS/Azure DB/Cloud SQL)External managed for production; must use a non-superuser role (RLS)
Licensing modeairgap (self-signed, self-contained) · online (calls the vendor license server)airgap unless you have an install key + reachable license server
Login methodemail one-time-code (needs SMTP) · authenticator/TOTP · SSO (OIDC/SAML)email OTP if you have SMTP; else TOTP or SSO
TLSCaddy + Let's Encrypt (auto) · internal CA / load-balancerAuto for public; internal CA for air-gap
Single-deployment bindingon (host-locked license) · offOn in production (USCP_REQUIRE_HOST_BINDING=true)

Phase 1 — Pre-flight (exit gate: all boxes ticked)

  • [ ] Target host/cluster meets sizing: 2 vCPU / 4 GB / 20 GB minimum (more for production load).
  • [ ] PostgreSQL 16 reachable, with a database and a non-superuser role (superusers bypass

row-level security — see post-install.md → Database hardening).

  • [ ] DNS: an A/AAAA record for your hostname points at the server's public IP (public TLS only).
  • [ ] Secrets ready: USCP_DATA_KEY (32-byte hex — the installer generates one if unset), DB

credentials, and (if online) USCP_INSTALL_KEY.

  • [ ] Login prerequisite ready: an SMTP relay (email OTP) or a TOTP plan or an **OIDC/SAML

IdP** app registration with redirect https://<host>/auth/callback.

  • [ ] For online licensing: outbound HTTPS to your license server is allowed, and the server is

live and will issue a license for this deployment.

  • [ ] For air-gap: you can transfer the signed bundle in, or let the installer self-sign one.

Phase 2 — Deploy

Follow the guide for your environment (each is copy-paste accurate against the shipped tooling):

kubernetes.md · airgap.md

The one-command path (sudo ./deploy/install.sh with your USCP_* vars, or interactive) will: build the static binary, provision/point at Postgres, run all migrations, generate or install the license (host-bound), configure the login method (verifying SMTP if chosen), obtain TLS, start the controlplane systemd service, then run the acceptance suite automatically (Phase 3).

Exit gate: the service is running (systemctl status controlplane, or pods Ready on Kubernetes).


Phase 3 — Verify (automated acceptance)

The installer runs this for you; run it yourself any time to re-confirm health:

deploy/verify-deployment.sh https://<your-host>     # or http://127.0.0.1:8080 locally

It checks, and must PASS: liveness/readiness · Prometheus metrics · license active & not expired · OpenAPI contract · capability discovery · embedded admin console served · auth surface responds · every licensed route rejects anonymous callers (401/403) · TLS reachable (https URL).

Exit gate: verify-deployment.sh exits 0 ("DEPLOYMENT ACCEPTED"). Resolve any FAIL; review every WARN (e.g. "configure SMTP", "front with TLS") before go-live.


Phase 4 — First login & bootstrap admin

The installer provisions the first admin automatically (no manual SQL). Complete first login per post-install.md → First login:

  • Email OTP: browse to https://<host>, enter the admin email, retrieve the one-time code from

the inbox, sign in, enrol a passkey/MFA.

  • TOTP: scan the otpauth:// URI the installer printed; sign in with username + 6-digit code.
  • SSO: click the SSO button; the configured USCP_BOOTSTRAP_ADMIN email is elevated to admin

on first login.

Exit gate: you are signed in as an admin and can see the console.


Phase 5 — Production acceptance sign-off

Tick each before declaring go-live:

  • [ ] verify-deployment.sh passes over HTTPS (0 failures).
  • [ ] Admin login works end-to-end via your chosen method; MFA enrolled.
  • [ ] Licensing: /v1/licensing/status shows state: active, the expected modules, and a sensible

not_after. (Single-deployment: the license is bound to this host — controlplane fingerprint.)

  • [ ] Database runs as a non-superuser; RLS isolation verified

(GET /v1/_audit/verify per tenant, or the isolation test in staging).

  • [ ] Backups configured and a restore drill rehearsed (deploy/helm/uscp/backup-scripts/ or

pg-backup.sh/pg-restore.sh; see post-install.md).

  • [ ] Monitoring: /metrics scraped by Prometheus; alerting rules loaded

(Helm monitoring.prometheusRule.enabled=true).

  • [ ] One real workflow exercised for your use case (e.g. enrol an estate target and open a brokered

session, or ingest an event and run a detection).

  • [ ] TLS certificate valid and auto-renewing; HTTP→HTTPS redirect in place.

Phase 6 — Enterprise assurance evidence (as your procurement requires)

These are proof artifacts, produced against your running deployment — not code changes. Run the shipped tooling in a staging replica:

  • Performance: k6 load profiles under qa/ → p95/p99 latency + throughput evidence.
  • Resilience / DR: chaos + backup-restore drills (qa/chaos/, restore drill) → RTO/RPO evidence.
  • Supply chain: make reproducible-build, cosign-sign the release binary + image, ship the

SBOM/CBOM (sbom.cdx.json/cbom.cdx.json) and VEX.

  • Accessibility: manual screen-reader pass (NVDA/JAWS/VoiceOver) against the console.

Keep the outputs with this runbook as your go-live evidence pack.


Phase 7 — Day-2 operations

  • Upgrade (zero-downtime): deploy the new image/binary; the app fails readiness first, drains

in-flight, then restarts (Helm RollingUpdate maxUnavailable:0 + PDB). Signed module updates via the console Licensing → Signed updates (expand-contract migrations).

  • License renewal: replace the air-gap bundle (or let online refresh); the console Licensing

page shows an expiry countdown. Anti-rollback + host-binding remain enforced.

  • Backups: verify the scheduled job; periodically re-run the restore drill.
  • Rotation: USCP_DATA_KEY rotation via USCP_DATA_KEYS_RETIRED (see fieldcrypto docs);

service-token and credential rotation from the console.


Troubleshooting quick reference

Symptom Likely cause / fix
verify licensing FAILexpired/degraded license, or online server unreachable → check /v1/licensing/status; for air-gap re-import the bundle
login returns 503 EMAIL_OTP_UNAVAILABLESMTP not configured → set USCP_SMTP_*, or switch to TOTP/SSO
readyz not greenDB unreachable or migrations pending → check DATABASE_URL, controlplane migrate
TLS not answeringDNS still propagating or cert issuing → wait, re-check https://<host>/healthz
host-bound license refusedwrong host → run controlplane fingerprint on this host and request a license bound to it
SSO disabled warningexpected until USCP_OIDC_ISSUER is set; local/TOTP still work