Daevix Docs

Security Model

Open as Markdown

Daevix's security posture, principles, trust boundaries, and built-in platform guarantees.

Security Model

Core Posture: Never Trust the Agent

The platform treats the agent process as untrusted. This is not a pessimistic assumption - it is the only correct assumption, because:

  1. The agent might not be ours - Operators can run any agent code (BYOA). The platform cannot guarantee what’s running inside the container.
  2. The agent could modify itself - Depending on its access level, an agent may alter its own binary, configuration, or behavior. Even without admin rights, it may find ways to act outside its intended scope.
  3. The agent acts autonomously - It makes decisions that are not fully predictable by the operator.
  4. LLMs can be manipulated - Prompt injection, jailbreaking, and adversarial inputs are real risks that can cause agents to take unintended actions.

Therefore, all security controls exist outside the agent process, at layers the agent cannot subvert (or at least, where subversion is detectable).

The agent host is the trust boundary

The unit of containment is the agent host - the machine an agent runs on, in whatever form the enclave provisions it (see Agent Host). Five invariants define that boundary, and they hold whatever the compute backend is:

  1. Everything on the host is a single untrusted domain. The agent process, any Daevix runtime alongside it, and every credential delivered to it share one trust domain. Compromise of any part is compromise of all of it.
  2. The agent is expected to hold administrative rights on its own host. That is the managed-workstation model rather than an oversight: organizations that give employees admin on their laptops give agents the same. It follows that privilege separation inside the host is deliberately not a security control. The platform enforces a boundary around the host, not within it.
  3. A host is never shared across trust domains. One agent per host, so the blast radius of a compromise is that one agent.
  4. The boundary is not perforated. No mechanism may bridge platform or peer infrastructure into an agent host: no host filesystem, no host network identity, no credential broader than that agent’s own scope.
  5. Enforcement lives at or outside the boundary. Egress policy applied off-host, credential scoping and lifetime, and keeping platform-wide credentials off the host entirely.

Because containment rests wholly on that boundary, there is a floor on what can serve as a host: an isolation domain that does not share a kernel with the platform or with other agents. A hardware-virtualized machine clears it; a shared-kernel container does not.

The agent runtime is inside that boundary

Invariant 2 has a consequence worth stating outright: the Daevix runtime shipped alongside an agent is not a security boundary. It occupies the same untrusted domain as the agent’s own code and is granted nothing the agent could not obtain for itself, so whatever it holds - including the platform JWT in memory - sits inside the blast radius. What protects the platform is the scope and lifetime of those credentials, not any separation from the agent.

On Kubernetes, an agent runs as a two-container pod: a dvxir sidecar (which owns bootstrap, JWT refresh, the WireGuard tunnel, and SSH) alongside the operator’s unmodified main container. The sidecar is not a security boundary - it lives in the same untrusted trust domain as the agent image and is granted nothing the agent did not already have. Identity is delivered to the main container through shared volumes (the JWT read-only, the dvxir binary at /opt/dvx/bin/dvxir), while the sidecar’s refresh-token state stays on a sidecar-only volume the main container never mounts. Enforcement stays entirely at the layers below. See Architecture for the topology.

Principles

Authenticate at Every Boundary

Every component-to-component interaction requires authentication:

FlowMechanism
Agent → EnclavePlatform JWT (ES256, 5-minute lifetime)
Agent → LLM proxyNetproxy-forwarded identity (service-signed headers) or platform JWT - see Agent identity
Enclave → Control PlaneAPI key (SHA-256 hashed)
Operator (dvx CLI) → Control Plane APIOperator JWT (IAM-issued)

No component accepts unauthenticated requests for state-changing operations. The only unauthenticated endpoint is GET /v1/agent/discover (read-only enclave capabilities).

Defense in Depth

No single control is sufficient. Daevix layers network controls, host controls, platform controls, and (optionally) agent-level controls. A failure at one layer should be caught by the next.

Least Privilege

  • Agents receive only the credentials they need (scoped secrets, short-lived JWTs).
  • An agent host carries no credential broader than that one agent’s own scope, and in particular nothing that grants access to the platform’s own infrastructure.
  • NetworkPolicy restricts agent egress to DNS, the enclave, the proxy, and the public internet - no east-west traffic to private services.
  • Each tier migrates only its own database: the control plane applies migrations/controlplane/ against the controlplane database, and the enclave applies its own migrations/enclave/ set against a separate enclave database. Neither can migrate the other’s schema.

Secrets Never at Rest in Plaintext

  • All tokens (bootstrap, refresh, enclave API keys) are stored as SHA-256 hex digests. The database never sees raw tokens.
  • Agent secrets are encrypted with AES-256-GCM (random 12-byte nonce, prepended to ciphertext). Decrypted only during delivery to the agent.

