Operator API
The controlplane's operator API manages identity, policy, and the desired state of each enclave. It authors and publishes; the enclave converges. It never receives sovereign data.
Machine-readable reference (OpenAPI)
The exhaustive, always-current reference for every route — paths, parameters, request/response schemas, status codes, and the capability each route requires — is a generated OpenAPI 3.2 document. It is rendered from the live command tree, so it never drifts from what the binary serves:
- Controlplane operator API —
/openapi/controlplane-operator.json - Enclave (broker) operator API —
/openapi/enclave-broker-operator.json
This page is the orientation: the shape of the API, how authentication works, and the model behind it. For the endpoint-by-endpoint contract, read the spec.
Shape
The API is served by dvx serve controlplane over TLS. Responses are JSON, and
errors are RFC 9457 problem documents. Every authoring action is audited on a
first-class audit stream that is separate from application logging — both land
on stdout and are distinguished by a top-level stream key.
Alpha software: the surface is real but still landing, and mounting is
feature-gated. GET /healthz is always served. When no OIDC issuer is
configured the controlplane runs in skeleton mode and serves only a placeholder
that reports the operator API as not-yet-implemented. With an issuer configured,
the full IAM surface is mounted, and individual route families appear only when
their backing capability was wired at startup (policy authoring, config
authoring, metadata channels, agent provisioning, and broker registration are
each enabled independently). The OpenAPI document describes the full surface;
a given controlplane may mount a subset.
Authentication
Every /api/v1/… route is wrapped by bearer authentication. There are two
authenticators, composed fail-closed: Zitadel (the OIDC issuer) for human and
machine principals, and — for the agent-operator caller class — the
controlplane’s own short-lived token issuer. Which authenticator accepts a token
determines the caller’s principal kind, structurally: an agent token can
never be read as a human principal, and no claim a caller sends can choose the
kind. Per-route capability checks are layered on top. In the OpenAPI document
this is the global bearerAuth security scheme, and each route records the
capability it needs as an x-required-capability extension.
GET /api/v1/whoami is the exception — it requires only a valid token and
returns the caller’s own identity and effective capabilities. Capability targets
are derived from the route path, never from the request body, so a
cross-tenant id in a payload cannot widen authority — with one documented,
narrowing-only exception: group-aware agent create
(POST /api/v1/orgs/{org}/agents). There the body group enters the
authorization decision as the attribute being authored on the agent, not as a
borrowed coordinate, and only ever to narrow — the handler requires the
caller to already hold agents:manage over that group (and, on an Upsert, over
the existing agent’s stored group), so a body group the caller does not manage
is a 403. The authorization org stays path-derived exactly as everywhere else.
See docs/http-api-conventions.md rule 6 and docs/iam.md §8.
The CLI dials the API as an authenticated client. dvx whoami, dvx grant,
dvx policy, dvx config, dvx agent, and dvx enclaves take a
--controlplane base URL plus caller-authentication flags: --zitadel-key
(a service-account key that mints its own token) with --issuer and optional
--audience, or a static --token-file; and --ca-cert to trust the API’s TLS
certificate (--insecure-skip-verify for dev only).
Agent operators
A running agent can call this API without ever holding a credential. Its egress toward the controlplane is terminated at the enclave gateway (like all agent egress); the gateway relays the agent’s attested identity through the enclave broker and presents a 60-second, controlplane-issued agent-operator token itself on the agent’s behalf. The token never enters the agent’s view, so pod death stops presentation, and a contained agent is refused at the mint before the controlplane is even consulted.
Authorization for this caller class is deliberately tiny: an org admin grants an
agent:<uid> principal the auditor role (dvx grant create --org <org> --principal agent:<uid> --role auditor) — org-scoped only, validated against the
grant’s own org. A kind condition on every capability-gated route refuses the
agent-operator kind before grants are even loaded, so even an over-granted
agent reaches exactly one surface: the own-actor audit read. An agent
auditing its own trail sees precisely the records attributed to
agent:<uid>, told apart from human operators in the audit record by a
first-class actor_kind field.
Authoring desired state
The operator authors; the enclave converges. Writing through the authenticated
API (rather than the CLI’s break-glass direct-to-DB path) publishes the change
and fires an SSE nudge so the target enclave re-pulls. Every change is attributed
to the human or workload that made it, and sovereign secret bytes never traverse
this API — they are authored enclave-side through the broker (dvx secret),
whose own surface is the enclave (broker) operator API spec linked above.
The enclave broker also serves the approval loop on this shape: pending
tool-call approvals list, decision, and a live SSE feed at
/api/v1/approvals/*, bearer-authenticated and org-scoped exactly like these
routes. Work it with dvx approvals list/approve/deny/stream — see
Inspect LLM responses and approve tool calls.
Audit
When an audit store is configured the controlplane also mounts a read-only audit
query API at /api/v1/audit/* — the same routes the enclave broker serves, over the
controlplane’s own audit store. Query it with dvx audit --source controlplane.
Those routes are included in the OpenAPI document above.
Audit visibility is an authorization decision the server enforces, not a UI filter: the scope is derived from the caller’s grants and applied as a mandatory predicate on every query and the live SSE stream. A caller may narrow within its scope but can never widen it — a missing filter means “everything I’m allowed to see”, never “everything”. Three read personas:
- regular member (
audit:read) — own org, and own actor only. Records attributed to other principals, and un-attributed/system records, are not returned (fail-closed). An agent auditor lands here too, pinned to its ownagent:<uid>actor — that own-actor read is the agent-operator class’s entire surface (see “Agent operators” above). - org admin / auditor (
audit:read:all) — own org, all actors. - platform operator (
platform:audit:read) — a standing read of every org’s controlplane audit in one call (the controlplane’s own identity/policy audit is non-sovereign platform observability). Narrow to one org with?org=. This is a controlplane-only capability: it grants no access to any org’s sovereign enclave audit, and it never confers standing authority inside a customer org for anything else.
Cross-org reads are reciprocally transparent: when a platform operator reads an org’s controlplane audit, that read is itself audited into the target org’s own audit view, so an org can always see who-outside-it accessed its records.
The enclave broker serves the same /api/v1/audit/* shape over the enclave’s
sovereign audit store, but authorizes it entirely enclave-locally (from
the enclave’s own grants, never the controlplane): an enclave org admin sees all
actors in that enclave, a member sees its own, and a caller with no enclave-local
audit grant is refused. There is no cross-enclave aggregation — a client that
wants several enclaves’ audit calls each broker in turn.