
# 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](/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

| Actor | Starting Access | Motivation |
|-------|----------------|------------|
| **Compromised Agent** | Container 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 Operator** | Valid IAM JWT scoped to an organization. Access to the operator API. | Access another organization's data, escalate within the org, abuse destructive operations. |
| **External Attacker** | No credentials. Network access to public-facing endpoints. | Steal tokens, exploit unauthenticated endpoints, man-in-the-middle, denial of service. |
| **Compromised Enclave** | Enclave 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 Attacker** | Read 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           │               │
│       └─────────────────────────────────┘               │
└─────────────────────────────────────────────────────────┘
```

| Boundary | Between | Authentication |
|----------|---------|----------------|
| **B1** | Agent ↔ Enclave | Bootstrap token (single-use), then platform JWT (ES256, 5min) |
| **B2** | Agent ↔ LLM Proxy | Platform JWT (ES256, 5min) or netproxy-forwarded service-signed identity headers |
| **B3** | Enclave ↔ Control Plane | Enclave API key (SHA-256 hashed) |
| **B4** | Enclave ↔ Compute Runtime | Unix socket (Podman) or service account (Kubernetes) |
| **B5** | Operator ↔ Control Plane | IAM JWT (ES256, JWKS-validated) |
| **B8** | LLM Proxy ↔ Upstream LLM | Resolved API key (injected from encrypted storage) or agent's own credentials (passthrough mode) |
| **B9** | All services ↔ Database | Connection string credentials |
| **B10** | Agent cert ↔ Platform CA | X.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

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker could stand up a fake enclave and respond to discovery requests, directing agents to malicious endpoints. |
| **Tampering** | A MITM could alter the endpoint list in the response, redirecting agents to attacker-controlled services. |
| **Repudiation** | Discovery requests are unauthenticated and not logged - there is no record of which agents queried the enclave. |
| **Information Disclosure** | The response reveals enclave version and supported endpoints, which aids reconnaissance. |
| **Denial of Service** | The endpoint is unauthenticated and could be flooded. |
| **Elevation of Privilege** | Not applicable - no authentication or authorization occurs. |

### DF-2: Agent Bootstrap

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker who intercepts or guesses a bootstrap token can impersonate the agent and claim its identity. |
| **Tampering** | A MITM could alter the response, injecting a malicious JWT or modified secrets into the agent's identity bundle. |
| **Repudiation** | The bootstrap event is logged, but the agent has no way to prove which identity bundle it received. |
| **Information Disclosure** | The 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 Service** | An 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 Privilege** | A 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

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker with a stolen refresh token can obtain a valid JWT and impersonate the agent. |
| **Tampering** | A MITM could modify the renewed JWT or secrets in transit if TLS is compromised. |
| **Repudiation** | Renewal events are logged with agent_id. Replay detection creates a forensic trail when tokens are reused. |
| **Information Disclosure** | Same as bootstrap - decrypted secrets are delivered in the response body. |
| **Denial of Service** | An 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 Privilege** | Refresh tokens are scoped to a single agent - they cannot be used to renew tokens for a different agent. |

### DF-4: Agent Secret Access

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker with a stolen JWT can retrieve secrets for that agent. |
| **Tampering** | Not applicable - endpoint is read-only. |
| **Repudiation** | Secret access is not individually logged (only renewal/bootstrap delivery is audited). |
| **Information Disclosure** | Secrets 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 Service** | Repeated requests could cause load on decryption operations. |
| **Elevation of Privilege** | An 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

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker with a stolen JWT can read and write agent-scoped config. |
| **Tampering** | A compromised agent can write arbitrary values to its own config, potentially influencing platform behavior (e.g., downstream URL overrides). |
| **Repudiation** | Config writes are not individually audited. |
| **Information Disclosure** | Config values (including org-level overrides) are returned in plaintext. May reveal infrastructure details (URLs, feature toggles). |
| **Denial of Service** | An agent could write excessive config entries. |
| **Elevation of Privilege** | Agents 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

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker with a stolen JWT can make LLM calls as the agent, consuming API credits and potentially exfiltrating data via crafted prompts. |
| **Tampering** | A compromised agent can send any content to the LLM - there is no request content validation at the proxy layer. |
| **Repudiation** | Full request and response bodies are written to the audit log, providing non-repudiation for LLM interactions. |
| **Information Disclosure** | The 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 Service** | No rate limiting on the proxy - a compromised agent can make unlimited LLM calls, exhausting API credits. Request body limit is 32 MB. |
| **Elevation of Privilege** | The 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

| Category | Threat |
|----------|--------|
| **Spoofing** | Not applicable - audit writes are internal (proxy to database). |
| **Tampering** | If 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. |
| **Repudiation** | The audit log exists to prevent repudiation. If it can be tampered with, repudiation becomes possible. |
| **Information Disclosure** | The 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 Service** | The 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 Privilege** | Not applicable - audit writes use the proxy's database credentials. |

### DF-8: Enclave SSE Event Stream

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker with a stolen enclave API key can connect to the SSE stream and receive events intended for that enclave. |
| **Tampering** | Events 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. |
| **Repudiation** | Event consumption is tracked via cursor (`last_event_id`) but individual event reads are not logged. |
| **Information Disclosure** | Events contain agent_id, agent_name, and organization_id. A stolen API key reveals all agents assigned to that enclave. |
| **Denial of Service** | A 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 Privilege** | Events are scoped to the authenticated broker_id - an enclave cannot receive events for other enclaves. |

### DF-9: Agent Provisioning (Podman)

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker with access to the Podman Unix socket can create containers that appear to be legitimate agents. |
| **Tampering** | The 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. |
| **Repudiation** | Container creation is not audited at the platform level (Podman's own logs may capture it). |
| **Information Disclosure** | Bootstrap 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 Service** | No resource limits are applied to containers by default. A single agent could exhaust host CPU or memory. |
| **Elevation of Privilege** | Containers 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)

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker with access to the Kubernetes API and the enclave's service account could create pods that impersonate agents. |
| **Tampering** | The bootstrap token is passed as a plaintext environment variable in the pod spec. Anyone with `get pods` permission in the namespace can read it. |
| **Repudiation** | Kubernetes audit logging (if enabled) records pod creation. |
| **Information Disclosure** | Bootstrap 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 Service** | No 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 Privilege** | Pods 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)

| Category | Threat |
|----------|--------|
| **Spoofing** | An attacker with a stolen or forged IAM JWT can perform operations as that operator. |
| **Tampering** | An authenticated operator can modify any agent in their organization - there is no role-based restriction on who can delete, suspend, or revoke. |
| **Repudiation** | Operations are not individually audit-logged at the application level (only database-level timestamps on mutations). |
| **Information Disclosure** | Agent list reveals all agents, their status, enclave assignments, and last-heard-from timestamps for the organization. |
| **Denial of Service** | No rate limiting - an authenticated operator could mass-create or mass-delete agents. List endpoints return all records without pagination. |
| **Elevation of Privilege** | JWT 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)

| Category | Threat |
|----------|--------|
| **Spoofing** | Same as DF-11 - stolen IAM JWT. |
| **Tampering** | An 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. |
| **Repudiation** | Enclave creation and token rotation return plaintext keys - no audit record of who received the key. |
| **Information Disclosure** | Enclave 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 Service** | Deleting an enclave orphans all its agents. Rotating a key immediately disconnects the enclave from SSE. |
| **Elevation of Privilege** | Same RBAC gap as DF-11 - any org member can manage enclaves. |

### DF-13: Operator API (Secrets Management)

| Category | Threat |
|----------|--------|
| **Spoofing** | Same as DF-11 - stolen IAM JWT. |
| **Tampering** | An authenticated operator can overwrite any agent's secrets. There is no confirmation step or version control on secret values. |
| **Repudiation** | Secret writes update timestamps but do not log who wrote the value or what the previous value was. |
| **Information Disclosure** | Secret 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 Service** | No limit on the number or size of secrets per agent. |
| **Elevation of Privilege** | Same RBAC gap - any org member can create secrets for any agent. |

### DF-16: Event Publishing and Purging

| Category | Threat |
|----------|--------|
| **Spoofing** | Events are created by the control plane's service layer - no external input. A database attacker could insert fake events. |
| **Tampering** | A database attacker could modify event payloads (agent_id, org_id) or manipulate the enclave's cursor to cause events to be replayed or skipped. |
| **Repudiation** | Events are append-only by convention. No integrity protection (signing, HMAC) on event payloads. |
| **Information Disclosure** | Events contain agent_id, agent_name, and organization_id - visible to anyone with database read access. |
| **Denial of Service** | Events accumulate if enclaves don't process them. The purge job only removes consumed events. A disconnected enclave's events grow without bound. |
| **Elevation of Privilege** | Fake 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

| Category | Threat |
|----------|--------|
| **Spoofing** | An 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. |
| **Tampering** | The 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. |
| **Repudiation** | Integration creation is recorded in the database with timestamps but no audit trail of who created it. |
| **Information Disclosure** | The 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 Service** | Deleting the integration deletes the encrypted secret - the agent loses access on next renewal. |
| **Elevation of Privilege** | The 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)`.

| Category | Threat |
|----------|--------|
| **Spoofing** | **Tool 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. |
| **Tampering** | **Policy 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. |
| **Repudiation** | All 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 Disclosure** | Denial 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 Service** | **Approval 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 Privilege** | Policies 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. |