Fail Closed

  • Missing or invalid authentication → 401.
  • CIDR mismatch → 403.
  • Token replay → revoke the entire token family + 409.
  • Unknown errors → generic 500 to the client, real error logged server-side.
  • No fallback to insecure modes in production.

Four Control Layers

Daevix uses a defense-in-depth model with four layers of security controls, each enforced at a level the agent cannot easily bypass.

Layer 1: Network Controls

Network controls restrict what the agent can reach. These are enforced by the organization’s network infrastructure - firewalls, load balancers, and cloud security groups.

ControlDescription
Egress filteringAgents can only reach services they need. A K8s deployment agent doesn’t need access to the email server.
TLS inspectionThe platform terminates agent egress at a TLS-intercepting proxy. The interception CA is delivered into the agent at runtime via the merged trust bundle (see Fail-Closed CA Trust) - no rebuilt base image is required.
DNS filteringBlock access to known-bad domains, restrict to approved services.
Network segmentationAgent hosts placed in a dedicated network segment with its own rules, separate from production and employee workstations.

In Kubernetes deployments, Daevix enforces network isolation per agent via NetworkPolicy. Each agent namespace gets a policy (agent-isolation) that:

  • Denies all ingress
  • Allows egress to DNS (port 53)
  • Allows egress to the enclave (ports 8444/8445)
  • Allows egress to the internet (excluding private ranges 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)

All agent egress is additionally redirected through the network proxy. For bring-your-own-image agents the redirect covers all outbound TCP (not just 80/443), so every connection either terminates at the proxy for inspection or is denied - there is no uninspected plaintext egress path. This pairs with the fail-closed CA-trust contract (see Fail-Closed CA Trust).

Layer 2: Host Controls

Agent hosts should have the same security controls as any managed endpoint in the organization.

ControlDescription
EDRCrowdStrike, SentinelOne, Wazuh, etc. Detects malicious behavior, provides response capabilities.
Audit loggingauditd capturing syscalls, process execution, file access, network connections. Shipped to SIEM.
File integrity monitoringDetect unexpected changes to critical files (including the agent binary itself).
Mandatory access controlsSELinux or AppArmor profiles to limit what the agent process can do, even with root access.
Immutable infrastructureAgents can run on read-only root filesystems with writable scratch space, making persistent compromise harder.

On Kubernetes, the agent host is a per-agent virtual machine: every agent runs inside its own kata-containers VM (the kata-clh runtime class), which is how this backend clears the isolation floor set out in the trust boundary. That VM boundary - hardware-virtualized isolation, not just cgroups and namespaces - is the real containment: whatever the agent’s main container does, a container escape is bounded to that one agent’s own VM, never the shared node kernel.

Kata is a mandatory platform requirement, not an opt-in: a cluster without the kata-clh RuntimeClass cannot provision agent pods at all. There is one isolation model for every agent, with no per-broker or per-agent variant to choose.

Because the VM is the boundary, the context inside the pod is deliberately loose. Every agent pod runs with:

  • the main container on the image’s own default UID (including root) - no runAsNonRoot, no forced UID, so stock images run unmodified
  • the image’s default capability set (nothing artificially dropped) and allowPrivilegeEscalation: true, because the managed-workstation model needs sudo
  • an Unconfined seccomp profile
  • Pod Security Admission set to Privileged on the agent namespace, since the above exceeds Baseline

Everything in the pod - main container, dvxir sidecar, init containers - is a single untrusted trust domain. The platform does not enforce a boundary inside it; it enforces one around it.

Two pod-level controls survive, because neither depends on how the container itself is configured:

  • automountServiceAccountToken: false - no container in an agent pod can reach the Kubernetes API
  • fsGroup: 1000 - keeps the agent-home volume group-writable whatever UID the image runs as

The dvxir sidecar runs as root and drops all capabilities, then adds nine back: five so s6-overlay can initialise (CAP_SETGID, CAP_SETUID, CAP_CHOWN, CAP_FOWNER, CAP_DAC_OVERRIDE) and four for the SSH path (CAP_SYS_ADMIN, CAP_SYS_PTRACE, CAP_SYS_CHROOT, CAP_AUDIT_WRITE), so it can nsenter an operator’s SSH session into the main container’s mount namespace, giving a faithful shell in the agent’s own environment. Those capabilities widen the container-escape surface toward the node, which is exactly what the kata VM boundary contains.

