---
title: 'Runbook: compromised agent'
description: Contain first, investigate second, revoke only when you are sure — because revocation is terminal.
label: Compromised agent
---

**Trigger.** An agent is doing something it should not: volume well above
baseline, destinations nobody configured, repeated refusals, or a key you have
reason to believe is exposed.

## Contain first

:::steps

:::step Isolate the agent

```bash
curl -X POST $CP/v1/security/emergency/preview \
  -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{ "kind": "ISOLATE_AGENT", "target_id": "agt_01JAV…" }'

curl -X POST $CP/v1/security/emergency/execute \
  -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{
    "kind": "ISOLATE_AGENT",
    "target_id": "agt_01JAV…",
    "reason": "Refund volume 8x baseline with no corresponding claim volume",
    "confirm_blast_radius": true
  }'
```

`ISOLATE_AGENT` rather than `PAUSE_AGENT`: isolate cuts **existing sessions**
too, not just new actions. If something is actively using the identity, pause
alone leaves the current session running.

:::danger Do not revoke yet
Revocation is terminal and there is no un-revoke. You cannot yet distinguish a
compromise from a bug in your own agent, and the two have very different
responses. Isolate is reversible and stops the harm just as fast.
:::
:::

:::step If several agents are involved, pause the environment

```bash
curl -X POST $CP/v1/environments/env_01ABC/pause \
  -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{ "reason": "Multiple agents showing anomalous refund volume" }'
```

Over-broad on purpose. Stopping too much for ten minutes is cheaper than
stopping the wrong thing while the right thing continues.

**Do not drop the enforcement mode.** `OBSERVE` stops refusing anything while
leaving everything running — the exact opposite of containment.
:::

:::step Open an incident

```bash
curl -X POST $CP/v1/incidents \
  -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{
    "title": "finance-agent refund volume 8x baseline",
    "severity": "HIGH",
    "description": "Volume rose sharply at 02:14 UTC with no corresponding claim volume. Agent isolated at 02:31.",
    "affected_agent_ids": ["agt_01JAV…"]
  }'
```

`HIGH` means contained, not worried. Severity should reflect what you have done.

An open incident also makes `trust.active_incidents > 0`, so any rule testing it
applies immediately — which is why that rule is worth having before you need it.
See [Incidents](/operate/incidents).
:::

:::

## Investigate

:::steps

:::step What did it actually do?

```bash
veefore logs --agent agt_01JAV… --limit 500
```

Then the full lineage on anything interesting:

```bash
curl -s $CP/v1/actions/act_01ABC -H "Authorization: Bearer $KEY"
```

Read: the delegation chain it acted under, the policies consulted, the rules
that matched, the risk signals, the destinations, and the downstream statuses.
:::

:::step Was it authority or behaviour?

Two very different findings.

**Authority was exceeded and refused.** `AUTHORITY_EXCEEDED` or `ACTION_BLOCKED`
in the log means enforcement held. The agent tried and failed. Bad behaviour,
contained.

**Authority was sufficient and the actions succeeded.** The agent did something
harmful *within* what it was granted. Enforcement worked as configured and the
configuration was wrong. That is a policy and delegation problem, and the fix is
narrower grants — not a tighter agent.

```bash
curl -s $CP/v1/agents/agt_01JAV…/authority -H "Authorization: Bearer $KEY"
```

Read the **resolved** authority. This is frequently the moment someone discovers
a ceiling nobody intended.
:::

:::step Was the identity used from somewhere unexpected?

```bash
veefore audit list --type agent.authenticated --from 2026-10-03T00:00:00Z
```

Session records carry the source address and the reported SDK. An authentication
from an address your agent does not run at is the difference between "our agent
misbehaved" and "someone else has the key".

Also check authentication failures:

```promql
sum by (reason) (increase(veefore_authentication_failures_total[6h]))
```

A spike in `nonce_replayed` means someone replayed a captured assertion, which
is a strong signal of interception.
:::

:::step Which credentials were brokered?

The action records list credential leases by reference. Anything the agent
reached is a credential whose downstream may have been used.

Note that the agent never held the material — it received brokered leases, each
short-lived and scoped to one action. The exposure is bounded to what those
leases could do during their lifetime, not to the credential's whole lifetime.
That bound is the point of the broker.
:::

:::

## Decide

| Finding | Response |
| --- | --- |
| Bug in your agent | Fix the agent. Resume. Keep the incident open until deployed |
| Authority too broad | Narrow the delegation and the policy. Resume |
| Prompt injection, bounded by policy | Enforcement worked. Tighten the rule, resume |
| Identity used from an unexpected place | **Revoke.** Go to the next section |
| Key material exposed | **Revoke.** Go to the next section |

## Revoke, if it comes to that

```bash
veefore agents revoke agt_01JAV… --reason "Identity used from an unrecognised address; see inc_01JAV…"
```

The CLI shows the full blast radius — sessions, cascading delegations, abandoned
approvals — and requires step-up. It is terminal.

Then:

:::steps

:::step Rotate everything the agent could reach

Every credential attached to a tool the agent was authorized for. Not only the
ones it used — you are no longer certain what it could see.

```bash
curl -X POST $CP/v1/credentials/cred_01ABC/rotate \
  -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{ "material": { "api_key": "…", "header": "x-api-key" } }'
```
:::

:::step Register a replacement agent

```bash
veefore init --name finance-agent-2 --capability refund.create
```

A new key pair, generated locally. Grant it the narrowest authority that works —
this is the moment to fix whatever was too broad.
:::

:::step Check for lateral reach

```bash
curl -s $CP/v1/delegations/graph -H "Authorization: Bearer $KEY"
```

Did the compromised agent hold delegations it could have issued onward? The
cascade handles the grants it made, but look at what it *could* have made and at
what else shares its credentials.
:::

:::

## Close out

- Write what you found on the incident. An incident closed with no note teaches nothing.
- Close the incident, which lifts the rules that depended on it.
- Record trust evidence if a review was done.
- `veefore doctor --strict`.
- `veefore audit verify`.
- Ask: would a narrower delegation have prevented this? Usually the answer is yes, and that is the durable fix.

## Related

:::cards

:::card Emergency controls | /operate/emergency-controls
All seven, with blast radius previews.
:::

:::card Credential leak | /operate/runbook-credential-leak
If a secret is the problem.
:::

:::card Delegate authority | /build/delegate-authority
Narrowing grants after the fact.
:::

:::card Incidents | /operate/incidents
Letting policy react to an investigation.
:::

:::
