Daevix Docs

Threat Model

Open as Markdown

Threat actors, trust boundaries, data flows, and STRIDE analysis for the Daevix platform.

Threat Model

This document identifies who would attack the platform, where trust boundaries exist, what data crosses those boundaries, and what threats apply to each flow. For the platform’s security posture and controls, see Security Model.

Scope

In scope: Control plane, enclave, LLM proxy, agent containers, database, Redis, SSE event stream, and all data flows between them.

Out of scope: External IAM provider internals, upstream LLM provider (Anthropic API) internals, and the operator’s broader cloud/network infrastructure.


Threat Actors

ActorStarting AccessMotivation
Compromised AgentContainer with platform JWT, refresh token, delivered secrets. Network access to enclave and proxy.Escalate privileges, access other agents’ data, exfiltrate secrets, pivot to internal services.
Malicious OperatorValid IAM JWT scoped to an organization. Access to the operator API.Access another organization’s data, escalate within the org, abuse destructive operations.
External AttackerNo credentials. Network access to public-facing endpoints.Steal tokens, exploit unauthenticated endpoints, man-in-the-middle, denial of service.
Compromised EnclaveEnclave API key, ECDSA signing key, secrets decryption key, database credentials. All agent secrets pass through in cleartext during delivery.Impersonate any agent, forge JWTs, intercept secrets, manipulate provisioning.
Database AttackerRead or write access to PostgreSQL.Extract secrets, forge tokens, manipulate agent state, access cross-tenant data.

Trust Boundaries

┌─────────────────────────────────────────────────────────┐
│                    Operator Network                      │
│                                                          │
│  ┌──────────┐     ┌────────────────┐     ┌───────────┐  │
│  │ dvx CLI  │────▶│ Control Plane  │◀────│    IAM    │  │
│  │(operator)│     │                │     │           │  │
│  └──────────┘     └───────┬────────┘     └───────────┘  │
│                           │ SSE + API                    │
│       ┌───────────────────┼──────────────┐               │
│       │                   ▼              │               │
│       │           ┌──────────────┐       │               │
│       │           │    Enclave   │       │               │
│       │           └──┬───────┬──┘       │               │
│       │              │       │          │               │
│       │      ┌───────┘       └────┐     │               │
│       │      ▼                    ▼     │               │
│       │ ┌─────────┐      ┌───────────┐  │               │
│       │ │  Agent  │      │ LLM Proxy │──┼──▶ Anthropic  │
│       │ │(untrust)│      │           │  │               │
│       │ └─────────┘      └───────────┘  │               │
│       │                                 │               │
│       │         Agent Network           │               │
│       └─────────────────────────────────┘               │
└─────────────────────────────────────────────────────────┘
BoundaryBetweenAuthentication
B1Agent ↔ EnclaveBootstrap token (single-use), then platform JWT (ES256, 5min)
B2Agent ↔ LLM ProxyPlatform JWT (ES256, 5min) or netproxy-forwarded service-signed identity headers
B3Enclave ↔ Control PlaneEnclave API key (SHA-256 hashed)
B4Enclave ↔ Compute RuntimeUnix socket (Podman) or service account (Kubernetes)
B5Operator ↔ Control PlaneIAM JWT (ES256, JWKS-validated)
B8LLM Proxy ↔ Upstream LLMResolved API key (injected from encrypted storage) or agent’s own credentials (passthrough mode)
B9All services ↔ DatabaseConnection string credentials
B10Agent cert ↔ Platform CAX.509 client certificate (ECDSA P-256, 5min, issued by enclave CA)

Note: Boundaries B6 and B7 previously covered the web Operator Console (SPA ↔ Control Plane and Console ↔ IAM); the web console has been removed. Operators now use the dvx CLI, whose operator authentication is covered by B5 (and the OAuth2 device flow against IAM). The remaining boundary numbers are unchanged.

Control plane ↔ IAM tables