Loosening the in-pod context is a deliberate trade, and it is not free. Privileged PSA also gives up Baseline’s admission-level blocks on hostPath, hostNetwork, and hostPID, and kata does not subsume the hostPath one: a host mount is bridged into the guest over virtiofs, so it crosses the VM boundary by design. See DF-10 in the threat model for the full accounting and the compensating admission policy that replaces the lost backstop.

Layer 3: Platform Controls

Controls provided natively by Daevix at the platform level.

ControlDescription
Identity revocationInstantly revoke all agent credentials, rendering it unable to authenticate to any service.
Credential scopingExternal identities scoped to minimum required permissions. An agent gets the IAM role it needs, not admin.
Audit trailComplete record of agent creation, bootstrap, credential issuance, rotation, and revocation.
Token hashingAll tokens (bootstrap, refresh) stored as SHA-256 digests. The database never sees raw tokens.
Secrets encryptionAgent secrets encrypted at rest with envelope encryption (an AES-256-GCM data key wrapped by the org’s KMS, or the platform key when the org has none). Ciphertext is AAD-bound to its (org, agent, name) identity, so a row can only be decrypted in the exact context it was written - cross-org, cross-agent, or cross-name transplant fails closed. Decrypted only during bootstrap/renewal.
CIDR-bound JWTsOptional IP binding that prevents token use from outside the agent’s network. See IP Binding.
Short-lived agent identityAgents authenticate with enclave-issued platform JWTs (ES256, 5-minute lifetime); agents reaching the LLM proxy through the netproxy are identified by service-signed headers. See Agent identity.
Refresh token familiesFamily-based rotation with replay detection. Reusing an old refresh token revokes the entire family.
JTI blacklistingOptional Redis-backed instant JWT revocation. Without Redis, revocation takes up to 5 minutes (token expiry).
LLM content inspectionContent security pipeline inspects all LLM traffic for PII, API key leaks, secret exposure, and custom patterns. Policies cascade Platform > Org > Agent with immutable floors. See LLM Content Policy.
Model restrictionAllowlist or blocklist which LLM models agents can use. Enforced at the proxy before the request reaches the upstream provider.
Execution-time policy engineThree-layer architecture (inspectors → policy engine → enforcers) evaluates tool calls from LLM responses against operator-defined policies. Supports allow, deny, alert, and require_approval actions. Both LLM proxy and network proxy share the same inspection library and policy engine.
Approval workflowTool calls matching require_approval policies enter an escalation-tier workflow. Tiers progress from LLM Judge auto-resolution to human approval by an operator (dvx policy approvals). Per-agent and org-wide limits prevent approval queue flooding. Approvals are scoped via canonicalized tool call hashing.
Fail-closed policy cacheWhen the execution-time policy cache is cold or stale and the database is unreachable, all actions are denied by default. Operators can configure fail-open as an org-wide setting (requires org:manage permission and compensating controls acknowledgment). Every fail-open event is logged at WARN level.
Kill switchThe enclave can terminate agent compute immediately, without waiting for graceful shutdown (dvx agent isolate).

Layer 4: Agent-Level Controls (dvxir Sidecar / Runtime)

When the dvxir sidecar runs (the default, Mode A), additional controls are available even though the main container is an arbitrary image. These are explicitly best-effort - they run inside the agent’s trust boundary (the sidecar shares that boundary, see The agent runtime is inside that boundary) and could be bypassed by a modified agent. In the no-sidecar power-user mode (Mode B) they apply only if the self-managed image implements them.

ControlDescription
Tool call inspectionAll tool calls logged with arguments and results. Operators can set alert policies.
LLM API proxyRoute LLM calls through the platform proxy for logging and spend control enforcement.
Human-in-the-loop gatesCertain tool calls can require operator approval before execution.
Behavioral policiesRules like “never commit directly to main” enforced at the tool level.

Token Security

Platform JWTs

  • Algorithm: ES256 (ECDSA P-256) - algorithm confusion attacks are rejected
  • Lifetime: 5 minutes
  • Subject: agent:<id>
  • Issued by the enclave, not the control plane
  • Optionally CIDR-bound to the agent’s network

Agent identity

An agent’s identity is its short-lived platform JWT, issued by the enclave at bootstrap and rotated continuously (ES256, 5-minute lifetime). There are two paths by which an agent’s identity reaches the LLM proxy:

  1. Netproxy-forwarded identity. When agent traffic flows through the network proxy (the default for managed agents), the netproxy attaches a service-signed X-Dvx-Service-Authorization header alongside X-Dvx-* identity headers. The LLM proxy verifies the service signature cryptographically and trusts the identity it carries. This is the path the default Claude Code agent uses, and it’s how an agent can forward its own upstream LLM credentials while the platform still tracks who it is.
  2. Platform JWT (direct). Agents that talk to the LLM proxy directly present their enclave-issued platform JWT via Authorization: Bearer … or the Anthropic-style x-api-key header.

