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 |
|---|---|---|
| Environment | AWS · Azure · GCP · on-prem VM (VMware/Proxmox/bare) · Kubernetes · air-gapped | Pick one guide in this folder |
| Provisioning | Terraform (deploy/terraform/) · one-command deploy/install.sh · Helm (deploy/helm/uscp) | Terraform for cloud/on-prem VM; Helm for a cluster |
| Database | container (bundled) · native package · external managed (RDS/Azure DB/Cloud SQL) | External managed for production; must use a non-superuser role (RLS) |
| Licensing mode | airgap (self-signed, self-contained) · online (calls the vendor license server) | airgap unless you have an install key + reachable license server |
| Login method | email one-time-code (needs SMTP) · authenticator/TOTP · SSO (OIDC/SAML) | email OTP if you have SMTP; else TOTP or SSO |
| TLS | Caddy + Let's Encrypt (auto) · internal CA / load-balancer | Auto for public; internal CA for air-gap |
| Single-deployment binding | on (host-locked license) · off | On 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):
- aws.md · azure.md · gcp.md · onprem-vm.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_ADMINemail is elevated toadmin
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.shpasses over HTTPS (0 failures). - [ ] Admin login works end-to-end via your chosen method; MFA enrolled.
- [ ] Licensing:
/v1/licensing/statusshowsstate: 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:
/metricsscraped 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_KEYrotation viaUSCP_DATA_KEYS_RETIRED(see fieldcrypto docs);
service-token and credential rotation from the console.
Troubleshooting quick reference
| Symptom | Likely cause / fix |
|---|---|
verify licensing FAIL | expired/degraded license, or online server unreachable → check /v1/licensing/status; for air-gap re-import the bundle |
login returns 503 EMAIL_OTP_UNAVAILABLE | SMTP not configured → set USCP_SMTP_*, or switch to TOTP/SSO |
readyz not green | DB unreachable or migrations pending → check DATABASE_URL, controlplane migrate |
| TLS not answering | DNS still propagating or cert issuing → wait, re-check https://<host>/healthz |
| host-bound license refused | wrong host → run controlplane fingerprint on this host and request a license bound to it |
| SSO disabled warning | expected until USCP_OIDC_ISSUER is set; local/TOTP still work |