IAM owns the iam_* tables (accounts, organizations, RBAC roles, OAuth2 clients), and other services normally only validate IAM-issued JWTs via JWKS - they do not mutate IAM data. The operator IAM-management surface (/api/v1/iam/*, boundary B5) is the documented exception: it lets the control plane write to iam_* tables - creating accounts, mutating organizations, assigning/revoking roles, and managing OAuth2 clients - making the control plane a second writer alongside dvx iam. These writes are gated by RBAC (members:*, org:manage, clients:manage) and are strictly org-scoped from the operator JWT claims - no route accepts a client-supplied organization id, including for platform-admin - so the blast radius of a leaked operator token stays confined to its own tenant. The control plane and IAM share one database and trust domain, so this is a write path within an existing boundary, not a new one.


Data Flows

DF-1: Agent Discovery

  • Boundary: B1 (Agent → Enclave)
  • Trigger: Agent startup
  • Auth: None (unauthenticated)
  • Data: Agent sends GET /v1/agent/discover. Enclave returns capabilities: service name, version, available endpoints.

DF-2: Agent Bootstrap

  • Boundary: B1 (Agent → Enclave)
  • Trigger: Agent first boot with bootstrap token
  • Auth: Single-use bootstrap token in request body
  • Data: Agent sends { token }. Enclave returns identity bundle: { agent_id, organization_id, agent_name, jwt, refresh_token, secrets (decrypted), config, config_hash }.

DF-3: Agent JWT Renewal

  • Boundary: B1 (Agent → Enclave)
  • Trigger: Every 4-5 minutes via agent refresh loop
  • Auth: Refresh token in request body (single-use, family-rotated)
  • Data: Agent sends { refresh_token }. Enclave returns { jwt (new), refresh_token (new, rotated), secrets, config, config_hash }.

DF-4: Agent Secret Access

  • Boundary: B1 (Agent → Enclave)
  • Trigger: Agent requests a credential on demand
  • Auth: Platform JWT (Bearer token)
  • Data: Agent sends GET /v1/agent/secrets/{name}. Enclave returns { name, value (decrypted) }. Also: GET /v1/agent/secrets returns name list (no values).

DF-5: Agent Config Access

  • Boundary: B1 (Agent → Enclave)
  • Trigger: Agent startup, drift detection
  • Auth: Platform JWT (Bearer token)
  • Data: Agent sends GET /v1/agent/config. Enclave returns merged config (platform → org → agent). Agent can also write agent-scoped config via PUT /v1/agent/config/{key}.

DF-6: Agent LLM Request

  • Boundary: B2 (Agent → LLM Proxy), then B8 (Proxy → Upstream)
  • Trigger: Agent makes an inference call
  • Auth: Platform JWT (Bearer token) or x-api-key header
  • Data: Agent sends Anthropic Messages API request. Proxy validates JWT, resolves the agent’s LLM API key from the database, injects it, forwards to upstream. Buffers the SSE response, runs audit interceptors, replays to agent.

DF-7: LLM Audit Logging

  • Boundary: B9 (Proxy → Database)
  • Trigger: Every LLM request/response pair
  • Auth: Database connection credentials
  • Data: Proxy writes to llm_audit_log: organization_id, agent_id, model, input/output tokens, full request body (JSONB), full response body (JSONB).

DF-8: Enclave SSE Event Stream

  • Boundary: B3 (Enclave → Control Plane)
  • Trigger: Enclave startup (long-lived connection)
  • Auth: Enclave API key (Bearer token)
  • Data: Enclave connects to GET /v1/broker/events with optional Last-Event-ID cursor. Control plane streams SSE events (e.g., agent.created with agent_id, name, org_id). Heartbeat every 30 seconds.

DF-9: Agent Provisioning (Podman)

  • Boundary: B4 (Enclave → Podman)
  • Trigger: Enclave receives agent.created SSE event
  • Auth: Unix socket (implicit local trust)
  • Data: Enclave generates bootstrap token, calls Podman REST API to create container with env vars: DVX_BROKER_URL, DVX_BOOTSTRAP_TOKEN (plaintext), DVX_AGENT_ID, DVX_AGENT_NAME, DVX_TLS_CA. CA cert bind-mounted read-only.

DF-10: Agent Provisioning (Kubernetes)

  • Boundary: B4 (Enclave → Kubernetes API)
  • Trigger: Enclave receives agent.created SSE event
  • Auth: In-cluster service account token
  • Data: Enclave creates namespace (labeled), applies NetworkPolicy, waits for CA ConfigMap (trust-manager), creates pod with env vars (same as Podman) and security context (non-root, UID 1000).

DF-11: Operator API (Agent CRUD)

  • Boundary: B5 (Operator → Control Plane)
  • Trigger: Operator action via the dvx CLI
  • Auth: IAM JWT (Bearer token, validated against JWKS)
  • Data: Create, list, get, update, delete agents. Change agent status. Revoke tokens. Trigger re-provisioning. All scoped by organization_id from JWT.

DF-12: Operator API (Enclave Management)

  • Boundary: B5 (Operator → Control Plane)
  • Trigger: Operator action
  • Auth: IAM JWT
  • Data: Register enclave (returns API key once), list, get, delete, rotate API key. All scoped by organization_id.

DF-13: Operator API (Secrets Management)

  • Boundary: B5 (Operator → Control Plane)
  • Trigger: Operator action
  • Auth: IAM JWT
  • Data: Create/update secret (plaintext in, encrypted at rest with AES-256-GCM). List secrets (metadata only - name, agent, timestamps - no encrypted values returned).

DF-16: Event Publishing and Purging

  • Boundary: Internal (Control Plane)
  • Trigger: Agent creation with broker_id (publish), hourly timer (purge)
  • Auth: None (in-process)
  • Data: Events written to broker_events table with auto-incrementing cursor. In-process event bus notifies SSE handler. Purge deletes events already consumed by their enclave (cursor-based) or owned by deleted enclaves.

DF-17: Telegram Integration

  • Boundary: B5 (Operator → Control Plane), then external (Control Plane → Telegram API)
  • Trigger: Operator links a Telegram bot to an agent
  • Auth: IAM JWT (operator), then Telegram bot token (validation call)
  • Data: Operator sends bot_token. Control plane validates via Telegram getMe API. Token encrypted and stored as agent secret (telegram:bot_token). Integration record created. Agent receives decrypted token during bootstrap/renewal.

STRIDE Analysis

DF-1: Agent Discovery

CategoryThreat
SpoofingAn attacker could stand up a fake enclave and respond to discovery requests, directing agents to malicious endpoints.
TamperingA MITM could alter the endpoint list in the response, redirecting agents to attacker-controlled services.
RepudiationDiscovery requests are unauthenticated and not logged - there is no record of which agents queried the enclave.
Information DisclosureThe response reveals enclave version and supported endpoints, which aids reconnaissance.
Denial of ServiceThe endpoint is unauthenticated and could be flooded.
Elevation of PrivilegeNot applicable - no authentication or authorization occurs.

DF-2: Agent Bootstrap

CategoryThreat
SpoofingAn attacker who intercepts or guesses a bootstrap token can impersonate the agent and claim its identity.
TamperingA MITM could alter the response, injecting a malicious JWT or modified secrets into the agent’s identity bundle.
RepudiationThe bootstrap event is logged, but the agent has no way to prove which identity bundle it received.
Information DisclosureThe response contains decrypted secrets in plaintext over the wire. If TLS is compromised or --insecure-skip-tls is used, all secrets are exposed.
Denial of ServiceAn attacker could consume the bootstrap token before the legitimate agent, permanently blocking it from bootstrapping. A flood of invalid tokens causes repeated database lookups.
Elevation of PrivilegeA bootstrap token for one agent cannot be used to claim a different agent’s identity - tokens are bound to a specific agent_id.

DF-3: Agent JWT Renewal

CategoryThreat
SpoofingAn attacker with a stolen refresh token can obtain a valid JWT and impersonate the agent.
TamperingA MITM could modify the renewed JWT or secrets in transit if TLS is compromised.
RepudiationRenewal events are logged with agent_id. Replay detection creates a forensic trail when tokens are reused.
Information DisclosureSame as bootstrap - decrypted secrets are delivered in the response body.
Denial of ServiceAn attacker who steals a refresh token can use it first, which triggers replay detection and revokes the entire token family - denying service to the legitimate agent.
Elevation of PrivilegeRefresh tokens are scoped to a single agent - they cannot be used to renew tokens for a different agent.

DF-4: Agent Secret Access

CategoryThreat
SpoofingAn attacker with a stolen JWT can retrieve secrets for that agent.
TamperingNot applicable - endpoint is read-only.
RepudiationSecret access is not individually logged (only renewal/bootstrap delivery is audited).
Information DisclosureSecrets are returned in plaintext. A stolen JWT grants access to all of the agent’s secrets for the JWT’s lifetime. The secret name list endpoint reveals what integrations an agent has.
Denial of ServiceRepeated requests could cause load on decryption operations.
Elevation of PrivilegeAn agent can only access its own secrets - the enclave resolves agent_id from the JWT claims, not from the request.

DF-5: Agent Config Access

CategoryThreat
SpoofingAn attacker with a stolen JWT can read and write agent-scoped config.
TamperingA compromised agent can write arbitrary values to its own config, potentially influencing platform behavior (e.g., downstream URL overrides).
RepudiationConfig writes are not individually audited.
Information DisclosureConfig values (including org-level overrides) are returned in plaintext. May reveal infrastructure details (URLs, feature toggles).
Denial of ServiceAn agent could write excessive config entries.
Elevation of PrivilegeAgents can only write agent-scoped config - they cannot modify org-level or platform-level config. However, if config merging gives agent-level config precedence, a compromised agent could override org-level settings for itself.

DF-6: Agent LLM Request

CategoryThreat
SpoofingAn attacker with a stolen JWT can make LLM calls as the agent, consuming API credits and potentially exfiltrating data via crafted prompts.
TamperingA compromised agent can send any content to the LLM - there is no request content validation at the proxy layer.
RepudiationFull request and response bodies are written to the audit log, providing non-repudiation for LLM interactions.
Information DisclosureThe proxy resolves and injects the LLM API key - a compromised proxy or database attacker could intercept it. The agent never sees the raw API key (it’s injected after JWT validation).
Denial of ServiceNo rate limiting on the proxy - a compromised agent can make unlimited LLM calls, exhausting API credits. Request body limit is 32 MB.
Elevation of PrivilegeThe proxy falls back from agent-level to org-level secrets for API key resolution - all agents in an org share the org-level LLM credential.

DF-7: LLM Audit Logging

CategoryThreat
SpoofingNot applicable - audit writes are internal (proxy to database).
TamperingIf the database is compromised, audit records could be modified or deleted. The table is append-only by convention but not enforced at the database level.
RepudiationThe audit log exists to prevent repudiation. If it can be tampered with, repudiation becomes possible.
Information DisclosureThe audit log stores complete request and response bodies as JSONB. This may contain sensitive data from LLM interactions (PII, credentials in prompts, proprietary content). Anyone with database read access can see all logged conversations.
Denial of ServiceThe audit log grows continuously with no built-in retention. Over time it could consume all available storage. Logging is fire-and-forget - audit failure does not block the request.
Elevation of PrivilegeNot applicable - audit writes use the proxy’s database credentials.

DF-8: Enclave SSE Event Stream

CategoryThreat
SpoofingAn attacker with a stolen enclave API key can connect to the SSE stream and receive events intended for that enclave.
TamperingEvents are written by the control plane and delivered read-only via SSE - a network attacker cannot inject events into the stream. However, a database attacker could insert fake events directly into the broker_events table.
RepudiationEvent consumption is tracked via cursor (last_event_id) but individual event reads are not logged.
Information DisclosureEvents contain agent_id, agent_name, and organization_id. A stolen API key reveals all agents assigned to that enclave.
Denial of ServiceA rogue client holding the API key could maintain a persistent connection, potentially blocking the legitimate enclave from connecting (depending on server-side connection limits). Events accumulate if the enclave never processes them.
Elevation of PrivilegeEvents are scoped to the authenticated broker_id - an enclave cannot receive events for other enclaves.

DF-9: Agent Provisioning (Podman)

CategoryThreat
SpoofingAn attacker with access to the Podman Unix socket can create containers that appear to be legitimate agents.
TamperingThe bootstrap token is passed as a plaintext environment variable - any process that can inspect container metadata or /proc on the host can read it. The container image could be tampered with if the registry is compromised.
RepudiationContainer creation is not audited at the platform level (Podman’s own logs may capture it).
Information DisclosureBootstrap tokens and TLS CA paths are visible in container environment variables. No network isolation - containers on the same host can communicate with each other and reach host services.
Denial of ServiceNo resource limits are applied to containers by default. A single agent could exhaust host CPU or memory.
Elevation of PrivilegeContainers share the host network namespace (pasta networking). A compromised agent can reach other agents, the database, Redis, and any host-local service.

DF-10: Agent Provisioning (Kubernetes)

CategoryThreat
SpoofingAn attacker with access to the Kubernetes API and the enclave’s service account could create pods that impersonate agents.
TamperingThe bootstrap token is passed as a plaintext environment variable in the pod spec. Anyone with get pods permission in the namespace can read it.
RepudiationKubernetes audit logging (if enabled) records pod creation.
Information DisclosureBootstrap tokens in pod env vars. NetworkPolicy blocks private ranges but not link-local addresses (169.254.0.0/16) - cloud metadata services may be reachable.
Denial of ServiceNo resource limits are set on agent pods by default - a single agent could exhaust node resources. An attacker who can trigger agent creation could exhaust namespace or pod quotas.
Elevation of PrivilegePods run with allowPrivilegeEscalation: true, which permits setuid binaries and sudo. Combined with a kernel vulnerability, this could enable container escape. automountServiceAccountToken: false prevents Kubernetes API access.

DF-11: Operator API (Agent CRUD)

CategoryThreat
SpoofingAn attacker with a stolen or forged IAM JWT can perform operations as that operator.
TamperingAn authenticated operator can modify any agent in their organization - there is no role-based restriction on who can delete, suspend, or revoke.
RepudiationOperations are not individually audit-logged at the application level (only database-level timestamps on mutations).
Information DisclosureAgent list reveals all agents, their status, enclave assignments, and last-heard-from timestamps for the organization.
Denial of ServiceNo rate limiting - an authenticated operator could mass-create or mass-delete agents. List endpoints return all records without pagination.
Elevation of PrivilegeJWT claims include Roles and Permissions fields, but no handler checks them - any authenticated user in the organization has full access to all operations. Queries are scoped by organization_id from the JWT, preventing cross-tenant access.

DF-12: Operator API (Enclave Management)

CategoryThreat
SpoofingSame as DF-11 - stolen IAM JWT.
TamperingAn operator can delete an enclave that has active agents assigned to it, orphaning those agents (no pre-deletion check). Token rotation invalidates the previous key immediately.
RepudiationEnclave creation and token rotation return plaintext keys - no audit record of who received the key.
Information DisclosureEnclave API key is returned in plaintext once. If the response is logged or intercepted, the key is compromised. Enclave list reveals infrastructure topology (names, status, last_seen_at).
Denial of ServiceDeleting an enclave orphans all its agents. Rotating a key immediately disconnects the enclave from SSE.
Elevation of PrivilegeSame RBAC gap as DF-11 - any org member can manage enclaves.

DF-13: Operator API (Secrets Management)

CategoryThreat
SpoofingSame as DF-11 - stolen IAM JWT.
TamperingAn authenticated operator can overwrite any agent’s secrets. There is no confirmation step or version control on secret values.
RepudiationSecret writes update timestamps but do not log who wrote the value or what the previous value was.
Information DisclosureSecret values are accepted in plaintext over the wire (HTTPS), encrypted before storage. The list endpoint returns metadata only (no values). However, the copyable field reveals whether a secret’s plaintext was retained for display.
Denial of ServiceNo limit on the number or size of secrets per agent.
Elevation of PrivilegeSame RBAC gap - any org member can create secrets for any agent.

DF-16: Event Publishing and Purging

CategoryThreat
SpoofingEvents are created by the control plane’s service layer - no external input. A database attacker could insert fake events.
TamperingA database attacker could modify event payloads (agent_id, org_id) or manipulate the enclave’s cursor to cause events to be replayed or skipped.
RepudiationEvents are append-only by convention. No integrity protection (signing, HMAC) on event payloads.
Information DisclosureEvents contain agent_id, agent_name, and organization_id - visible to anyone with database read access.
Denial of ServiceEvents accumulate if enclaves don’t process them. The purge job only removes consumed events. A disconnected enclave’s events grow without bound.
Elevation of PrivilegeFake events could trigger provisioning of containers - but the provisioner validates agent state (must be “created” or “provisioning”) before generating a bootstrap token.

DF-17: Telegram Integration

CategoryThreat
SpoofingAn attacker with a valid Telegram bot token could register it before the legitimate operator, hijacking the bot’s identity. The getMe validation only checks that the token is valid, not that the operator owns the bot.
TamperingThe bot token is encrypted at rest but delivered in plaintext to the agent during bootstrap/renewal. A compromised agent has full access to the Telegram bot.
RepudiationIntegration creation is recorded in the database with timestamps but no audit trail of who created it.
Information DisclosureThe Telegram bot token grants full control of the bot (read messages, send messages, access chat members). This is a high-value credential delivered alongside all other secrets.
Denial of ServiceDeleting the integration deletes the encrypted secret - the agent loses access on next renewal.
Elevation of PrivilegeThe Telegram bot token is scoped to a single bot - it cannot be used to access other bots or the operator’s Telegram account.

DF-18: Execution-Time Policy Evaluation

  • Boundary: B2 (Agent → LLM Proxy)
  • Trigger: Agent’s LLM response contains tool_use blocks
  • Auth: Platform JWT or netproxy-forwarded service-signed identity headers
  • Data: LLM proxy inspects tool calls against execution-time policies (3-layer architecture: inspectors → policy engine → enforcers). Policy decisions are logged to policy_decisions. Actions requiring approval enter the approval workflow; denied actions are returned as tool_result(is_error).
CategoryThreat
SpoofingTool name spoofing: A compromised agent could rename tools to evade LLM-level policies (e.g., rename stripe.create_refund to process_payment). LLM-level policies match on agent-defined names, so this is a real risk. Mitigations: network-level policies as backstop (destination matching doesn’t depend on tool names), tool catalog with “expected tools” alerting, and tool definition change detection between requests. Approval spoofing: Approval resolution requires an authenticated IAM session; the resolved_by field is set from the caller’s verified identity. LLM Judge resolvers authenticate via platform service credentials with resolved_by set to the judge’s config ID.
TamperingPolicy version drift: If a policy is updated while an approval is pending, the wrong version could apply. Mitigation: policy decisions are logged with a snapshot of the policy at evaluation time (policy_version field). TOCTOU between evaluation and execution: The proxy evaluates tool_use blocks from the LLM response, but the agent may modify parameters before actual execution. LLM-layer policies constrain the LLM’s output; network-layer policies are the binding enforcement point. For high-risk operations, operators should define policies at both layers.
RepudiationAll policy decisions (allow, deny, require_approval) are logged to policy_decisions with the full evaluation context. Approval resolutions record resolved_by, resolver_type, and timestamp.
Information DisclosureDenial messages default to a generic “action denied by policy” with no threshold values, policy names, or rule details exposed. Operators can opt into custom denial_message fields when they want to help well-behaved agents self-correct. LLM Judge resolver manipulation: A compromised agent could craft tool inputs or conversation content to manipulate an LLM Judge resolver into auto-approving a dangerous action (prompt injection via request_context). Mitigations: request_context sanitization with structured envelopes and clear delimiters, LLM Judge resolvers use dedicated service accounts, operators can disable LLM Judge tiers for high-risk policies via "human_only": true, and all auto-approvals are logged with resolver_type: "llm_judge" for separate audit review.
Denial of ServiceApproval timeout flooding: An attacker could flood the approval queue to overwhelm human reviewers, causing legitimate requests to time out or exhaust escalation tiers. Mitigations: max pending approval requests per agent (default: 10), org-wide pending approval limit (default: 100), per-agent rate limit on approval request creation (max 50/hour). When any limit is exceeded, require_approval verdicts become deny with reason “approval rate limit exceeded.”
Elevation of PrivilegePolicies are stored in the database and only modifiable by authenticated operators with org:manage permission. Agents cannot read or modify their own policies through the enclave API - the enclave only delivers the action result, never the policy definition.