agent-custody

Proof of what your AI agents did.

agent-custody records it, denies what was never authorized, and proves it to anyone.

A gateway between an agent and its tools. Every call is checked against a grant a person signed and a policy, then forwarded or denied, and a signed receipt is issued either way. The receipt's hash lands in a log nobody in the chain can rewrite. For every agent framework, in any language.

Register for a free accountView GitHub Verify a receipt

93 seconds, narrated. What agent-custody is, how it works, and a refund agent caught by a prompt injection. The receipts in it are real ones from bun run demo, against a stand-in Stripe.

The problem

Your agents' record is their own word.

When an agent acts wrongly, the only account of it is the log its own process wrote, kept by the team that ran it. Every field is a claim. Nobody outside the team can tell what was authorized from what merely happened.

Made-up authority

The agent refunds £50,000. Its log says the customer asked for it. Nobody signed anything that allowed it.

Looks like: a closed ticket

A log that can be edited

The team that ran the agent holds the log. After the incident, the entry that matters is gone, or different, and nobody can tell.

Looks like: a clean audit

Evidence nobody else can check

Traces sit in a vendor account. The auditor, the customer, or the regulator gets a screenshot and a promise.

Looks like: compliance

What a gateway receipt looks like instead
decision
denied no permit policy matched
tool
stripe.refund observed
asked for
refund £50,000.00 to cust_123 claimed
gateway checked
customer cust_123, verified true observed, fetched by the gateway
policy
refunds up to £1,000, verified customer · ba4e4461ffc4…
agent
support-agent attested
authorized by
user_456 attested, a grant they signed
log
leaf 2 of 2 · root 910a95eef12e…

Open in the verifierFrom the published conformance vectors. Runs in your browser; nothing is uploaded.

A receipt proves what was signed, observed, and logged, and labels everything else as the agent's own claim. The proof table says which is which, for whoever has to sign off. Where the data goes, what reaches the log, and how secrets are handled: the FAQ.

How it works

Authorize. Gate. Record. Log. Verify.

  1. 01Authorize

    A person signs a grant: which agent, which tools, for how long. The gateway trusts that key and nothing else.

  2. 02Gate

    Every call goes through the gateway. It fetches the facts itself, evaluates the policy, and forwards or denies before the tool hears anything.

  3. 03Record

    One signed receipt per call, allowed or denied: who authorized it, what the agent asked, what the gateway checked, what happened.

  4. 04Log

    The receipt's hash goes to a Merkle log whose signed heads are published on a second host. The operator cannot rewrite it unnoticed.

  5. 05Verify

    Anyone with the public keys checks a receipt, in the shell or in the browser, offline. No account, no access to the agent.

Principala person, with a keyAgentany frameworkGatewaychecks the grantevaluates the policyfetches facts itselfToolMCP, REST, anythingsigns a granttrusted keytool callonly if permittedone per call, allowed or deniedSigned receiptwho · what · saw · did · dependedhashMerkle logsigned headsCheckpointspublishedVerifierpublic keys, nothing elsereads
The gateway sits between the agent and its tools. Every call becomes a signed receipt whose hash lands in a log the agent cannot rewrite. Anyone with the public keys can verify a receipt, offline.

Denied at runtime

The call never reaches the tool.

A policy is a Cedar file. The gateway evaluates it against what the agent asked and the facts it fetched itself. Consequential calls are logged before they are forwarded, so the evidence exists before the side effect does. A denial is a receipt too.

What it looks like from the shell

Not a trace

Not a trace. Not an application log.

Application logTrace
OpenTelemetry, LangSmith
Gateway receipt
Written bythe agent's processthe agent's processa gateway the agent talks to, signing with its own key
Says the call was allowed or denied before the tool rannonoyes, and the denial is a receipt too
Who authorized itnot recordednot recordedthe grant a person signed, embedded and verified
Can a third party show the record was not rewrittennonoyes: a Merkle log with heads signed by a key the operator does not hold
Who can check itwhoever has the log accesswhoever has the tracing accountanyone with the public keys, offline

Traces stay useful. A receipt can be exported to OpenTelemetry or Splunk as one span or event per call, with the receipt id as the trace id, so the evidence and the observability sit side by side.

Quick start

A verified receipt in ten minutes.

  1. 1Install and make keys

    Node 22 or later. One key for the gateway, one for the person who signs grants.

  2. 2Sign a grant

    Which agent, which tools, for how long. The gateway trusts the principal's public key.

  3. 3Put the gateway in front of the tools

    It is an MCP server. Point your agent at it instead of at the tools, or use an SDK adapter: Claude Code, Claude Agent SDK, OpenAI Agents SDK, LangChain, Vercel AI, Python.

  4. 4Verify the receipt

    In the shell, or drop it on the browser verifier. Register a tenant on the hosted log and the receipt's hash is somewhere you cannot rewrite.

terminal
# 1. install and make keys
npm install @agent-custody/receipts
npx agent-custody keygen --dir keys --name gateway
npx agent-custody keygen --dir keys --name principal

# 2. sign a grant for the agent
npx agent-custody grant --key keys/principal.key \
  --principal user_456 --agent support-agent \
  --scopes customer.lookup,stripe.refund --out grant.json

# 3. run the gateway in front of the tools
npx agent-custody gateway --config gateway.json

# 4. verify a receipt it issued
npx agent-custody verify receipts/<id>.json \
  --issuer-key keys/gateway.pub --principal-key keys/principal.pub
gateway.json
{
  "identity": { "keyFile": "keys/gateway.key" },
  "grantFile": "grant.json",
  "trustedPrincipalKeys": ["keys/principal.pub"],
  "policyFile": "policy.cedar",
  "upstream": { "command": "your-mcp-server" },
  "precommit": ["stripe.refund"],
  "receiptsDir": "receipts",
  "log": { "url": "https://log.agent-custody.dev/t/<tenant>/",
           "tokenEnv": "AGENT_CUSTODY_LOG_TOKEN", "hashOnly": true }
}

The whole path, with every command's output, is Getting started. Twenty runnable tutorials cover the rest.

Where to go

This repository is developed under custody: every tool call the coding agent makes is a receipt, hash-logged to our tenant on the hosted log. The custody page shows the hook, the policy, the keys, and two of those receipts to verify. Everything you run yourself is Apache-2.0.