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

Unified Security Control Plane (USCP) — Administrator Guide

Audience: the platform/security administrator who operates USCP after it is deployed. This guide assumes no prior knowledge of the product. Every task is a numbered, explicit procedure that tells you what it does, why, the exact console location, the exact API endpoint, the expected result, and how to undo it. A novice admin should be able to run the platform from this document alone.

This guide is not the install guide. Standing up the binary + PostgreSQL + licensing is covered by codes/docs/site/docs/how-to/deploy.md. This document begins at "the service is running and answering GET /healthz."

Accuracy note. Every endpoint, role, entitlement key, request field, and flag in this guide is quoted from the source of truth: codes/ci/entitlements.yaml (the 389-route boundary manifest), codes/web/admin-shell/src/catalog.ts (the console nav + sample bodies), codes/internal/app/authorize.go (roles), and the individual Go handlers under codes/internal/app/ and codes/internal/identity/. Every value in this guide is pinned to source; where a behaviour is environment-supplied (e.g. an IdP's own MFA policy) the guide says so.


Table of contents

  1. Admin overview
  2. Getting started as admin
  3. Tenant administration
  4. Identity & access
  5. Licensing
  6. Governance & trust
  7. Security operations administration
  8. Estate management
  9. Observability & operations
  10. Resilience & business continuity
  11. Routine operations & runbooks
  12. Security hardening checklist
  13. Admin troubleshooting
  14. Complete admin operation reference

Conventions used in every procedure

  • Console path is written Sidebar group → Domain → action. The console is served from

the root URL (https://<your-host>/). Every capability is also reachable from the Command palette (press ⌘K / Ctrl-K) and the generic endpoint runner (see §2.4), so if a purpose-built screen does not exist for a capability, you still operate it from the UI.

  • API call is written METHOD /path with an example JSON body. All examples assume you have

a valid session cookie (uscp_session) or a service-account bearer token (Authorization: Bearer uscp_sa_…). Replace https://<host> with your deployment URL.

  • Entitlement is the license key that gates the route (from entitlements.yaml). If your

license does not include it, the route fails closed with 402/403 before the handler runs.

  • Undo tells you the exact reverse operation. Where an action is irreversible, the guide

says so explicitly and in bold.

  • {id}, {name}, {subject} etc. are path parameters — substitute the real value.

1. Admin overview

1.1 What USCP is, from an operator's chair

USCP is a single self-contained Go binary that embeds a React admin console and talks to one PostgreSQL database. There is no separate web server, no separate app tier, and no message broker to babysit — the binary serves the API, the console SPA, the metrics endpoint, and the background workers (retention, webhook/notification delivery, forward-queue) in-process.

It is a multi-tenant control plane: every row in PostgreSQL is tagged with a tenant and isolated by Row-Level Security (RLS), so one tenant can never read another's data even if a query is wrong. Tenants are created and destroyed through the API (§3).

It exposes 90 capabilities (20 GA, 70 Beta — see codes/docs/capability-matrix.md) across 389 HTTP routes. Each capability is a licensed module; the license you loaded determines which ones answer.

1.2 The console layout

The sidebar is organised into nav groups, and each group contains domains (screens). This is defined in catalog.ts (NAV_GROUPS) and is exactly what you will see:

Nav group Domains (screens)
Identity & AccessIdentity & session, Identity governance (IGA), Tenants
Security OperationsAgentic SOC, Detection hub, CTEM/exposure, Data lake/intel/SOAR, Threat Analytics, Deception, Detonation, Digital Risk
Estate ManagementEstate plane, OT/ICS, Fleet & Provisioning
Governance & TrustPolicy plane, Audit & evidence, Lockbox & HYOK, SLA & private trust, Privacy & DSAR, Hardening & Drift
PlatformLicensing & updates, Integrations, Public & self-serve, System health, Release & Supply Chain, Suite & MSP

Two universal tools sit on top of these screens:

  • Command palette (⌘K) — fuzzy-search any endpoint by name and jump straight to it.
  • Generic endpoint runner — a built-in form that can call any of the 389 routes with a

method, path, and JSON body (pre-filled from the sample in catalog.ts). This is your escape hatch for capabilities that have no dedicated screen — it is a first-class, supported way to operate the platform, not a debug back door.

1.3 Roles — who can do what

Roles are enforced in internal/app/authorize.go. There are four:

Role What it can do Enforced by
adminEverything: tenant lifecycle, credential/token issuance, break-glass setup, RBAC role management, geo-fence, release channels, MSP.requireAdmin → 403 ADMIN_REQUIRED if missing
operatorManagement/write actions (create channels, webhooks, collectors, set log level, IP allowlist, RBAC settings). Cannot do admin-only actions above.requireManage (admin or operator) → 403 MANAGE_REQUIRED
viewerRead-only.Rejected by requireManage/requireAdmin
auditorRead-only, and additionally time-boxed + role-gated for the evidence locker. An auditor only sees the locker while a live grant exists (§6.7).auditor.HasAuditorRole + active grant

Two independent gates protect every privileged action:

  1. Entitlement — does the license include this capability? (e.g. control_plane.shell).
  2. Role — does the caller hold admin/operator? The entitlement control_plane.shell is

held by nearly every role, so it is never sufficient on its own for a destructive or management action; the handler additionally calls requireAdmin/requireManage.

Some sensitive actions add a third gate — step-up re-authentication (§1.5).

1.4 Authentication model

  • Interactive humans log in with SSO (OIDC or SAML) and complete **phishing-resistant

MFA** (WebAuthn passkey, or TOTP/email OTP depending on tenant auth policy). The session is a cookie named uscp_session. Anonymous/password-only access is forbidden by design; local password accounts are an admin-enabled fallback (§4.1).

  • Machines/automation use service-account bearer tokens (uscp_sa_<prefix>_<secret>),

which carry a scoped role and an explicit scope list (§2.5).

  • SSO is multi-tenant. Home-realm discovery (GET /auth/sso?email=…) matches the email domain

to a registered IdP across all tenants and lands the user in the tenant that owns that IdP — not the bootstrap tenant.

  • SCIM 2.0 (/scim/v2/..., bearer-authed) lets your IdP provision and deprovision users.

1.5 Step-up re-authentication (sensitive-action gate)

A small set of high-blast-radius actions require you to have re-authenticated with your passkey within the last 5 minutes (the step-up window). This is a property of your session, not a header you send — there is no X-Step-Up field. If your last passkey re-auth is older than 5 minutes, the call returns HTTP 401 with problem code STEP_UP_REQUIRED ("re-authenticate with your passkey within the step-up window to perform this sensitive action"). Re-authenticate (tap your passkey when the console prompts) and retry — you then have a fresh 5-minute window.

Actions that require step-up (confirmed in source):

  • POST /v1/tenants/{id}/export — bulk data egress
  • DELETE /v1/tenants/{id} — right-to-erasure (also destroys keys)
  • POST /v1/estate/killswitch and POST /v1/estate/killswitch/reset
  • POST /v1/ai/models/{id}/promote and POST /v1/ai/models/{id}/retire

Window length: the step-up freshness window is 5 minutes (stepUpMaxAge = 5 * time.Minute in internal/identity/service.go, enforced by Service.StepUpFresh). Re-running your passkey login resets the clock. There is no configuration flag to change this in the shipped build.

1.6 How entitlements gate everything

Every one of the 389 routes resolves an entitlement decision at the boundary, before the handler runs (entitlements.yaml). A route is exactly one of:

  • entitlement: <key> — fail-closed. No license → the call is rejected.
  • public: "<reason>" — un-gated table-stakes (health probes, licensing posture, break-glass

invoke, SSO bootstrap, the console SPA itself).

You can see your live entitlement posture any time at GET /v1/licensing/status (public) and the full capability list at GET /.well-known/api-capabilities (public).


2. Getting started as admin

2.1 Procedure — become the first administrator

What/why: a brand-new install has no users. USCP mints the first admin automatically so you never run manual SQL.

  1. Confirm the deployment set the environment variable USCP_BOOTSTRAP_ADMIN to your email

(this is done at deploy time; see the deploy guide). This email is JIT-granted admin + operator on its first SSO login into the bootstrap tenant. Every other first-time user gets only operator. The same email in a non-bootstrap tenant is not elevated.

  1. Browse to https://<host>/. You are redirected to your IdP (GET /auth/login).
  2. Authenticate with SSO, then complete MFA.
  3. You land in the console. Confirm your identity and roles: Command palette → "Who am I", or

GET /v1/me → expect {"tenant_id":…,"user_id":…,"email":"you@…","roles":["admin","operator"]}.

  • Expected result: you hold admin; the sidebar shows every nav group your license enables.
  • Undo: to remove your own elevation later, have a second admin edit your role assignment, or

deprovision via SCIM/local-user delete (§4.1). Do not leave USCP_BOOTSTRAP_ADMIN set to a personal address long-term — rotate it to a break-glass identity after onboarding.

Alternative (no IdP yet): if the deployment set USCP_LOCAL_ADMIN_USER / USCP_LOCAL_ADMIN_PASSWORD, you can sign in via POST /auth/local/login and then enable SSO from inside the console. Treat local admin as a bootstrap-only path and disable it once SSO works (§4.1).

2.2 Procedure — enrol your MFA passkey (WebAuthn)

What/why: step-up actions and phishing-resistant login require a passkey.

  1. Console path: top-right user menu → Security → Add passkey (drives the WebAuthn ceremony).
  2. API (the console performs these for you): POST /auth/webauthn/register/begin then

POST /auth/webauthn/register/finish.

  1. Follow the browser/OS prompt (Touch ID, security key, platform authenticator).
  • Expected result: the passkey is bound to your account; future step-up prompts succeed with a

tap. Register a second authenticator (spare) so you are never locked out of step-up.

  • Undo: remove a passkey from the same Security screen.

2.3 Procedure — enrol TOTP (alternative MFA)

  1. POST /v1/iam/totp/enroll with body {"username":"you@example.com"} → returns `{"secret":…,

"otpauth_uri":"otpauth://totp/USCP:…"}`.

  1. Scan the otpauth_uri into your authenticator app.
  2. POST /v1/iam/totp/verify with {"username":"you@example.com","code":"123456"}

{"verified":true,"enabled":true}.

  • Expected result: TOTP is enabled for that account.
  • Undo: re-enrol (a fresh enroll resets totp_enabled=false until re-verified) or delete/rotate

the account.

2.4 Procedure — navigate the console and command palette

  1. Sidebar — pick a nav group, then a domain screen.
  2. Command palette — press ⌘K/Ctrl-K, type part of an operation name (e.g. "kill switch",

"auditor grant"), press Enter to jump to it.

  1. Generic endpoint runner — open any domain, choose an endpoint, edit the pre-filled JSON

body, and Run. This calls the real API with your session and shows the raw response — use it for any capability without a bespoke screen.

  • Expected result: you can reach all 389 routes without leaving the browser.

2.5 Procedure — mint, scope, and rotate a service-account token

What/why: automation (CI, SIEM pollers, scripts) authenticates with a bearer token instead of a human session. Tokens carry a role and a scope list (which entitlement keys they may reach).

Console path: Identity & Access → Identity & session → Service tokens (or command palette "service token"). Entitlement: control_plane.shell. Role: admin (requireAdmin).

  1. MintPOST /v1/iam/service-tokens
   { "name": "siem-poller", "scopes": ["control_plane.siem_hub","control_plane.shell"], "role": "viewer", "expires_days": 90 }
  • scopes defaults to ["*"] (all). role defaults to operator. expires_days: 0 means no

expiry (avoid for production).

  • You cannot mint a token with a role you do not hold, or an admin token as a non-admin →

403 SCOPE_ESCALATION.

  • Response 201 returns the plaintext token once: `{"id":…,"name":…,"prefix":…,

"token":"uscp_sa_…","scopes":[…],"role":"viewer"}`. Copy it now — it is never shown again (only a SHA-256 hash is stored).

  1. Use — send Authorization: Bearer uscp_sa_<prefix>_<secret>. The token may only call routes

whose entitlement key is in its scopes (* = all); anything else → 403 SCOPE_DENIED.

  1. ListGET /v1/iam/service-tokens → metadata only (no secret, no hash).
  2. RotatePOST /v1/iam/service-tokens/{id}/rotate revokes the old secret and returns a new

plaintext token (same name/role/scopes/expiry). Update your automation immediately.

  • Expected result: scoped, auditable machine credentials.
  • Undo / revoke: DELETE /v1/iam/service-tokens/{id} → soft-revokes (revoked_at=now()).

Rotate is itself the safe way to replace a possibly-leaked token.

2.6 Procedure — call the API by hand (curl)

# Public posture (no auth):
curl -s https://<host>/v1/licensing/status
# Authenticated read with a service token:
curl -s https://<host>/v1/me -H "Authorization: Bearer uscp_sa_ab12_…"
# A write:
curl -s -XPOST https://<host>/v1/tenants \
     -H "Authorization: Bearer uscp_sa_ab12_…" -H "Content-Type: application/json" \
     -d '{"slug":"acme","name":"Acme Corp"}'

The machine-readable contract is always available: GET /openapi.yaml (OpenAPI 3.1), GET /v1/openapi.json (for the in-app explorer), GET /asyncapi.yaml (events).


3. Tenant administration

Tenants are the isolation unit. Entitlement for all tenant lifecycle routes: control_plane.shell. Note on scoping: the suspend/resume/export/delete handlers enforce that the {id} in the path equals your own tenant (self-service lifecycle); acting on another tenant returns 403 FORBIDDEN. Cross-tenant management is done through the MSP/federation planes (§10.7) or by operating within each tenant.

3.1 Procedure — create a tenant

Console path: Identity & Access → Tenants → Create a tenant. Role: admin.

  1. POST /v1/tenants
   { "slug": "acme", "name": "Acme Corp" }
  1. Note the returned tenant id.
  • Expected result: a new isolated tenant with its own RLS scope.
  • Undo: delete the tenant (§3.5) — but read the crypto-erase warning first; deletion is

irreversible. To take a tenant offline reversibly, suspend it instead (§3.2).

3.2 Procedure — suspend a tenant

What/why: freeze all activity (non-payment, investigation, offboarding hold) without deleting anything. A middleware rejects every request from a suspended tenant with 403 TENANT_SUSPENDED.

Console path: Identity & Access → Tenants → Suspend. Role: admin. Step-up: not required.

  1. POST /v1/tenants/{id}/suspend (no body).
  • Expected result: 200 {"status":"suspended"}; the tenant's users are locked out.
  • Undo: resume (§3.3). The /resume path is deliberately exempt from the suspension block so a

suspended tenant can be revived.

3.3 Procedure — resume a tenant

Console path: Identity & Access → Tenants → Resume. Role: admin.

  1. POST /v1/tenants/{id}/resume (no body).
  • Expected result: 200 {"status":"active"}; access restored. This reverses suspend.
  • Undo: suspend again.

3.4 Procedure — export a tenant (data portability)

What/why: produce a JSON snapshot of the tenant for portability/DR/compliance. This is bulk data egress, so it is a sensitive action.

Console path: Identity & Access → Tenants → Export. Role: admin or operator (requireManage). Step-up: REQUIRED (passkey re-auth).

  1. Ensure your passkey step-up is fresh (§1.5).
  2. POST /v1/tenants/{id}/export (no body).
  • Expected result: `200 {"export":{ "tenant_id":…, "generated_at":…, "users":[…],

"notification_channels":[…], "service_tokens":[…], "soar_cases":[…] (≤10 000), "audit_log":[…] (≤5 000), "metering_events":[…] (≤50 000) }}`. It is a non-destructive point-in-time JSON dump.

  • Undo: none needed — read-only. Store the export securely; it contains tenant data.

3.5 Procedure — delete a tenant (right-to-erasure + crypto-erase)

What/why: permanently erase a tenant for right-to-erasure. This does two things: (a) crypto-erase — destroys the tenant's HYOK key-encryption key (KEK) so encrypted-at-rest data, including backups and replicas, becomes unrecoverable; and (b) cascade-deletes all tenant rows. A retained erasure certificate is written to an installation-wide ledger that survives the delete.

Console path: Identity & Access → Tenants → Delete. Role: admin. Step-up: REQUIRED.

  1. Confirm you truly intend this. It is IRREVERSIBLE — there is no undo, no restore, no un-erase.
  2. Ensure passkey step-up is fresh.
  3. DELETE /v1/tenants/{id} with body:
   { "confirm_slug": "acme" }

confirm_slug must exactly equal the tenant's slug, or you get 422 CONFIRM_REQUIRED and nothing is deleted (this is the guard rail — mistyping is safe).

  • Expected result: 200 with deleted:true, crypto_erase:true, keys_destroyed:<n>,

erasure_certificate:<id>, and the note that HYOK material was destroyed and encrypted-at-rest data (incl. backups) is unrecoverable. The certificate id is your proof of erasure for auditors.

  • Undo: NONE. Irreversible by design. If you might need the data, run an export (§3.4)

first, or suspend (§3.2) instead of delete.

3.6 Procedure — pin data residency (data-sovereignty)

What/why: constrain where a tenant's data may live for sovereignty regimes (EU, etc.).

Console path: Estate Management → Fleet & Provisioning → Sovereign topology. Entitlement: platform.sovereign_topology_api.

  1. Inspect regions/topology: GET /v1/sovereignty/regions, GET /v1/sovereignty/topology.
  2. Pin: PUT /v1/sovereignty/residency
   { "region": "eu" }
  • Expected result: the tenant's data-path is pinned to the chosen region.
  • Undo: PUT again with the previous region.

4. Identity & access

4.1 Local accounts and auth policy

Local accounts are an admin-enabled fallback to SSO. Entitlement: control_plane.shell.

4.1.1 Set the tenant auth policy

Console path: Identity & Access → Identity & session → Auth settings.

  1. Read: GET /v1/iam/auth-settings.
  2. Update: PUT /v1/iam/auth-settings
   { "local_enabled": false, "oidc_enabled": true, "mfa": "webauthn" }
  • mfanone | totp | webauthn | email | either. Choosing email requires SMTP to be

configured (USCP_SMTP_HOST) or you get 400 EMAIL_OTP_UNAVAILABLE.

  • You cannot disable both local and OIDC → 400 NO_LOGIN_METHOD (prevents lockout).
  • Undo: PUT again with the prior values. Best practice: once SSO works, set

local_enabled:false and mfa:"webauthn".

4.1.2 Create / delete a local user

Console path: Identity & Access → Identity & session → Local users. Role: admin path in practice (user management).

  1. Create — POST /v1/iam/local-users
   { "username": "svc-break-glass", "email": "bg@acme.com", "password": "at-least-8-chars", "roles": ["operator"], "source": "local" }
  • roles defaults to ["operator"]. Password ≥8 chars for source:"local" else 400

WEAK_PASSWORD. sourcelocal | ad-ldap | radius. Toxic role combinations are rejected 403 SOD_CONFLICT (§6). Seat cap enforced → 402 LICENSE_LIMIT_EXCEEDED.

  1. Set password — POST /v1/iam/local-users/{username}/password {"password":"…"} (also revokes

that user's sessions).

  • Undo / offboard: DELETE /v1/iam/local-users/{username} — deletes the account and bumps

the session epoch so live sessions die within ≤60s.

4.1.3 External auth backends (AD/LDAP/RADIUS)

  1. Read: GET /v1/iam/external-backends.
  2. Replace-all: PUT /v1/iam/external-backends
   { "backends": [ { "type":"ad-ldap","name":"corp","host":"dc1.corp","port":636,"base_dn":"dc=corp","realm":"CORP","bind_ref":"vault://ad-bind","enabled":true } ] }

bind_ref is a non-secret reference to a vaulted credential — never a plaintext password.

  • Undo: PUT the list again without the entry (this endpoint replaces the whole list).

4.2 Roles, RBAC, and role mappings

4.2.1 Coarse roles (admin/operator/viewer/auditor)

  • GET /v1/rbac/roles, POST /v1/rbac/roles, DELETE /v1/rbac/roles/{name} manage the coarse

role catalog. Entitlement: control_plane.shell, role: admin.

4.2.2 Fine-grained RBAC (action+resource permissions)

Console path: Identity & Access → Identity & session → RBAC. Role: operator/admin (requireManage).

  1. Toggle: GET /v1/iam/rbac/settings then PUT /v1/iam/rbac/settings {"fine_grained":true}.
  2. Inspect grants: GET /v1/iam/rbac/grants{"roles":{"analyst":[{"action":"read","resource":"cases"}]}}.
  3. Set a role's permissions: PUT /v1/iam/rbac/roles/analyst
   { "permissions": [ {"action":"read","resource":"cases"}, {"action":"write","resource":"detections"} ] }
  1. Verify yourself: GET /v1/iam/rbac/effective.
  • Undo: DELETE /v1/iam/rbac/roles/analyst reverts that role to its built-in defaults.

4.2.3 Role mappings (IdP group / email-domain → platform role)

  1. GET /v1/iam/role-mappings, then replace-all: PUT /v1/iam/role-mappings
   { "mappings": [ {"match_type":"group","match_value":"SecOps","role":"operator"}, {"match_type":"email_domain","match_value":"acme.com","role":"viewer"}, {"match_type":"default","match_value":"","role":"viewer"} ] }

match_typegroup | email_domain | default. These drive JIT role assignment on SSO login. Role changes bump the session epoch (≤60s propagation).

  • Undo: PUT the list without the mapping.

4.3 SSO — OIDC IdP registration (multi-tenant)

Console path: Identity & Access → Identity & session → OIDC IdPs. Entitlement: control_plane.shell.

  1. Register — POST /v1/iam/idps
   { "label": "Okta (Acme)", "issuer": "https://acme.okta.com", "client_id": "0oa…", "client_secret": "…", "email_domains": ["acme.com","acme.co.uk"] }

The client_secret is sealed at rest and never echoed back.

  1. List — GET /v1/iam/idps (no secret returned).
  2. Users at acme.com now reach this IdP via home-realm discovery (GET /auth/sso?email=jane@acme.com)

and land in the tenant that owns this IdP.

  • Undo: DELETE /v1/iam/idps/{id} (also evicts the IdP cache).

4.4 SSO — SAML 2.0 IdP registration

Console path: Identity & Access → Identity & session → SAML IdPs. Entitlement: control_plane.shell.

  1. Give your IdP the SP metadata: GET /auth/saml/metadata (public). The SP entity id is that

metadata URL; the ACS is /auth/saml/acs.

  1. Register the IdP — POST /v1/iam/saml-idps
   { "label": "ADFS (Acme)", "idp_metadata_xml": "<EntityDescriptor …>…</EntityDescriptor>", "email_domains": ["acme.com"], "allow_idp_initiated": false }

You supply the IdP's metadata XML (which itself carries entity id / SSO URL / signing cert) — not separate fields. Invalid XML → 400 INVALID_METADATA. Leave allow_idp_initiated:false unless you specifically need IdP-initiated flows (SP-initiated is safer).

  1. List — GET /v1/iam/saml-idps (returns SP metadata + registered providers).
  • Undo: DELETE /v1/iam/saml-idps/{id}.

4.5 SCIM 2.0 provisioning

What/why: let your IdP create/update/deactivate users automatically. Auth: bearer (a service token, §2.5) with entitlement control_plane.shell.

  1. Point your IdP's SCIM connector at https://<host>/scim/v2 with the bearer token. It will probe

GET /scim/v2/ServiceProviderConfig, /ResourceTypes, /Schemas first.

  1. Provisioning operations your IdP will call:
    • POST /scim/v2/Users {"userName":"jane@acme.com","active":true,"name":{"givenName":"Jane","familyName":"Doe"}}
    • GET /scim/v2/Users, GET /scim/v2/Users/{id}
    • PATCH /scim/v2/Users/{id} {"Operations":[{"op":"replace","path":"active","value":false}]} (deactivate)
    • DELETE /scim/v2/Users/{id} (deprovision)
    • POST /scim/v2/Groups {"displayName":"operators"}, GET/PATCH /scim/v2/Groups/{id}
  • Undo: SCIM is declarative — set active:false or DELETE to remove access; recreate to restore.

4.6 Session management and revocation

Console path: Identity & Access → Identity & session → Sessions. Entitlement: control_plane.shell.

  1. List active sessions — GET /v1/iam/sessions (your own is flagged current:true; token hashes

never shown).

  1. Revoke one — DELETE /v1/iam/sessions/{id}{"revoked":true,"id":…}.
  2. Sign out all of your other sessions — POST /v1/iam/sessions/revoke-others

{"revoked_count":n} (keeps your current session).

  • Undo: none — the user simply logs in again. Absolute session TTL is enforced by the identity

layer (documented as ~12h); it is not a per-request field here.

4.7 IP allowlist (tenant network fence)

Console path: Identity & Access → Identity & session → IP allowlist. Role: operator/admin (requireManage).

  1. GET /v1/iam/ip-allowlist{"cidrs":[…]}.
  2. PUT /v1/iam/ip-allowlist {"cidrs":["203.0.113.0/24","198.51.100.7"]}.
    • Self-lockout guard: your current IP must be inside a non-empty list or you get 422

SELF_LOCKOUT. An empty list means "no restriction".

  • Requests from outside the list are rejected 403 IP_NOT_ALLOWED.
  • Undo: PUT {"cidrs":[]} to remove the restriction.

5. Licensing

USCP is fail-closed: capabilities you are not licensed for are simply unavailable. Licensing has two modes, chosen by the deploy-time env var LICENSING_MODE (airgap default, or online) — it is not changed via the API.

5.1 See your licensing posture (no auth)

Console path: Platform → Licensing & updates.

  • GET /v1/licensing/status — overall posture (mode, entitlements, continuity claim). Public.
  • GET /v1/crypto/status — crypto manifest, FIPS mode, PQC readiness. Public.
  • GET /.well-known/api-capabilities — the full 90-capability matrix. Public.

5.2 Air-gap vs online activation

  • Air-gap (LICENSING_MODE=airgap): a signed offline bundle (USCP_AIRGAP_BUNDLE) is

verified against trusted keys (USCP_LICENSE_JWKS). Posture: GET /v1/licensing/airgap (entitlement platform.airgap_licensing) → {mode, airgap_active, host_binding, status_url}. To update entitlements, distribute a new signed bundle and restart/reload (air-gap bundle distribution, §10.8).

  • Online (LICENSING_MODE=online): the binary calls https://licensing.doublelogic.org

(configured via ENTITLEMENT_SERVICE_BASE_URL, USCP_LICENSE_TENANT, USCP_INSTALL_KEY). Posture: GET /v1/licensing/online (entitlement platform.online_licensing) → {mode, online_active, server_configured}.

  • mTLS profile (online): GET /v1/licensing/mtls-profile (entitlement platform.mtls_license_profile).
  • Undo: licensing mode is an environment change (redeploy), not an API toggle.

5.3 Seat limits and capability entitlements

  • Seats: creating local users / SCIM users / service tokens is capped by license; over-cap

operations return 402 LICENSE_LIMIT_EXCEEDED. Free seats by deprovisioning users (§4) or revoking tokens (§2.5).

  • Capabilities: each of the 90 keys (§14) is individually licensed. If a whole nav group is

missing, the capability is not in your license — check GET /.well-known/api-capabilities.

5.4 Continuity / escrow (vendor-failure protection)

See §10.6 for the full activation procedure. Posture is public at GET /v1/licensing/continuity; the escrow marker capability is platform.continuity_escrow.


6. Governance & trust

6.1 Policy plane (RBAC + ABAC, two-person publish, simulator)

Console path: Governance & Trust → Policy plane. Entitlement: control_plane.shell.

USCP evaluates policy with a deny-wins precedence engine (RBAC + ABAC layers). Policies are drafted, then published by a distinct second approver (two-person rule).

  1. Simulate a decision before you change anything — POST /v1/policy/simulate
   { "subject": { "roles": ["operator"] }, "action": "estate.killswitch", "resource": "target/123" }
  1. Draft a policy — POST /v1/policies
   { "name": "deny-prod-delete", "effect": "deny", "actions": ["estate.killswitch"], "resources": ["*"] }
  1. Publish with a second approver — POST /v1/policies/{id}/approve
   { "approver": "second.operator@example.com" }

The approver must be distinct from the drafter (four-eyes).

  1. ListGET /v1/policies.
  • Undo: publish a new policy version that reverses the rule, or draft+approve a superseding

policy. Always simulate first. The digital-twin/regression tooling (§7.10) lets you test a policy change against scenarios before publishing.

6.2 Audit log (tamper-evident) and verification

Console path: Governance & Trust → Audit & evidence. Entitlement: control_plane.shell.

The audit log is a hash-chained, tamper-evident ledger. Verify its integrity regularly.

  1. Verify the chainGET /v1/_audit/verify. Expect an OK/valid result; a break indicates

tampering — treat as a SEV incident (§11).

  1. Activity timelineGET /v1/_audit/activity (last 24h + recent events).
  2. Export tenant logsGET /v1/logs/export.
  • Undo: the audit log is append-only/WORM by design — you cannot and must not edit it.

6.3 Auditor role — time-boxed grants

What/why: give an external/internal auditor read-only access to the evidence locker for a bounded window. Access requires both the auditor role and a live grant.

Console path: Governance & Trust → Audit & evidence → Auditor grants. Role: admin.

  1. GrantPOST /v1/audit/auditor-grants
   { "subject": "auditor@big4.com", "ttl_days": 30 }

ttl_days defaults to 30, maximum 90 (>90 → 422 TTL_TOO_LONG). Re-granting the same subject extends/replaces the window.

  1. ListGET /v1/audit/auditor-grants (each shows active = not yet expired).
  2. The auditor then uses GET /v1/audit/evidence-locker and

GET /v1/audit/evidence-locker/export?framework=<caiq|sig|ccm|800-53|oscal> (entitlement platform.customer_auditor_role).

  • Undo / early revoke: DELETE /v1/audit/auditor-grants/{subject}{"revoked":true}. Access

also lapses automatically at expiry.

6.4 Lockbox — customer approval of vendor access

What/why: vendor/support access to your data is default-deny; each access is a request you approve or deny (Access Approval). Entitlement: platform.customer_lockbox.

Console path: Governance & Trust → Lockbox & HYOK.

  1. See requests — GET /v1/lockbox/requests.
  2. Raise a request (typically vendor-initiated) — POST /v1/lockbox/requests

{"reason":"support","scope":"tenant-data","ttl":"1h"}.

  1. DecidePOST /v1/lockbox/requests/{id}/decision {"decision":"approve"} (or "deny").
  • Undo: deny future access; an approval is time-boxed by its ttl. Pair with HYOK (§6.5) so the

vendor cannot decrypt without your key grant.

6.5 HYOK / BYOK — hold-your-own-key management

What/why: encrypt tenant data under a key you control, so the vendor cannot decrypt without your grant. Revoking the key renders data undecryptable by construction. Entitlement: platform.hyok (BYOK envelope posture: platform.byok_envelope).

Console path: Governance & Trust → Lockbox & HYOK.

  1. Provision the tenant KEK — POST /v1/lockbox/hyok/provision (no body). Generates a 32-byte

KEK wrapped by your configured KMS provider. Refuses to overwrite an active key → 409 ALREADY_PROVISIONED (revoke first). KMS unreachable → 502 KMS_UNAVAILABLE.

  1. StatusGET /v1/lockbox/hyok/status and GET /v1/lockbox/byok (mechanism, provider,

external-KMS flag, manage URL).

  1. Encrypt / decrypt (envelope ops) — POST /v1/lockbox/hyok/encrypt {"plaintext":"…"}

{"envelope":…}; POST /v1/lockbox/hyok/decrypt {"envelope":…}{"plaintext":"…"}.

  1. RevokePOST /v1/lockbox/hyok/revoke (no body). After this, all decrypts fail 403

KEY_REVOKED — the vendor cannot read the data.

  • Undo: Revoke is NOT reversible for existing data — revoked data stays undecryptable

(that is the point). You may provision a new KEK afterward, but it does not restore access to data encrypted under the old (revoked) key. Treat revoke as a last-resort control.

Entitlement: control_plane.shell. Console path: Governance & Trust → Privacy & DSAR.

  • Consent receiptPOST /v1/privacy/consent {"Subject":"jane@acme.com","Purpose":"marketing","Basis":"consent","Granted":true}.

If the request carries Sec-GPC: 1 or DNT: 1 and the purpose isn't "essential", Granted is forced false (binding opt-out; gpc_honored:true in the response). List: GET /v1/privacy/consent. Undo: record a new receipt with Granted:false (append-only; no delete).

  • DSAR export (right of access) — GET /v1/privacy/dsar/export returns the caller's identity

bundle as an attachment. Read-only.

  • DSAR erase (right to erasure) — POST /v1/privacy/dsar/erase {"confirm":true} (must be

true, else 400 CONFIRM_REQUIRED). Deletes the user + sessions + WebAuthn creds + role assignments + any local account; WORM audit entries are retained under legal-basis exemption. Undo: NONE — irreversible.

  • DSAR registerGET /v1/privacy/dsar/requests (auditable log of all DSAR actions).

6.7 DLP — egress governance

What/why: inspect outbound content (e.g. webhook payloads) and allow / redact / block it. Entitlement: control_plane.dlp. Console path: Governance & Trust → Hardening & Drift (DLP is operated via the endpoint runner / DLP screen).

  1. See rules — GET /v1/dlp/rules (built-in + tenant rules).
  2. Add a rule — POST /v1/dlp/rules
   { "ID": "no-ssn", "Name": "Block SSN", "Pattern": "\\d{3}-\\d{2}-\\d{4}", "Action": "block", "Severity": "high" }

ID and Pattern (a valid regexp, else 422 BAD_PATTERN) are required. Action

allow | redact | block (default redact). Severity default medium.

  1. Test content — POST /v1/dlp/scan {"Content":"my ssn is 123-45-6789"} → verdict + findings +

redacted copy.

  1. Review egress events — GET /v1/dlp/egress-events (last 200; block/redact are recorded).
  • Behavior: on outbound webhooks, block suppresses delivery, redact sends a masked copy,

allow sends as-is. Rule-load faults fail open to built-ins.

  • Undo: DELETE /v1/dlp/rules/{id}, or POST the same ID to overwrite.

6.8 Sanctions floor and per-tenant geo-fencing

What/why: the OFAC comprehensive-embargo floor is ALWAYS ON and cannot be disabled — requests from CU, IR, KP, SY are blocked pre-auth regardless of tenant policy (451 GEO_FENCED). On top of that, each tenant can add its own allow/deny country list.

Console path: Governance & Trust → Hardening & Drift (Geo-fence). Entitlement: control_plane.shell; PUT requires admin.

  1. Read — GET /v1/geo-fence{"mode":"off","countries":[…],"sanctions_floor":["CU","IR","KP","SY"]}.
  2. Configure — PUT /v1/geo-fence
   { "mode": "allow", "countries": ["US","GB","DE"] }

modeoff | allow | block. allow permits only the listed countries; block rejects the listed ones; allow/block require ≥1 country (else 422 COUNTRIES_REQUIRED). Codes are ISO-3166 alpha-2, upper-cased.

  • Undo: PUT {"mode":"off"} to remove the tenant fence. **The OFAC floor cannot be turned off

by any endpoint.**


7. Security operations administration

All SecOps domains are operated from the Security Operations and Governance & Trust nav groups (plus the endpoint runner). Each has its own entitlement key — if a screen is empty, confirm the capability is licensed (§5.3).

7.1 Detection hub (detection-as-code)

Entitlement: control_plane.detection_hub. Console path: Security Operations → Detection hub.

  • List/author detections — GET /v1/detections, POST /v1/detections

{"name":"impossible-travel","logic":"sigma://…","enabled":true}.

  • Rules lifecycle — POST /v1/detect/rules, POST /v1/detect/rules/install-starters,

POST /v1/detect/rules/import-sigma, POST /v1/detect/rules/test, GET /v1/detect/rules, DELETE /v1/detect/rules/{id}.

  • Findings — GET /v1/detect/findings, escalate POST /v1/detect/findings/{id}/escalate.
  • ATT&CK coverage — GET /v1/detections/coverage (entitlement control_plane.attack_coverage_map).
  • Tuning workflow: author → POST /v1/detect/rules/test (dry-run) → enable. Undo: disable

or DELETE the rule. Follow the detection-lifecycle.md runbook (author→dry-run→canary→deploy).

7.2 CTEM / exposure management

Entitlement: control_plane.exposure_mgmt (+ attack_path, validation_bas). Console path: Security Operations → CTEM/exposure.

  • Exposures/scope/vectors — GET /v1/ctem/exposures, GET /v1/ctem/scope, GET /v1/ctem/vectors.
  • Run an assessment — POST /v1/ctem/assess; results GET /v1/ctem/assessments.
  • Attack path — POST /v1/ctem/attack-path {"from":"internet","to":"crown-jewel"}.
  • BAS validation — POST /v1/ctem/validate {"technique":"T1078","target":"…"} (safe, scoped;

see ctem-validation.md — never destructive payloads).

  • CVE feed — POST /v1/ctem/cve-feed, POST /v1/ctem/cve-feed/fetch.

7.3 SOAR, cases, and playbooks

Entitlement: control_plane.case_management (cases) / control_plane.soar (playbooks). Console path: Security Operations → Data lake/intel/SOAR.

  • Cases — POST /v1/soar/cases {"title":"Suspicious login","severity":"SEV-3"}, GET/`PATCH

/v1/soar/cases/{id}, notes GET/POST /v1/soar/cases/{id}/notes, transition/assign/priority/ escalate via the corresponding /v1/soar/cases/{id}/…` routes.

  • Playbooks — POST /v1/soar/playbooks/{id}/run {"inputs":{}}.
  • CACAO import/export — POST /v1/soar/cacao/export, POST /v1/soar/cacao/import.
  • Undo: transition a case back / reassign; playbook runs are logged. Follow soar.md.

7.4 Agentic SOC (autonomous agents + kill-switch)

Entitlement: control_plane.agentic_soc. Console path: Security Operations → Agentic SOC.

  • Investigate — POST /v1/soc/agents/investigate {"alertId":"…","scope":"tenant"}.
  • Execute a remediation — POST /v1/soc/agents/execute {"caseId":"…","action":"isolate-host"}.
  • Halt all agentsPOST /v1/soc/agents/killswitch {"reason":"manual override"}. This is

fail-safe: tripping stops autonomous action immediately.

  • Reset the kill-switchPOST /v1/soc/agents/killswitch/reset.
  • Scorecards / model — GET /v1/soc/agents/scorecards, GET /v1/soc/agents/model.
  • Undo: the kill-switch reset re-enables agents. Follow soc-agent-action.md /

agent-budget-breach.md for runaway-agent handling.

7.5 Threat analytics — UEBA, ITDR, correlation, posture, risk, SIEM, AIOps

Console path: Security Operations → Threat Analytics. Each has its own entitlement.

  • UEBA (control_plane.ueba): GET /v1/ueba/entities, POST /v1/ueba/scan ({}),

GET /v1/ueba/anomalies, GET /v1/ueba/entities/{entity}.

  • ITDR (control_plane.itdr): GET /v1/itdr/signals, POST /v1/itdr/scan, GET /v1/itdr/detections.
  • Correlation (control_plane.correlation): POST /v1/correlation/run, GET /v1/correlation/live,

GET /v1/correlation/incidents.

  • Posture score (control_plane.posture_score): GET /v1/posture/score,

POST /v1/posture/snapshot, GET /v1/posture/history.

  • Quantitative risk / FAIR (control_plane.risk_quant): GET /v1/risk/portfolio,

POST /v1/risk/scenarios {"name":"ransomware","asset_value":500000,"threat_freq":0.5,"vuln":0.4}, GET /v1/risk/scenarios, DELETE /v1/risk/scenarios/{id}.

  • SIEM hub (control_plane.siem_hub): GET /v1/siem/overview, GET /v1/siem/alerts.
  • AIOps (control_plane.aiops): GET /v1/aiops/health, POST /v1/aiops/scan, GET /v1/aiops/anomalies.
  • Tuning: the POST …/scan calls persist anomalies; run them on a schedule (a service token +

cron is the usual pattern). Undo: these are analytic reads; delete risk scenarios to remove.

7.6 Deception (honeytokens)

Entitlement: control_plane.deception. Console path: Security Operations → Deception.

  • Plant — POST /v1/deception/honeytokens {"Name":"decoy-cred","Kind":"credential","Placement":"vault"}.

Kindcredential | url | file | dns | api_key | host. The token value (ht-<kind>-<hex>) is returned once — place it where an intruder would find it.

  • List — GET /v1/deception/honeytokens. Report/observe hits — POST /v1/deception/trip

{"token":"ht-credential-…","source_ip":"10.0.0.9"}; feed — GET /v1/deception/trips. Any trip is high-signal (severity critical).

  • Undo: DELETE /v1/deception/honeytokens/{id}.

7.7 Detonation (sandbox)

Entitlement: control_plane.detonation. Console path: Security Operations → Detonation.

  • Provider is environment-configured, not via API: USCP_DETONATION_PROVIDER =

simulation (default) or http; USCP_DETONATION_ENDPOINT (SSRF-guarded) for the http provider. Inspect it: GET /v1/detonation/provider{provider, endpoint_configured, simulation}.

  • Submit — POST /v1/detonation/submit {"sample_ref":"https://x/payload.bin","sample_hash":"…","sample_type":"file"}

(provide sample_ref or sample_hash). Jobs/verdicts — GET /v1/detonation/jobs.

  • Undo: none (analysis only). Configure a real sandbox by setting the env vars and restarting.

7.8 OT / ICS ingestion (read-only)

Entitlement: control_plane.ot_visibility / control_plane.ot_readonly. Console path: Estate Management → OT/ICS. USCP is read-only toward OT (no control writes).

  • Ingest an asset (sensor push) — POST /v1/ot/assets

{"name":"plc-1","asset_type":"plc","protocol":"modbus","vendor":"Siemens","ip":"10.1.0.5","zone":1}.

asset_typeplc | rtu | hmi | scada | sensor | historian; zone is a Purdue level 0-5.

  • Inventory/zones/safety — GET /v1/ot/assets, GET /v1/ot/zones, GET /v1/ot/safety-posture.
  • UAT seed — POST /v1/ot/simulate-seed (labels assets source=simulation).
  • Undo: none via API (visibility only). Follow network-fabric-config.md for fabric changes.

7.9 Digital Risk Protection (DRP)

Entitlement: control_plane.drp. Console path: Security Operations → Digital Risk.

  • Monitor an asset — POST /v1/drp/assets {"Kind":"domain","Value":"acme.com"} (Kind

domain | brand | keyword | email_domain).

  • Ingest a finding from your DRP feed — POST /v1/drp/ingest

{"asset":"acme.com","category":"typosquat","severity":"high","detail":{}}.

  • Findings — GET /v1/drp/findings; UAT — POST /v1/drp/simulate.
  • Undo: re-POST an asset to overwrite (upsert); findings age out by retention.

7.10 Threat intel, data lake, hardening/drift, collective defense, policy twin

  • Threat intel (TIP) (control_plane.threat_intel): POST/GET /v1/intel/indicators,

suppress/unsuppress POST /v1/intel/indicators/{id}/suppress|unsuppress, TAXII GET /v1/intel/collections/{id}/objects. Follow threat-intel.md / intel-feed-anomaly.md.

  • Data lake (control_plane.security_data_lake / federated_search): POST /v1/lake/ingest,

POST /v1/lake/query.

  • Config drift (control_plane.config_drift): baseline POST /v1/config-drift/baselines,

check POST /v1/config-drift/check, view GET /v1/config-drift/drifts. Undo: re-baseline.

  • Telemetry fabric (control_plane.telemetry_fabric): GET/POST /v1/telemetry/routes,

DELETE /v1/telemetry/routes/{id}, POST /v1/telemetry/evaluate.

  • Collective defense (control_plane.collective_defense): GET /v1/collective/status,

opt-in POST /v1/collective/optin {"opted_in":true}, IOCs GET/POST /v1/collective/iocs. Undo: optin {"opted_in":false}.

  • Policy twin (control_plane.policy_twin): POST /v1/policy-twin/simulate,

suites POST/GET /v1/policy-twin/suites, run POST /v1/policy-twin/suites/{id}/run — use this to regression-test RBAC changes before publishing (§6.1).

7.11 IGA — segregation-of-duties, certification, joiner/leaver

Console path: Identity & Access → Identity governance (IGA).

  • SoD check (control_plane.sod): POST /v1/iga/sod/check {"user":"jane@acme.com","proposedRole":"approver"}.
  • Access certification (control_plane.access_certification): POST /v1/iga/certify,

campaigns GET /v1/iga/campaigns.

  • Joiner/leaver (control_plane.iga): POST /v1/iga/joiner, POST /v1/iga/leaver

{"user":"jane@acme.com"} (deprovisions; ≤60s session revocation). Follow iga-governance.md.

  • Undo: re-provision via joiner/SCIM.

8. Estate management

The estate plane manages the machines/devices you operate through USCP (Linux/Windows hosts, network gear, BMCs). Sessions are brokered and recorded (WORM); credentials are vaulted and injection-only. Follow estate-session-anomaly.md, vault-credential-anomaly.md, hostkey-attestation-failure.md.

8.1 Enrol targets and agents

Entitlement: control_plane.estate_inventory. Console path: Estate Management → Estate plane.

  • Register a target — POST /v1/estate/targets {"kind":"linux-host","address":"10.0.0.10","labels":{"env":"prod"}}.
  • List/inventory — GET /v1/estate/targets, GET /v1/estate/capabilities, GET /v1/estate/topology.
  • Agents — GET /v1/estate/agents, enrol POST /v1/estate/agents/enroll.

8.2 Vault credentials (PAM)

Entitlement: control_plane.estate_pam.

  • Vault — POST /v1/estate/credentials

{"cred_id":"root-key","kind":"ssh-key","username":"root","secret":"…","source":"platform"}.

sourceplatform | cyberark | hashicorp-vault | delinea | ad-ldap; platform requires secret, external sources require a reference. Secrets are injection-only (never copied out).

  • List (metadata only) — GET /v1/estate/credentials. Providers/connectors —

GET /v1/estate/credential-providers, GET/POST/DELETE /v1/estate/pam-connectors[/{id}].

  • Undo / rotate: DELETE /v1/estate/credentials/{id}; follow estate-credential-rotation.md.

8.3 Brokered, recorded sessions

Entitlement: control_plane.session_broker.

  • Open — POST /v1/estate/sessions {"target_id":"…","cred_id":"…","protocol":"ssh","allowlist":["^uptime$"]}

{"session_id":…,"protocol":…,"recorded":true} (recording is mandatory).

  • Exec — POST /v1/estate/sessions/{id}/exec {"command":"uptime"} (blocked commands → 403

COMMAND_BLOCKED).

  • Recording — GET /v1/estate/sessions/{id}/recording{events, chain_valid, broke_at_seq}

(chain_valid proves the WORM recording is untampered).

8.4 Host-key pinning

Entitlement: control_plane.estate_inventory.

  • View pins — GET /v1/estate/host-keys. Reset (on legitimate rekey) — POST /v1/estate/host-keys/reset.
  • Why: an unexpected host-key change is a spoofing signal — see hostkey-attestation-failure.md.

8.5 Firewall policy management

Entitlement: control_plane.estate_policy. Console path: Governance & Trust → Policy plane (host firewall) / endpoint runner.

  • GET /v1/policy/firewall, live GET /v1/policy/firewall/live, import POST /v1/policy/firewall/import,

create POST /v1/policy/firewall, patch PATCH /v1/policy/firewall/{id}, delete DELETE /v1/policy/firewall/{id}, duplicate POST /v1/policy/firewall/{id}/duplicate.

  • Undo: DELETE the rule or PATCH it back.

8.6 Policy packs and fan-out

Entitlement: control_plane.estate_policy (packs) / control_plane.estate_response (fan-out).

  • Plan → apply — POST /v1/estate/packs/plan {"pack":"cis-baseline","scope":{"env":"prod"}} then

POST /v1/estate/packs/apply {"planId":"…"}.

  • Fan-out — POST /v1/estate/fanout {"pack":"linux-stig","targets":[…],"canary_count":1,"ring_count":3,"abort_on_canary_failure":true}.

Refused 503 ESTATE_HALTED if the estate kill-switch is tripped. Pack values include

linux-stig | windows-stig | osquery | ngfw-connector.

8.7 Estate kill-switch (step-up + four-eyes) — THE emergency stop

Entitlement: control_plane.estate_response. Console path: Estate Management → Estate plane → Kill-switch.

  1. Trip (halt all estate response) — POST /v1/estate/killswitch. Step-up REQUIRED. Tripping

is single-admin and fail-safe → {"kill_switch":"tripped"}. All fan-out is now refused.

  1. Reset (re-enable) is deliberately hard — it needs step-up AND two admins:
    • Admin #1: POST /v1/estate/killswitch/reset {"reason":"incident resolved"} (step-up

required, admin role). This does not reset directly — it opens a dual-control pending request202 {"dual_control":"pending","request_id":…}.

  • Admin #2 (distinct): approve it — POST /v1/dualcontrol/{request_id}/approve. Only now does

the reset actually run. (Reject with POST /v1/dualcontrol/{request_id}/reject; list pending with GET /v1/dualcontrol.)

  • Undo: trip is undone only by the two-admin reset above; a reset is undone by tripping again.

8.8 Fleets and zero-touch provisioning

  • Fleets (control_plane.fleet_mgmt): create POST /v1/fleets {"name":"prod-fleet","tags":["prod"]},

list GET /v1/fleets, members POST /v1/fleets/{id}/members {"target_id":"…"}, delete DELETE /v1/fleets/{id}. (No delete-member route — delete the fleet to drop membership.)

  • ZTP (control_plane.ztp): mint enrol token POST /v1/ztp/tokens

{"name":"edge","ttl_hours":24,"max_uses":1,"template":{"role":"agent"}} (token ztp-<hex> returned once), list GET /v1/ztp/tokens, revoke DELETE /v1/ztp/tokens/{id}, redeem POST /v1/ztp/redeem {"token":"ztp-…"}.

  • Undo: DELETE the fleet / revoke the ZTP token.

9. Observability & operations

9.1 Metrics

  • GET /metrics — Prometheus scrape endpoint. **Public at the app layer — protect it at the

network layer** (do not expose it to the internet). Point Prometheus/Grafana at it.

9.2 Health probes

  • GET /healthz, GET /livez (liveness), GET /readyz (readiness — DB + migrations),

GET /healthz/deep (per-component dependency health). Wire these into your load balancer and uptime checks. All public.

9.3 SRE dashboards

  • GET /v1/sre/dashboards (entitlement platform.sre_dashboards) — vendor-neutral dashboard specs

you can import into Grafana. Console path: Platform → Release & Supply Chain.

9.4 Log retention (config + regulatory floors)

What/why: control how long each log class is kept across hot/warm/cold tiers. Some classes have regulatory floors you cannot go below. Entitlement: control_plane.shell. Console path: Governance & Trust → Audit & evidence (retention) / endpoint runner.

  1. Read — GET /v1/observability/retention → each class shows hot_seconds, warm_seconds,

cold_seconds, floor_cold_seconds, legal_hold, overridden.

  1. Override — PUT /v1/observability/retention
   { "overrides": [ { "class": "security", "hot_seconds": 2592000, "warm_seconds": 15552000, "cold_seconds": 63072000 } ] }

Validation: warm ≥ hot, cold ≥ warm (when set), and cold may never be below the class floor → 422 RETENTION_FLOOR. Duration helper: 30d=2 592 000s, 1y=31 536 000s, 7y=220 752 000s.

Regulatory cold-tier floors (cannot be set lower):

Class Floor Class Floor
audit7yincident7y
security1ypolicy_change7y
access1ylicense_event7y

Classes not listed have no floor. audit and incident are legal-hold classes — never deleted by the retention worker regardless of horizon.

  • Undo: there is no delete-override endpoint; PUT the class again with the built-in defaults

(or any values ≥ the floor).

9.5 Log forwarding — collectors, DLQ, replay

What/why: ship logs to your SIEM/lake with durable retry and a dead-letter queue. Entitlement: control_plane.shell. Console path: Platform → Integrations / endpoint runner.

  1. Collectors — GET /v1/logs/collectors; add (requireManage) — POST /v1/logs/collectors
   { "name": "splunk-prod", "type": "splunk_hec", "endpoint": "https://splunk:8088", "config": {"index":"sec"}, "auth": {"token":"…"} }

types3 | syslog | kafka | https | otel | splunk_hec. Endpoints are SSRF-guarded; auth is sealed at rest. Delete (requireManage) — DELETE /v1/logs/collectors/{id}.

  1. Forward-queue health — GET /v1/observability/forward-queue{pending, dead, oldest_pending_at}.
  2. Replay the DLQPOST /v1/observability/forward-queue/replay{"replayed":n} (requeues

this tenant's dead batches). Run this after fixing a collector outage.

  • Undo: DELETE the collector.

9.6 Telemetry routing

See §7.10 — control_plane.telemetry_fabric routes events by source/severity to destinations.

9.7 Notifications / alerting channels

What/why: send alerts to Slack, PagerDuty, email, or a webhook, with a durable delivery log + replay. Entitlement: control_plane.shell. Console path: Platform → Integrations.

  1. List — GET /v1/notifications/channels.
  2. Add (requireManage) — POST /v1/notifications/channels
   { "kind": "slack", "name": "SOC on-call", "target": "https://hooks.slack.com/…", "events": ["detection.finding","estate.killswitch"] }

kindslack | pagerduty | email | webhook (else 400 BAD_REQUEST). The target secret is sealed at rest.

  1. Test (requireManage) — POST /v1/notifications/channels/{id}/test{"delivered":true}

(channel error → 502 CHANNEL_ERROR).

  1. Deliveries + replay — GET /v1/notifications/deliveries, POST /v1/notifications/deliveries/{id}/replay.
  2. Undo: DELETE /v1/notifications/channels/{id}.

9.8 Outbound webhooks (with DLQ)

Entitlement: control_plane.shell. Console path: Platform → Integrations.

  1. Create (requireManage) — POST /v1/webhooks {"url":"https://api.acme.com/hook","events":["case.created"],"secret":"whsec_…"}

(both url and secret required; URL is SSRF-guarded; secret sealed). Deliveries are HMAC-signed (X-USCP-Signature, X-USCP-Event), at-least-once, up to 6 attempts then dead.

  1. List/test — GET /v1/webhooks, POST /v1/webhooks/test.
  2. Delivery log (DLQ) — GET /v1/webhooks/deliveries?status=dead, replay

POST /v1/webhooks/deliveries/{id}/replay (requireManage; resets a dead/failed delivery to pending).

  • Undo: there is no delete-webhook route exposed; disable by removing its events or rotating

the endpoint. (Verify with engineering if you need hard-delete — see uncertainties.)

9.9 Runtime log level and tracing

  • Log level (control_plane.shell): GET /v1/admin/log-level; set (requireManage)

PUT /v1/admin/log-level {"level":"debug"} (leveldebug | info | warn | error; applies at runtime, no restart). Undo: PUT back to info.

  • Events stream / emit (for integration testing): GET /v1/events/stream, POST /v1/events/emit.
  • Tracing is emitted via the OTel collector (suite GET /v1/suite/otel, suite.shared_otel_collector).

10. Resilience & business continuity

10.1 Break-glass — the emergency admin path (§98)

What/why: SSO + MFA are mandatory, so if your only identity path (the IdP) is down, every operator is locked out — often during the SSO change that broke it. Break-glass is the drilled way back in. It is IdP-independent, dual-control (M-of-N recovery codes), time-boxed, loud, and never entitlement-gated. Full runbook: codes/runbooks/break-glass.md (RB-F).

10.1.1 Procedure — provision/re-seal break-glass codes (day-0 and after every use)

Console path: Platform → (break-glass setup) / endpoint runner. Entitlement: control_plane.shell; must be admin, in the bootstrap tenant (else 412 NO_BOOTSTRAP_TENANT / 403 WRONG_TENANT).

  1. POST /admin/break-glass/setup
   { "threshold": 3, "ttl_minutes": 60 }

threshold (M-of-N quorum) defaults to 2; ttl_minutes defaults to 60. This generates exactly 10 recovery codes (bg-<hex>), replaces any prior codes, and stores only their hashes.

  1. Response 201 returns all 10 plaintext codes ONCE. Distribute one code to each of several

distinct custodians; store them offline (safe/HSM/sealed envelope) — never in USCP, never in the IdP.

  • Expected result: a sealed, deny-by-default emergency path exists.
  • Undo / rotate: re-run setup — it replaces all codes (old codes are invalidated). **Re-seal

after every activation** (codes are single-use).

10.1.2 Procedure — activate break-glass during an outage

This endpoint is public (POST /admin/break-glass) — never entitlement-gated — so it works when the IdP and licensing paths are down. Requires the quorum, from distinct custodians.

  1. Confirm this is genuinely an identity-path failure (not licensing fail-closed — break-glass

does not fix licensing; see §10.6). Mint INC-BG-YYYYMMDD-NN.

  1. POST /admin/break-glass
   { "reason": "IdP unreachable during SAML cert rotation", "codes": ["bg-aaa…","bg-bbb…","bg-ccc…"] }

reason is required. Codes are de-duplicated by hash (distinct custodians). If fewer than threshold distinct valid unused codes are presented, nothing is consumed and you get 403 BREAK_GLASS_QUORUM.

  1. On success (200): a time-boxed admin session is minted (TTL = ttl_minutes), MFA marked

complete, session cookie set; the presented codes are burned (single-use). A tenant-wide banner, SEV-2 audit, and security+billing notifications fire for the whole window.

  • Scope discipline: use break-glass only to restore identity (re-point IdP, re-upload

SAML/OIDC metadata + certs, re-enable users). It cannot disable audit, recording, or the break-glass controls themselves.

  • Undo / closeout: the session auto-expires at ttl_minutes; you cannot silently extend it.

Afterward, re-seal (§10.1.1) and run the mandatory post-use review (owner ≠ the operator who used it).

10.1.3 Estate break-glass (privileged-session emergency access, §90)

Separate from §98. Entitlement: control_plane.session_broker. Dual-control, least-scope, time-boxed:

  • Request — POST /v1/estate/breakglass/request

{"target_id":"…","justification":"incident","ttl_seconds":3600,"required_approvals":2,"scope":"target_only"}

(scopetarget_only | read_only | full).

  • Approve (distinct approver) — POST /v1/estate/breakglass/{id}/approve (requester cannot approve

own; grant activates when distinct approvers ≥ required_approvals).

  • List — GET /v1/estate/breakglass.
  • Undo: the grant auto-expires at ttl_seconds. Follow vendor-access-approval.md.

10.2 Backup and restore

  • Database is your source of truth — back up PostgreSQL with your standard tooling (PITR/WAL

archiving recommended). Crucially: because tenant delete crypto-erases the HYOK KEK, a DB backup alone cannot restore a deleted tenant's encrypted data — the key is gone (§3.5). Back up KMS/HYOK key material per your KMS provider's DR guidance.

  • Tenant-level export (POST /v1/tenants/{id}/export, §3.4) is a portable JSON snapshot for

point-in-time capture.

  • Self-service data ops (platform.self_service_data_ops): POST /v1/data-ops/export-snapshot,

POST /v1/data-ops/purge, GET /v1/data-ops/jobs.

  • Verify restores in a non-prod environment; confirm GET /readyz and GET /v1/_audit/verify

after a restore.

10.3 DR / region failover

Follow codes/runbooks/region-failover.md (RB-B) — cell-based failover, per-tenant home-region promotion, status-page + comms. Rehearse quarterly. Use GET /v1/sovereignty/topology to confirm region placement and GET /healthz/deep to confirm per-component health post-failover.

10.4 Continuity / escrow (vendor-failure) — §97

What/why: if the vendor disappears, activate an escrowed, signed "frozen build" license so you keep running (preservation-only — it grants nothing you did not already hold). Entitlement: platform.continuity_escrow; admin + dual-control. Follow vendor-continuity.md (RB-E).

  1. Ensure escrow is provisioned: env USCP_ESCROW_JWKS (+ USCP_LICENSE_JWKS) set, else 412

ESCROW_NOT_PROVISIONED. Check posture: GET /v1/licensing/continuity (public).

  1. Activate — POST /v1/licensing/continuity/activate
   { "bundle": "<compact-JWS>", "approvers": ["you@acme.com","second.admin@acme.com"] }

Your own email must be in approvers (else 403 APPROVER_IDENTITY); ≥2 distinct approvers required (else 400 DUAL_CONTROL). The escrow bundle's signing key must be trusted in the escrow JWKS or you get 422 CONTINUITY_REFUSED.

  • Expected result: 200 {"activated":true,"frozen_at":…}; a SEV-1 audit event is written.
  • Undo: NONE — this is an append-only, one-way emergency action. It is preservation-only, so

it is safe to leave active.

10.5 Release channels + module hot-reload

  • Release channels (platform.release_channels), admin: channel names are **`stable |

beta | edge** (not "canary"). Pin a channel version — PUT /v1/release/channels/{name} {"Version":"1.4.2","Notes":"…"}; assign this tenant — PUT /v1/release/assignment {"Channel":"stable"}. Read: GET /v1/release/channels, GET /v1/release/assignment. **Undo:** PUT` the prior channel/version.

  • Updates (control_plane.shell): GET/POST /v1/updates/check, GET /v1/updates/status,

POST /v1/updates/apply {"version":""}. Updates are cosign/SLSA-verified before apply (update-verification-failure.md).

  • Module hot-reload (platform.module_hot_reload): register POST /v1/modules/register

{"name":"soar","version":"1.0","ui_mount":"/soar"}, list GET /v1/modules, reload POST /v1/modules/{name}/reload (runtime, no restart).

10.6 Air-gap bundle distribution (parent→children)

Entitlement: suite.airgap_bundle_distribution. POST/GET /v1/suite/airgap-bundles — a parent control plane distributes signed offline update bundles to child installations. Pair with platform.self_hosted_release_parity (GET /v1/release/parity) so managed and air-gap run the same stream.

10.7 MSP / co-managed SOC (§49)

Entitlement: control_plane.msp / control_plane.comanaged_soc. Console path: Platform → Suite & MSP, admin.

  • Add a managed tenant — POST /v1/msp/managed {"managed_tenant":"cust-1","scope":"soc"}

(scopefull | soc | readonly; self-manage → 422 SELF_MANAGE).

  • List — GET /v1/msp/managed; co-managed SOC view — GET /v1/msp/soc.
  • Undo: DELETE /v1/msp/managed/{id} (soft-revokes the relationship).

10.8 Parent-suite federation (§37/§49)

Entitlement: suite.federation_core. Run a parent that federates children: POST/GET /v1/federation/children, GET /v1/federation/children/{id}, heartbeat POST /v1/federation/children/{id}/heartbeat, DELETE /v1/federation/children/{id}, health/posture GET /v1/federation/health, GET /v1/federation/posture. Follow federation-health.md / federation-assertion-anomaly.md. Undo: DELETE the child registration.


11. Routine operations & runbooks

USCP ships an operator runbook library at codes/runbooks/ (index: runbooks/INDEX.md). Every STRIDE detection signal has a matching playbook (RB-01…RB-27) plus mandatory playbooks (first-24h, cred-rotation, region-failover, break-glass, vendor-continuity, RACI, estate-credential -rotation). Playbooks unrehearsed >180 days fail CI — schedule the drills.

11.1 Daily checklist

  • [ ] GET /healthz/deep all green; GET /readyz OK.
  • [ ] GET /v1/_audit/verify returns a valid chain (tamper check).
  • [ ] Forward-queue drained: GET /v1/observability/forward-queuedead:0 (replay if not, §9.5).
  • [ ] Webhook/notification DLQ clear: GET /v1/webhooks/deliveries?status=dead, GET /v1/notifications/deliveries.
  • [ ] Review new SIEM alerts (GET /v1/siem/alerts), UEBA/ITDR anomalies, deception trips

(GET /v1/deception/trips — any trip = investigate now).

  • [ ] Confirm no unexpected kill-switch state (agentic + estate).

11.2 Weekly checklist

  • [ ] Run UEBA/ITDR/AIOps/correlation scans (POST …/scan, POST /v1/correlation/run).
  • [ ] Snapshot posture: POST /v1/posture/snapshot; review GET /v1/posture/score trend.
  • [ ] Review auditor grants (GET /v1/audit/auditor-grants) — revoke stale ones (§6.3).
  • [ ] Review service tokens (GET /v1/iam/service-tokens) — rotate/expire unused (§2.5).
  • [ ] Review sessions for anomalies (GET /v1/iam/sessions; auth-session-anomaly.md).
  • [ ] Config-drift check on golden configs (POST /v1/config-drift/check).

11.3 Monthly / quarterly

  • [ ] Generate SLA report: POST /v1/sla/reports/generate {"period":"2026-07"} (platform.sla_reporting).
  • [ ] Rotate estate credentials + signing keys (cred-rotation.md, estate-credential-rotation.md).
  • [ ] Break-glass drill (break-glass.md) — provision, activate in a test window, re-seal.
  • [ ] Region-failover drill (region-failover.md).
  • [ ] Tabletop/game-day (tabletop-game-day.md).
  • [ ] Access-certification campaign (POST /v1/iga/certify); leaver reconciliation.
  • [ ] Verify retention floors unchanged (§9.4) and legal-hold classes intact.

11.4 Incident response (P1+)

Follow first-24h.md (RB-00): T+0 detect + classify (P1 = customer-impacting >50% tenants or data integrity), status page → investigating within 5 min; T+0–15 page on-call, assign IC + comms, customer notice within 15 min; T+15–60 contain with two-person approval, status update every 30 min; T+60–24h recover; PIR within 5 business days. Evidence retained 10 years, locked WORM once PIR is published. Snapshot forensic evidence with GET /v1/_audit/activity + session recordings. Use the SEV matrix + RACI in raci.md and comms templates in customer-comms-templates.md.


12. Security hardening checklist

  • [ ] TLS/mTLS — terminate TLS in front of USCP (or via USCP_TLS_CERT/USCP_TLS_KEY); require

mTLS for the licensing/control path where supported (USCP_TLS_CLIENT_CA, GET /v1/licensing/mtls-profile). Confirm tls_min_version ≥ 1.2 (GET /v1/crypto/status).

  • [ ] Protect /metrics at the network layer — it is app-public; never expose it publicly (§9.1).
  • [ ] SSO + phishing-resistant MFA mandatory — set mfa:"webauthn", local_enabled:false once

SSO works (§4.1). Disable the bootstrap local admin.

  • [ ] Break-glass provisioned and drilled (§10.1); custodians distinct; codes offline.
  • [ ] IP allowlist for admin origins where feasible (§4.7).
  • [ ] Sanctions floor confirmed active (CU/IR/KP/SY — always on) and per-tenant geo-fence set to

your policy (§6.8).

  • [ ] Rate limits / WAF / CSRF — front USCP with a WAF; the app enforces SSRF guards on all

outbound targets (webhooks, collectors, detonation, OIDC/SAML). Confirm your reverse proxy adds rate limiting and standard security headers.

  • [ ] Secrets — no plaintext secrets: all secrets from vault/KMS (env *_ref/JWKS), sealed at

rest; service tokens scoped + expiring (§2.5).

  • [ ] RLS verification — periodically prove cross-tenant isolation (see cross-tenant-audit.md);

the binary ships RLS isolation tests.

  • [ ] HYOK/BYOK provisioned for restricted-data tenants (§6.5); lockbox default-deny (§6.4).
  • [ ] Audit chain verified on a schedule (GET /v1/_audit/verify); retention floors enforced (§9.4).
  • [ ] Signed releases — verify before apply: POST /v1/release/verify; SBOM/VEX/SLSA pack

GET /v1/release/pack.

  • [ ] Sender-auth for email notifications (SPF/DKIM/DMARC on your SMTP domain;

internal/identity/senderauth).


13. Admin troubleshooting

Symptom Likely cause Fix
401 STEP_UP_REQUIREDPasskey step-up is stale for a sensitive action (tenant export/delete, estate killswitch, AI promote/retire).Re-authenticate with your passkey when prompted, then retry (§1.5).
403 ADMIN_REQUIREDYou hold operator/viewer, not admin.Have an admin perform it, or get your role elevated (§4.2).
403 MANAGE_REQUIREDYou are viewer/auditor (read-only) on a write action.Use an operator/admin account (§1.3).
403 SCOPE_ESCALATION (token mint)Trying to mint a role/scope you don't hold.Mint with a role ≤ your own (§2.5).
403 SCOPE_DENIED (service token)Token's scopes don't include this route's entitlement.Rotate/mint a token whose scopes include the needed key (§2.5).
402 LICENSE_LIMIT_EXCEEDEDSeat/token cap reached, or capability not licensed.Free seats/tokens (§5.3), or update the license bundle (§5.2).
Whole nav group missing / 403 on a capabilityThat capability isn't in your license.Check GET /.well-known/api-capabilities; obtain the entitlement.
403 TENANT_SUSPENDEDThe tenant is suspended.Resume it (§3.3) — /resume works even while suspended.
422 CONFIRM_REQUIRED (tenant delete)confirm_slug didn't match the slug.Provide the exact slug (§3.5) — this guard protects you.
422 RETENTION_FLOORCold retention set below a regulatory floor.Raise cold_seconds to ≥ the class floor (§9.4).
422 SELF_LOCKOUT (IP allowlist)Your current IP isn't in the new list.Include your IP/CIDR, or PUT {"cidrs":[]} (§4.7).
451 GEO_FENCEDRequest from an OFAC-sanctioned country, or blocked by your geo-fence.Sanctions floor cannot be lifted; adjust your own fence if it's yours (§6.8).
403 BREAK_GLASS_QUORUMFewer than threshold distinct valid codes presented.Gather the quorum from distinct custodians (§10.1.2).
409 ALREADY_PROVISIONED (HYOK)An active KEK already exists.Revoke first (irreversible!), then provision (§6.5).
403 KEY_REVOKED on decryptThe HYOK key was revoked — data is intentionally undecryptable.This is by design; provision a new key for new data (§6.5).
Locked out — IdP down, no one can log inSSO/MFA path failure.Activate break-glass POST /admin/break-glass (public, works when IdP is down) (§10.1.2).
GET /readyz failingDB unreachable or migrations pending.Check DB connectivity and migration state; see GET /healthz/deep.
Webhook/notification not arrivingDelivery in DLQ (dead after 6 attempts).Inspect GET /v1/webhooks/deliveries?status=dead, fix endpoint, replay (§9.7–9.8).
Logs not reaching SIEMCollector down; batches dead-lettered.GET /v1/observability/forward-queue; fix collector; replay (§9.5).
502 KMS_UNAVAILABLEHYOK KMS provider unreachable.Restore KMS connectivity; retry provision (§6.5).
Audit chain verify failsPossible tampering.Treat as SEV incident; follow first-24h.md + cross-tenant-audit.md.

14. Complete admin operation reference

Every route registered by the binary (codes/ci/entitlements.yaml), grouped by domain. Auth column: public = un-gated; otherwise the value is the entitlement key that gates it. Where a handler adds a role gate, it is noted [admin] (requireAdmin) or [manage] (requireManage) or [step-up].

System, health & discovery (public)

Method Path Gate What it does
GET/healthz, /livez, /readyz, /healthz/deeppublicLiveness / readiness / deep dependency health
GET/metricspublicPrometheus scrape (protect at network layer)
GET/v1/licensing/statuspublicLicensing posture
GET/v1/crypto/statuspublicCrypto manifest, FIPS, PQC readiness
GET/openapi.yaml, /v1/openapi.json, /asyncapi.yamlpublicAPI/event contracts
GET/.well-known/api-capabilitiespublic90-capability matrix
GET/.well-known/security.txt, /openapi.json, /federationpublicRFC 9116 / contract / federation topology
GET/publicAdmin console SPA + assets

Auth & bootstrap (public / pre-auth)

Method Path Gate What it does
GET/auth/login, /auth/callbackpublicOIDC start / callback
POST/auth/local/loginpublicLocal-account login (admin-enabled)
GET/auth/ssopublicEmail-domain home-realm discovery → per-tenant IdP
GET/auth/saml/metadata, /auth/saml/{id}/loginpublicSP metadata / start SAML
POST/auth/saml/acspublicSAML assertion consumer
POST/auth/logoutpublicSession teardown
POST/auth/webauthn/register/begin\finish, /auth/webauthn/login/begin\finishpublicMFA enrolment / verification
POST/admin/break-glasspublic§98 break-glass invoke (never gated)

Tenants & control-plane shell

Method Path Gate What it does
POST/v1/tenantscontrol_plane.shellCreate a tenant
GET/v1/tenants/{id}control_plane.shellGet a tenant
POST/v1/tenants/{id}/suspendcontrol_plane.shell [admin]Suspend
POST/v1/tenants/{id}/resumecontrol_plane.shell [admin]Resume
POST/v1/tenants/{id}/exportcontrol_plane.shell [manage][step-up]Export (portability)
DELETE/v1/tenants/{id}control_plane.shell [admin][step-up]Delete + crypto-erase (irreversible)
GET/v1/mecontrol_plane.shellWho am I
GET/v1/_audit/verify, /v1/_audit/activitycontrol_plane.shellAudit chain verify / activity
POST/admin/break-glass/setupcontrol_plane.shell [admin, bootstrap tenant]Provision break-glass codes
GET/v1/dualcontrolcontrol_plane.shell [admin]List pending dual-control
POST/v1/dualcontrol/{id}/approve\rejectcontrol_plane.shell [admin]Four-eyes approve/reject

Identity, IAM & RBAC

Method Path Gate What it does
GET/PUT/v1/iam/auth-settingscontrol_plane.shellRead/set auth policy (mfa, local/oidc)
GET/POST/v1/iam/local-userscontrol_plane.shellList/create local users
DELETE/v1/iam/local-users/{username}control_plane.shellDelete user (kills sessions)
POST/v1/iam/local-users/{username}/passwordcontrol_plane.shellReset password
POST/v1/iam/totp/enroll\verifycontrol_plane.shellTOTP enrol/verify
GET/PUT/v1/iam/external-backendscontrol_plane.shellAD/LDAP/RADIUS backends (replace-all)
GET/PUT/v1/iam/role-mappingscontrol_plane.shellGroup/domain→role mappings
GET/POST/v1/iam/idpscontrol_plane.shellList/register OIDC IdP
DELETE/v1/iam/idps/{id}control_plane.shellRemove OIDC IdP
GET/POST/v1/iam/saml-idpscontrol_plane.shellList/register SAML IdP
DELETE/v1/iam/saml-idps/{id}control_plane.shellRemove SAML IdP
POST/GET/v1/iam/service-tokenscontrol_plane.shell [admin]Mint/list service tokens
POST/v1/iam/service-tokens/{id}/rotatecontrol_plane.shell [admin]Rotate token
DELETE/v1/iam/service-tokens/{id}control_plane.shell [admin]Revoke token
GET/PUT/v1/iam/ip-allowlistcontrol_plane.shell ([manage] on PUT)Tenant IP fence
GET/v1/iam/sessionscontrol_plane.shellList sessions
DELETE/v1/iam/sessions/{id}control_plane.shellRevoke a session
POST/v1/iam/sessions/revoke-otherscontrol_plane.shellRevoke my other sessions
GET/PUT/v1/iam/rbac/settingscontrol_plane.shell ([manage] on PUT)Fine-grained RBAC toggle
GET/v1/iam/rbac/grants, /v1/iam/rbac/effectivecontrol_plane.shellInspect grants / self
PUT/DELETE/v1/iam/rbac/roles/{role}control_plane.shell [manage]Set/reset role permissions
GET/POST/v1/rbac/rolescontrol_plane.shell [admin]Coarse role catalog
DELETE/v1/rbac/roles/{name}control_plane.shell [admin]Delete coarse role
POST/GET/GET/PATCH/DELETE/scim/v2/Users[/{id}]control_plane.shell (bearer)SCIM user provisioning
POST/GET/PATCH/scim/v2/Groups[/{id}]control_plane.shell (bearer)SCIM groups
GET/scim/v2/ServiceProviderConfig, /ResourceTypes, /Schemascontrol_plane.shellSCIM discovery
POST/v1/org/invite, /v1/org/childrencontrol_plane.shellOrg invite / child orgs
GET/v1/org/children, /v1/org/rollup, /v1/org/parentcontrol_plane.shellOrg hierarchy
DELETE/v1/org/children/{id}control_plane.shellRemove child org

Policy & governance

Method Path Gate What it does
POST/v1/policy/simulatecontrol_plane.shellSimulate a decision
GET/POST/v1/policiescontrol_plane.shellList / draft policy
POST/v1/policies/{id}/approvecontrol_plane.shellPublish (second approver)
GET/GET/POST/POST/PATCH/DELETE/POST/v1/policy/firewall[...]control_plane.estate_policyHost firewall policy CRUD + import/live/duplicate
GET/PUT/v1/geo-fencecontrol_plane.shell ([admin] on PUT)Geo-fence (OFAC floor always on)
GET/POST/DELETE/v1/dlp/rules[/{id}]control_plane.dlpDLP rules
POST/v1/dlp/scancontrol_plane.dlpScan content
GET/v1/dlp/egress-eventscontrol_plane.dlpEgress event log
POST/GET/v1/privacy/consentcontrol_plane.shellConsent receipts (GPC-aware)
GET/v1/privacy/dsar/exportcontrol_plane.shellDSAR export
POST/v1/privacy/dsar/erasecontrol_plane.shellDSAR erase (irreversible)
GET/v1/privacy/dsar/requestscontrol_plane.shellDSAR register

Audit, auditor & lockbox/HYOK

Method Path Gate What it does
GET/v1/audit/evidence-locker[/export]platform.customer_auditor_roleEvidence locker (role+grant)
POST/GET/v1/audit/auditor-grantscontrol_plane.shell [admin]Grant/list auditor time-box
DELETE/v1/audit/auditor-grants/{subject}control_plane.shell [admin]Revoke grant
POST/PATCH/POST/v1/status/incidents, /components, /maintenancecontrol_plane.shellStatus-page writes
GET/POST/v1/lockbox/requestsplatform.customer_lockboxLockbox requests
POST/v1/lockbox/requests/{id}/decisionplatform.customer_lockboxApprove/deny access
GET/v1/lockbox/hyok/statusplatform.hyokHYOK key status
POST/v1/lockbox/hyok/provision\encrypt\decrypt\revokeplatform.hyokHYOK key lifecycle (revoke irreversible)
GET/v1/lockbox/byokplatform.byok_envelopeBYOK envelope posture
GET/v1/metering/rollup, /v1/metering/eventscontrol_plane.shellMetering
POST/v1/metering/exportplatform.customer_auditor_roleExport metering

Observability & platform ops

Method Path Gate What it does
GET/v1/logs/exportcontrol_plane.shellExport tenant logs
POST/GET/v1/logs/ingest, /search, /flows, /topology, /seedcontrol_plane.shellLog ingest/search/flows/topology
GET/POST/DELETE/v1/logs/collectors[/{id}]control_plane.shell ([manage] on POST/DELETE)Log collectors
GET/POST/DELETE/v1/logs/investigations[/{id}]control_plane.shellInvestigations
GET/POST/v1/observability/forward-queue[/replay]control_plane.shellForward-queue DLQ + replay
GET/PUT/v1/observability/retentioncontrol_plane.shellRetention config (regulatory floors)
GET/PUT/v1/admin/log-levelcontrol_plane.shell ([manage] on PUT)Runtime log level
GET/POST/v1/events/stream, /v1/events/emitcontrol_plane.shellEvent stream / emit
GET/POST/v1/collab/{doc}[...]control_plane.shellCollaborative docs
GET/POST/POST/DELETE/GET/POST/v1/notifications/channels[...]control_plane.shell ([manage] on writes)Alert channels + deliveries + replay
POST/GET/POST/GET/POST/v1/webhooks[...]control_plane.shell ([manage] on writes)Webhooks + DLQ + replay
GET/v1/sre/dashboardsplatform.sre_dashboardsSRE dashboard specs
GET/v1/analytics/datasets; POST /v1/analytics/querycontrol_plane.shellAnalytics
POST/v1/mcpcontrol_plane.shellMCP endpoint
POST/GET/DELETE/POST/POST/v1/itsm/connectors[...]control_plane.shellITSM connectors + test/push

Estate management

Method Path Gate What it does
POST/GET/v1/estate/targetscontrol_plane.estate_inventoryRegister/list targets
GET/v1/estate/capabilities, /topology, /config-appliedcontrol_plane.estate_inventoryEstate maps
GET/POST/v1/estate/host-keys[/reset]control_plane.estate_inventoryHost-key pins
GET/POST/v1/estate/agents[/enroll]control_plane.estate_inventoryAgents
POST/GET/DELETE/v1/estate/credentials[/{id}]control_plane.estate_pamPAM credentials
GET/v1/estate/credential-providers, /pam-connectorscontrol_plane.estate_pamProviders/connectors
POST/DELETE/v1/estate/pam-connectors[/{id}]control_plane.estate_pamPAM connectors
POST/POST/GET/v1/estate/sessions[/{id}/exec,/recording]control_plane.session_brokerBrokered recorded sessions
POST/v1/estate/breakglass/requestcontrol_plane.session_brokerEstate break-glass request
POST/GET/v1/estate/breakglass/{id}/approve, /v1/estate/breakglasscontrol_plane.session_brokerApprove/list
POST/v1/estate/packs/plan\applycontrol_plane.estate_policyPolicy-pack rollout
POST/v1/estate/fanoutcontrol_plane.estate_responseFan-out action
POST/v1/estate/killswitchcontrol_plane.estate_response [step-up]Trip estate kill-switch
POST/v1/estate/killswitch/resetcontrol_plane.estate_response [step-up][admin]→dual-controlReset (opens 4-eyes)
POST/GET/GET/GET/POST/v1/ot/assets, /zones, /safety-posture, /simulate-seedcontrol_plane.ot_visibility / ot_readonlyOT/ICS (read-only)
POST/GET/GET/POST/DELETE/v1/fleets[...]control_plane.fleet_mgmtFleets + members
POST/GET/DELETE/POST/v1/ztp/tokens[/{id}], /redeemcontrol_plane.ztpZero-touch provisioning
GET/GET/PUT/v1/sovereignty/regions, /topology, /residencyplatform.sovereign_topology_apiData residency

Security operations

Method Path Gate What it does
POST/POST/POST/GET/GET/v1/soc/agents/investigate, /execute, /killswitch[/reset], /scorecards, /modelcontrol_plane.agentic_socAgentic SOC + kill-switch
GET/GET/GET/POST/POST/POST/POST/POST/GET/v1/ctem/exposures, /scope, /vectors, /assess, /attack-path, /validate, /cve-feed[/fetch], /assessmentscontrol_plane.exposure_mgmt / attack_path / validation_basCTEM
POST/GET/GET/v1/detections[/coverage]control_plane.detection_hub / attack_coverage_mapDetections
POST×6/GET×2/DELETE/v1/detect/rules[...], /findings[/{id}/escalate]control_plane.detection_hubDetection rules + findings
POST/POST/v1/lake/ingest, /querycontrol_plane.security_data_lake / federated_searchData lake
POST/GET/POST/POST/GET/v1/intel/indicators[/{id}/suppress\unsuppress], /collections/{id}/objectscontrol_plane.threat_intelThreat intel (TIP)
GET/POST/GET/PATCH + notes/transition/assign/priority/escalate/v1/soar/cases[...]control_plane.case_managementCases
POST/v1/soar/playbooks/{id}/runcontrol_plane.soarRun playbook
POST/v1/soar/cacao/export\importcontrol_plane.soarCACAO
GET/POST/GET/GET/v1/ueba/entities[/{entity}], /scan, /anomaliescontrol_plane.uebaUEBA
GET/POST/GET/v1/itdr/signals, /scan, /detectionscontrol_plane.itdrITDR
POST/GET/GET/v1/correlation/run, /live, /incidentscontrol_plane.correlationCorrelation
GET/POST/GET/v1/posture/score, /snapshot, /historycontrol_plane.posture_scorePosture score
POST/GET/DELETE/GET/v1/risk/scenarios[/{id}], /portfoliocontrol_plane.risk_quantFAIR risk
GET/GET/v1/siem/overview, /alertscontrol_plane.siem_hubSIEM hub
GET/POST/GET/v1/aiops/health, /scan, /anomaliescontrol_plane.aiopsAIOps
POST/GET/DELETE/POST/GET/v1/deception/honeytokens[/{id}], /trip, /tripscontrol_plane.deceptionDeception
POST/GET/GET/v1/detonation/submit, /jobs, /providercontrol_plane.detonationDetonation sandbox
POST/GET/POST/GET/POST/v1/drp/assets, /ingest, /findings, /simulatecontrol_plane.drpDigital risk
POST/GET/POST/GET/v1/config-drift/baselines, /check, /driftscontrol_plane.config_driftConfig drift
POST/GET/DELETE/POST/v1/telemetry/routes[/{id}], /evaluatecontrol_plane.telemetry_fabricTelemetry fabric
GET/POST/POST/GET/v1/collective/status, /optin, /iocscontrol_plane.collective_defenseCollective defense
POST/POST/GET/POST/v1/policy-twin/simulate, /suites[/{id}/run]control_plane.policy_twinPolicy twin
POST/POST/GET/POST/POST/v1/iga/sod/check, /certify, /campaigns, /leaver, /joinercontrol_plane.sod / access_certification / igaIGA

Licensing, releases, suite/MSP & self-serve

Method Path Gate What it does
GET/v1/licensing/airgap, /online, /mtls-profileplatform.airgap_licensing / online_licensing / mtls_license_profileLicensing posture
POST/v1/licensing/continuity/activateplatform.continuity_escrow [admin, dual-control]Continuity activate (irreversible)
GET/v1/licensing/continuitypublicContinuity posture
GET/POST/GET/POST/v1/updates/check, /status, /applycontrol_plane.shellUpdate orchestration
GET/PUT/GET/PUT/v1/release/channels[/{name}], /assignmentplatform.release_channels ([admin] on PUT)Release channels (stable/beta/edge)
GET/v1/release/parity, /packplatform.self_hosted_release_parity / signed_release_packParity / supply-chain pack
POST/v1/release/verifyplatform.signed_release_verificationVerify a signed release
GET/v1/contracts[/{name}], /v1/federation/openapiplatform.contract_registry / federation_openapiContract registry
POST/GET/GET/v1/migrate/import, /jobs, /sourcescontrol_plane.competitor_migrationCompetitor migration
POST/GET/DELETE/GET/v1/msp/managed[/{id}], /soccontrol_plane.msp / comanaged_soc ([admin] on writes)MSP / co-managed SOC
POST/GET/GET/POST/DELETE/GET/GET/v1/federation/children[...], /health, /posturesuite.federation_coreParent-suite federation
GET/PUT/DELETE/POST/GET/v1/suite/routes, /airgap-bundlessuite.cross_suite_routing / airgap_bundle_distributionCross-suite routing / air-gap bundles
GET/v1/suite/otel, /licensing-client, /audit-pipeline, /shell-configsuite.shared_* / admin_shell_uiShared suite services
POST/GET/POST/v1/modules[/register,/{name}/reload]platform.module_hot_reloadModule hot-reload
POST/POST/GET/v1/data-ops/export-snapshot, /purge, /jobsplatform.self_service_data_opsSelf-service data ops
POST/GET/POST/v1/ai/models[/{id}/promote,/retire]control_plane.ai_spm ([step-up] on promote/retire)AI model registry
GET/POST/v1/sla/reports[/generate]platform.sla_reportingSLA reports
GET/v1/trust/portal/privateplatform.trust_portal_automationPrivate trust portal
GET/GET/GET/GET/POST/v1/trust/portal, /marketplace/catalog, /roadmap, /status[/incidents], /status/subscriptionspublicSelf-serve discovery

Appendix A — irreversible actions (no undo)

Read twice before running any of these:

  • DELETE /v1/tenants/{id} — crypto-erases the HYOK KEK; encrypted data incl. backups

unrecoverable (§3.5).

  • POST /v1/lockbox/hyok/revoke — revoked data stays undecryptable forever (§6.5).
  • POST /v1/privacy/dsar/erase — user + credentials deleted (§6.6).
  • POST /v1/licensing/continuity/activate — append-only, one-way (safe: preservation-only) (§10.4).
  • POST /v1/ai/models/{id}/retire — no un-retire (§7.5, model lifecycle).

Appendix B — the safe alternatives to irreversible actions

  • Instead of deletesuspend (§3.2) or export first (§3.4).
  • Instead of HYOK revoke → deny future lockbox requests (§6.4) and rotate credentials.
  • Before any policy change → simulate (§6.1) and policy-twin regression (§7.10).
  • Before retention changes → confirm the floor (§9.4).

End of Administrator Guide. Runbooks referenced live in codes/runbooks/; the machine-readable route contract is codes/ci/entitlements.yaml; the capability matrix is codes/docs/capability-matrix.md.