Daevix Docs

Run an agent against a local enclave

Walk the agent-as-operator path end to end on a local kind cluster: create an agent through the operator API, grant its identity the auditor role, wire the gateway's controlplane-bound token injection, and watch the agent read its own audit trail — all without the agent ever holding a credential.

View as Markdown Edit on Tangled

This guide grounds the Agent as operator page in a concrete task. By the end, an agent pod in your local enclave will call the controlplane’s audit API and read its own records, with the enclave gateway minting and presenting a 60-second token on its behalf. To also govern what the agent’s model responses contain, see Inspect LLM responses and approve tool calls.

Before you start

  • A local dev cluster with the controlplane and enclave data plane deployed — the path documented in the repository’s deploy/kind/README.md (make kind-up cilium-install deploy-iam iam-migrate deploy-enclave, then the seeds). This wires the gateway in the shape described below; you do not configure it by hand. See Register an enclave for what the broker registration step does.
  • The dvx binary (make build) and kubectl pointed at the kind cluster.
  • An org to work in — the dev default is dev-org (created by the deploy targets).

Create the agent

Agents are desired state, authored through the controlplane’s operator API — you never touch Kubernetes, and the CLI never applies a Pod manifest. The broker on the enclave reconciles each desired agent into a dedicated namespace, an identity socket, and a Pod.

dvx
 dvx agent create \    --controlplane https://cp.example.com \    --org dev-org \    --uid agent-a --name agent-a \    --enclave enc_7f3a... \    --image curlimages/curl:8.11.1 \    --command /bin/sh --command -c --command 'sleep 3600'

--enclave places the agent in a registered enclave; --uid names its namespace and IMDS socket (omit it to get a generated one like nostalgic-otter). Isolation is a property of the ENCLAVE, not the agent: author the enclave’s requested isolation class with PUT /api/v1/enclaves/{enclave}/isolation-class (node-cvm runs every agent in that enclave as a Kata VM pod instead of plain runc containers). Watch the broker reconcile it:

sh
 kubectl wait --for=condition=Ready pod -l dvx.io/agent-uid=agent-a -A --timeout=150s kubectl exec -n agent-a agent-a -- curl -s --unix-socket /run/dvx/imds.sock http://imds/api/v1/identity

Grant the auditor role

An agent’s authority is an ordinary grant on the agent:<uid> principal — org-scoped only (the grant API validates the agent actually belongs to that org). The v1 role for agents is auditor: an org-scoped, own-actor audit read, and nothing else.

dvx
 dvx grant create \    --controlplane https://cp.example.com \    --org dev-org \    --principal agent:agent-a \    --role auditor

The role floor is structural, not a convention: a kind condition on every capability-gated route refuses the agent-operator kind before grants are even loaded, so even an agent mistakenly granted admin reaches no manage or read surface. The own-actor audit read is the entire agent surface.

The gateway’s controlplane-bound wiring

For agent egress toward the operator API to become token injection, the node-local gateway needs to know which hosts to inject on and how to verify the controlplane. In the in-repo dev deployment this is already set in deploy/enclave/gateway-daemonset.yaml; the same knobs exist as dvx serve gateway flags:

DaemonSet envCLI flagwhat it does
DVX_CP_API_HOSTS--cp-api-hostoperator-API host suffixes whose agent egress gets a 60s token injected
DVX_AGENT_TOKEN_BROKER_URL--agent-token-broker-urlbroker local mTLS channel the gateway relays the agent’s SVID cert to for token mints
DVX_AGENT_TOKEN_BROKER_CA--agent-token-broker-caCA verifying the broker’s local serving cert
DVX_AGENT_TOKEN_BROKER_CLIENT_CERT / _KEY--agent-token-broker-client-cert / -keythe broker-issued dvx-gateway-broker-client identity
DVX_CP_API_CA--cp-api-caCA verifying the controlplane’s serving cert on the re-originated hop

Two pieces of wiring exist but need no per-deployment work: the broker stamps a fixed east-west egress-redirect rule for the controlplane into each agent’s namespace (the CP is an in-cluster destination, not “world” — without the rule the connection was dropped by elimination and never mediated), and the broker stamps the gateway MITM CA into the agent’s trust store, so the agent’s TLS to the intercepted host just verifies. The agent itself needs no configuration, no CA bundle of its own choosing, and no token.

Allow the operator-API host in policy

The gateway governs every mediated connection — including the CP-bound one — against destination policy. Add the operator-API host to the org’s allow list, restating any hosts already allowed at that scope: a destination write replaces the scope’s whole list (see Author and pull policy).

dvx
 dvx policy destination \    --controlplane https://cp.example.com \    --org dev-org \    --allow dvx-controlplane.dvx-system.svc.cluster.local \    --allow example.com --allow example.org --allow example.net

(The dev baseline example.* entries here are what make seed-demo-policy authors; keep whatever your own scope list holds.)

The agent reads its own audit trail

Now the payoff. From inside the agent pod, call the controlplane’s audit API by its in-cluster host — no token, no key, nothing mounted:

sh
 kubectl exec -n agent-a agent-a -- \    curl -s https://dvx-controlplane.dvx-system.svc.cluster.local:8443/api/v1/audit/events{  "events": [    {      "action": "agenttoken.mint",      "actor": "agent:agent-a",      "actor_kind": "agent-operator",      "outcome": "allow",      ...    }  ]}

What just happened: Cilium redirected the connection into the gateway; the gateway terminated TLS with a leaf the agent already trusts, relayed the agent’s attested SVID cert (never its key) through the broker’s local mTLS channel, and the controlplane — verifying the cert against the enclave’s pinned agent-identity CA and the agent’s containment state — minted a 60-second, audience-scoped token under its own signing key. The gateway presented the bearer itself on the re-originated hop and cached the token for at most its lifetime. The agent’s view never changed: a plain HTTPS call to a host it cannot otherwise reach.

The read is scoped server-side to the caller’s own actor, so the response contains exactly the records attributed to agent:agent-a — that own-actor read is the agent-operator class’s entire surface.

Verify from the operator side

As a human operator, read the same trail and see the agent’s actions told apart by the first-class actor_kind field:

dvx
 dvx audit events --source controlplane \    --controlplane https://cp.example.com \    --action agenttoken.mint

The gateway side is audited too: each injection flow lands as a mitm-agent-token row in the enclave audit, so both the mint and the presentation are attributable.

Containment, not just TTL

Revoking the agent is immediate and layered. dvx grant delete removes the authority outright; containing the agent makes the mint endpoint refuse it and the gateway drop the cached token — both enforced by mediation, before the controlplane is consulted. The only residual is the ≤60-second lifetime of a token already minted, and pod death stops presentation entirely.

dvx
 dvx grant delete --controlplane https://cp.example.com \    --org dev-org --id <grant-id> dvx agent delete --controlplane https://cp.example.com \    --org dev-org --uid agent-a