Skip to Content
ConceptsAudit & revocation
Open in Claude Open in ChatGPT

Audit & revocation

Every action an agent takes — and every action it tried to take but wasn’t allowed to — writes a row to your audit log. When something goes wrong, this is the artefact.

What’s recorded

A single audit row contains:

  • agent_id — which agent
  • platform_id — which platform was called
  • action — the scope that was used (or attempted)
  • result — allowed, pending_approval, denied, or error
  • metadata — JSON with the reason, response status, approval ID if relevant, circuit-breaker failure count if relevant
  • created_at — timestamp
  • request_id — a correlation ID you can trace through proxy logs and back to the original agent call

The row is written after the action attempt, regardless of outcome. Denied calls are logged. Errors are logged. Approval queue entries log on creation and on resolution.

What’s retained

Retention varies by plan, with a 90-day floor — nothing younger than 90 days is ever pruned:

PlanAudit retention
Free90 days
Solo90 days
Studio90 days
Team365 days
Enterprise9,999 days (effectively unlimited)

A daily job deletes rows past your plan’s retention window. If you need a longer window than your plan provides, the cleanest path is to upgrade — the data isn’t there to recover later.

Forensics fields (Enterprise, opt-in)

On the Enterprise plan, you can flip a forensics_enabled toggle that captures additional fields on every audit row:

  • ip_address, user_agent (the network identity that called)
  • geo_country, geo_city, geo_asn (derived from IP)

request_id and session_id are not part of this: they’re recorded on every plan.

Forensics is off by default, even on Enterprise. Toggling it on records who turned it on and when, so the consent decision is itself audited. This is for organisations that need SOC 2-grade evidence of who-did-what-from-where.

Below Enterprise, those fields are always NULL. Privacy-by-default isn’t a marketing line; it’s how the schema works.

How writes are protected

The proxy writes audit rows through an insert-only database role, and a handful of backend functions and dashboard actions insert their own rows (registrations, anomaly checks, a breaker reset). Nobody gets update or delete rights on the table through the dashboard or the API, so there’s no everyday path to alter history. It isn’t strictly immutable, though: the retention job deletes rows past your window, and when an anonymous trial workspace is claimed into an existing account its rows are re-attributed to that account. The log is not hash-chained.

Every decision carries a signed receipt

Since 16 September 2026, every row the proxy writes for a decision on a governed call (allowed, held, denied, blocked, errored) and every approval replay is written together with a compact JWS receipt, signed at decision time by a dedicated ES256 key. The receipt stands alone: anyone holding it can verify what was decided, for which agent, under which policy version and rule, against a published key set, with no access to this database. The call’s audit_id (on _meta for a 2xx, on the body for a refusal) is the receipt’s jti. See Signed governance receipts for the payload, the fetch endpoint and verifier snippets.

Revocation

Sometimes you need an agent off, now. Revocation is implemented end-to-end:

  1. Set agents.status = 'revoked' (instant, via the dashboard) — on the agent and every child agent below it
  2. The agent’s next signed call is refused with 403 agent_halted, reason agent_revoked — the JWT is valid but the agent isn’t
  3. The revocation event itself is written to the audit log, as one row covering every agent it revoked

There’s no propagation delay. The proxy reads status on every call, as the first step of the permission check.

Revocation cuts the agent off; it doesn’t touch credentials. Nothing is removed from the vault and no token is revoked at the upstream SaaS, so your platform connections keep working for your other agents. To end AgentValet’s access to a platform itself, disconnect the platform.

What revocation doesn’t cover yet

Today, revocation is all-or-nothing per agent. You can’t say “this agent keeps Slack but loses Stripe” in one action — you’d remove the Stripe permission row, which has the same effect for that platform but takes a separate operation.

Cascading revocation across federated identity providers (SSF / CAEP) is on the roadmap. The intent is that revoking an agent in AgentValet also notifies any identity providers that issued downstream tokens — so a compromised agent loses its access globally, not just at AgentValet’s boundary. That depends on the ZeroID integration and isn’t in production yet.

Reading your audit log

Open Audit in the dashboard.

Audit log with date range, agent, and action filters
The audit log, filterable by date range, agent, and action type.

There are two tabs:

  • Audit — the chronological log of agent actions (allowed, denied, pending, errored)
  • Policy decisions — explicit AuthZEN evaluations (an agent calling authzen_evaluate to ask whether an action would be allowed): subject, action, resource, decision, reason. Ordinary denials show up on the Audit tab, not here.

You can filter by date range, agent, action type, and free-text. Export to CSV or JSON is a Team-plan feature; the in-app filtering is on every plan.

Next

Last updated on