A note on mTLS. Earlier versions identified agents to the LLM proxy with short-lived mTLS client certificates. That path was removed - long-lived agent connections don’t re-read a rotating client certificate, so 5-minute certs broke mid-session, and carrying a third auth path added complexity for no benefit. Service-to-service mTLS between platform components (enclave, control plane, relay) is unaffected.

Bootstrap Tokens

  • Random 32 bytes
  • SHA-256 hashed before storage
  • 1-hour expiry
  • Single-use - burned on first exchange
  • Plaintext returned once on creation, never stored

Refresh Tokens

  • 24-hour lifetime
  • Single-use with family-based rotation
  • SHA-256 hashed before storage
  • Replay detection: reusing an old token from the same family revokes the entire family
  • Used to obtain new platform JWT + new refresh token

Platform Security Guarantees

These properties are built into the codebase. Operators benefit from them without additional configuration.

JWT Algorithm Restriction

All JWT validators enforce ES256 only. Algorithm confusion attacks (e.g., accepting none or HS256 with a public key as the signing secret) are rejected at the library level. Both operator JWTs and platform JWTs go through the same strict validation: signature verification, issuer, audience, and expiration checks.

Platform JWT validation in HTTP contexts additionally enforces CIDR binding when the client_cidr claim is present (see IP Binding).

Tenant Isolation

Every API query is scoped by organization_id extracted from the caller’s JWT. The platform never trusts organization identity from request bodies - it always uses the cryptographically verified value from the token. There is no API path that can return data across organizations.

This holds for IAM administration as well. The control plane exposes an operator-facing IAM-management surface (/api/v1/iam/*) that can mutate IAM data - accounts, organizations, RBAC role assignments, and OAuth2 clients - making it a second writer to the IAM tables (which dvx iam otherwise owns). Every such operation is gated by RBAC (members:*, org:manage, clients:manage) and is strictly org-scoped from the operator JWT claims - no route accepts a client-supplied organization id, including for platform-admin. A leaked operator token can therefore only ever touch its own tenant’s IAM data. The control plane and IAM already share one database and trust domain, so this write boundary stays inside an existing trust domain rather than crossing a new one.

Fail-Closed CA Trust

The platform inspects agent egress by terminating TLS at the network proxy. An arbitrary BYO image will not trust the platform’s interception CA by default, so trust is delivered and the contract is inspect-or-deny - never plaintext passthrough:

  • A merged CA bundle is delivered into each agent namespace and mounted over the image’s distro trust-store paths (and a well-known /etc/dvx/ca.crt). The bundle carries public roots plus the internal interception CA - public roots come from trust-manager’s built-in default-CA source (useDefaultCAs), so legitimate public TLS still validates and currency tracks the trust-manager release cadence. The image’s own (untrusted, possibly absent) store is never harvested.
  • If the agent’s client honors the store, traffic is transparently inspected. The highest-value path (LLM traffic) can instead point the agent at the proxy via a base-URL env var, where the proxy presents a publicly-trusted certificate - full inspection with no private-CA dependency.
  • If a client ignores the store or pins, its handshake to the proxy simply fails and the connection is blocked - it is never passed through in the clear. Combined with the all-TCP egress redirect (Layer 1), there is no route around inspection. Runtimes that bypass the system store (Node, Python requests, Java) are pointed at /etc/dvx/ca.crt via a per-agent env var.

SQL Injection Prevention

All database queries are generated by sqlc as parameterized statements with type-safe Go bindings. No SQL is constructed by string concatenation anywhere in the codebase.

Error Masking

All API error responses use RFC 9457 Problem Details (application/problem+json). Internal details - stack traces, SQL errors, file paths - are never exposed to clients. Errors are logged server-side with full context for debugging, while clients receive sanitized messages.

Cryptographic Randomness

All security-sensitive random values (tokens, nonces, CSRF tokens) are generated using cryptographically secure sources (crypto/rand in Go, crypto.getRandomValues() in the browser).

Logging Hygiene

The platform uses structured logging and enforces a strict boundary: security-relevant events (authentication outcomes, status transitions, token lifecycle) are logged, while sensitive values (raw tokens, plaintext secrets, database credentials, private keys) are never written to logs.

Audit Trail Integrity

Daevix uses a tombstone pattern (soft deletes) for data deletion - no security-relevant data is hard-deleted. Tables have a deleted_at column, and all read queries filter on active records. This preserves a complete audit trail of agent creation, credential issuance, and lifecycle transitions.