Show what your agent did —
to someone who doesn’t trust you.
AI agents are just starting to take consequential actions — placing orders, moving money, changing records. When one does, the only evidence it happened as intended is the operator's own word. An Agent Action Capsule fixes that: a tamper-evident, content-private record of the action that anyone can verify from the record itself — without trusting the operator.
It's early — for the technology and for this profile. The aim is to get the record of what agents do right while it's still being figured out: in the open, verifiable by anyone, and built to be donated so it ends up owned by no one. (Roughly the role TLS plays for a connection, applied to an agent's actions.)
pip install capsule-emit → receipt = seal(payload) — a signed, witnessed record a stranger can verify. No config, keys, or account required. See it as real, runnable code →
A tamper-evident record of what your AI agent did.
Say your agent pays an invoice. Six months later an auditor asks: did it really only pay approved vendors, for the right amounts? Today your only answer is your own logs — your word for it. An Agent Action Capsule is a signed, privacy-preserving capsule of each consequential action, witnessed by an independent log, so you can hand the auditor proof they can check for themselves — without giving them access to your systems, and without asking them to trust you.
Who it's for today: agent developers who want verifiable records, and standards / security folks. It's early and built in the open — not yet a turnkey compliance product. What exactly is a capsule? →
Agents take actions. Right now, you have to trust the operator that they happened as claimed.
The Agent Action Capsule closes that gap with three properties — the same shape SCITT defines for software supply chains, profiled for agent actions. SCITT is the IETF effort standardizing transparency for signed statements: its Architecture is RFC 9943 and its COSE Receipts are RFC 9942, both published.
Sealed at the moment of action
A capsule is a statement committing to the action, its inputs and outputs (by digest), and the model/runtime that produced it — always content-addressed, and signed with COSE (the standard format for signing records like this). The specification makes the signature optional; the reference producer signs every capsule by default. Tamper any byte and verification fails.
Witnessed by an independent log
The statement is registered to a SCITT Transparency Service, which returns a receipt proving inclusion. The log is tamper-evident: a rewrite would be detectable.
Anyone can check, offline
An auditor, reviewer, or regulator verifies the signature and the inclusion proof from the bytes alone — no access to the operator's systems. You trust the log's key, not the operator.
A log is enough, until someone else has to believe it.
An ordinary log is the operator's diary: editable, deletable, and written by the system it describes. That is fine for debugging. It stops being enough when money moves, when a duty applies, or when someone else was harmed, because each of those has a party who was not in the room and does not take your word for it.
Sealed at the boundary, dated by a witness
Each action is sealed where it leaves the agent, by a key the model never holds. The record carries digests, and the content stays with the organisation. A signed checkpoint of the whole log goes to a witness, a SCITT Transparency Service that sees no content and returns a Receipt. Nothing else has left, and the date is already fixed.
Asked for later, recomputed by the asker
Months later, a party entitled to ask requests a slice. The answer is the records with their inclusion proofs, or a signed refusal with a reason. The asker checks the signatures, the inclusion and the witness receipt offline. The date was fixed before anyone asked.
Seal from the framework you already use.
One capsule, one witness, one verify — an adapter only changes where you hook in, over a ~30-line base. No protocol change; input and output are sealed automatically. Adapters seal consequential actions (writes + sensitive reads), not every call — what counts? → Pick your runtime and copy the code:
from mcp.server.fastmcp import FastMCP
from capsule_emit.adapters.mcp import MCPCapsuleEmitter
server = FastMCP("po-agent")
emitter = MCPCapsuleEmitter(operator="acme-co", developer="goose-agent@v1")
@server.tool() # Goose connects over MCP
@emitter.tool(effect_type="write_order") # capsule-emit seals each consequential call
def submit_order(vendor, amount, po_number):
return place(vendor, amount, po_number)
server.run() # stdio — add to Goose as an extensionfrom capsule_emit.adapters.mcp import MCPCapsuleEmitter
emitter = MCPCapsuleEmitter(operator="acme-co", developer="po-agent@v1")
@emitter.tool("submit_order") # one line — each consequential call sealed + witnessed
def submit_order(vendor, amount, po_number):
return place(vendor, amount, po_number)from capsule_emit.adapters.langchain_listener import LangChainCapsuleListener
listener = LangChainCapsuleListener(operator="acme-co", developer="research-agent@v1")
# attach as a callback — planned then confirmed, one chained pair per consequential tool call
agent.invoke(payload, config={"callbacks": [listener]})from capsule_emit.adapters.crewai import CrewAICapsuleEmitter
emitter = CrewAICapsuleEmitter(operator="acme-co", developer="ops-agent@v1")
safe_tool = emitter.wrap(my_tool) # one capsule per consequential call
agent = Agent(role="AP clerk", tools=[safe_tool])from capsule_emit.adapters.hermes import HermesCapsuleEmitter
emitter = HermesCapsuleEmitter(operator="acme-co", developer="hermes-agent@v1")
result = execute_tool(name, inputs)
emitter.after_tool(name, inputs, result) # one line at the tool boundary# 1. run the capsule-emit policy service (gRPC ExtMcp hook)
pip install "capsule-emit[agentgateway]"
capsule-emit-agentgateway # listens on :50051
# 2. point agentgateway at it — seal tools/call only (in config.yaml)
mcpGuardrails:
processors:
- kind: remote
host: "localhost:50051"
methods: { "tools/call": full } # reads pass through
failureMode: failOpen # an outage lets calls through UNSEALED;
# use failClosed where a record is requiredfrom google.adk.agents import LlmAgent
from capsule_emit.adapters.adk import ADKCapsuleEmitter
emitter = ADKCapsuleEmitter(operator="acme-co", developer="po-agent@v1")
agent = LlmAgent(
name="writer", tools=[write_order],
after_tool_callback=emitter.after_tool_callback, # one capsule per consequential call
)from agno.agent import Agent
from capsule_emit.adapters.agno_listener import AgnoCapsuleListener
listener = AgnoCapsuleListener(operator="acme-co", developer="my-agent@v1")
agent = Agent(model=None, tools=[], tool_hooks=[listener.hook]) # your model and tools herefrom capsule_emit.adapters.dapr_agents import DaprAgentsCapsuleEmitter
emitter = DaprAgentsCapsuleEmitter(
operator="acme-co", developer="invoice-agent@v1",
agent_name="invoice-checker", app_id="invoice-app",
workflow_instance_id="wf-abc123", # set per workflow run
)
@emitter.tool("check_invoice") # one capsule per consequential call
def check_invoice(invoice_id: str, amount: str) -> dict:
return {"invoice_id": invoice_id, "ok": True}# config.yaml — one line is the whole integration
litellm_settings:
callbacks: ["capsule_emit.adapters.litellm_listener.proxy_handler_instance"]
# then, at the shell:
export CAPSULE_EMIT_OPERATOR=acme-co
export CAPSULE_EMIT_DEVELOPER=support-gateway@v1
litellm --config config.yamlfrom llama_index.core.agent.workflow import FunctionAgent
from capsule_emit.adapters.llamaindex_listener import LlamaIndexCapsuleListener
listener = LlamaIndexCapsuleListener(operator="acme-co", developer="my-agent@v1")
listener.install() # registers on LlamaIndex's process-wide dispatcher — no agent/tool changes neededfrom agent_framework import Agent
from capsule_emit.adapters.msft_agent_framework import capsule_middleware
agent = Agent(
chat_client, "you are a procurement assistant",
tools=[get_price, submit_order],
middleware=capsule_middleware(operator="acme-co", developer="my-agent@v1"),
)from agents import Agent, Runner
from agents.tracing import add_trace_processor
from capsule_emit.adapters.openai_agents_listener import OpenAIAgentsCapsuleProcessor
processor = OpenAIAgentsCapsuleProcessor(operator="acme-co", developer="my-agent@v1")
add_trace_processor(processor) # keeps the SDK's own exporter; one capsule per tool-call spanfrom strands import Agent
from capsule_emit.adapters.strands_listener import StrandsCapsuleListener
listener = StrandsCapsuleListener(operator="acme-co", developer="my-agent@v1")
agent = Agent(model=None, tools=[], hooks=[listener]) # your model and tools hereDon't see your framework? The base is ~30 lines — add an adapter or open a request ↗. Full guides live in capsule-emit/docs/adapters.
Purpose-built for agent actions — not generic attestation.
Its tamper-evident seal is the capsule_id — a SHA-256 of the statement's canonical form (RFC 8785 JCS); recompute it and it must match. And it's content-private by construction: a capsule carries digests of your inputs and outputs, never the raw prompts, vendors, or amounts. Around that seal it commits exactly what an agent action needs to be accountable:
The may/did, bound to its effect
What the agent decided and whether it executed, was confirmed, denied, or blocked — with a confirmed-effect binding, so the claim and the real outcome can't drift apart.
Which model decided, by digest
The model that decided, with the full input it saw — system prompt, context, tools, and the action's arguments — and the output, each committed as a SHA-256 digest. The proof travels; the raw values stay with you.
Trails for human-in-the-loop
Confirm, supersede, and escalate links turn approved → executed → confirmed into one verifiable chain — the basis for review and selective disclosure.
That's the shape a build-artifact attestation doesn't give you — and the reason to standardize one agent-action profile together rather than reinvent it. What's in a capsule → · tamper one, watch the seal break →
One statement layer. Any transparency service. Neutral, donatable substrate.
The capsule is a statement-layer profile — it makes no claim about which verifiable-data-structure the log uses. Each layer is a separate open-source library.
agent-action-capsule — the Signed Statement format (COSE_Sign1 over application/agent-action-capsule+json) + the reference verifier.capsule-emit — seal an action in one call, or wrap an existing tool with one decorator. The agent-developer on-ramp.scitt-cose — verifies SCITT receipts across verifiable-data-structures (vds = the log's proof format): RFC9162_SHA256 (vds=1) and CCF ccf.v1 (vds=2).capsule-anchor — a neutral, product-free SCITT Transparency Service. Live at witness.agentactioncapsule.org.checkpointed-local-log (CLL) — the append-only local log a client keeps between witnessings. Its signed checkpoints are what POST /checkpoints registers, covering any number of records in one flat-cost round trip.Composes with what you already run — it doesn't replace it.
Identity, authorization, payments, runtime gates — each covers its own slice, and each is self-attested about it. The capsule composes with all of them by digest-reference, and adds the one thing none can self-provide: a record checkable by someone who trusts neither the operator nor any single layer.
| Leg | Answers | Doesn't answer |
|---|---|---|
| Identity e.g. SPIFFE/WIMSE, agent IDs | which agent is acting | which human authorized the action (WHO), or what it did |
| CAN — Authorization e.g. OAuth/AAuth, GNAP, policy | what it may do | what it actually did, and the outcome |
| Payments / settlement e.g. AP2 | whether a human authorized the spend | what was actually done, end-to-end |
| AUDIT — Runtime gate / observability e.g. a policy gateway, your logs | what you say happened | nothing to a party who doesn't trust you — it's self-attested |
| WHAT — Agent Action Capsule | what it verifiably did | composes with all of the above |
Each layer is self-attested about its own slice; the capsule is the independent record of what was actually done that they all commit into. Adopting it doesn't fork your stack — it references the others' evidence by digest. How it composes → · what's worth sealing →
Independently-verifiable records, joined by a shared action digest.
An accountable record often needs to answer more than one question — was the action permitted, which human authorized it, what the agent actually did, did the runtime enforce correctly. The Agent Action Capsule answers what it did. It composes with records that answer the others: each is produced and verified on its own, and they join by reference to a shared digest over the action — the join key every record refers to.
| Slot | Answers | Filled by |
|---|---|---|
| CAN — Authorization e.g. OAuth/AAuth, GNAP, policy | was the agent permitted to act? | an authorization record |
| WHO — Named-human authorization e.g. a device-bound authorization receipt | which accountable human authorized this exact action, before it ran? | an offline-verifiable authorization receipt |
| WHAT — Agent Action Capsule | what did the agent actually do? (verdict-complete, byte-stable) | the Agent Action Capsule |
| AUDIT — Runtime gate e.g. a policy gateway, observability log | did the runtime enforce correctly, in causal order, tamper-evidently? | a gateway or observability record |
Any conforming record may fill a slot; the set is open by design. Each slot may root in a different trust anchor, and the join holds even if one party is compromised or under review — no slot is a required root of trust for another. Digest equality is a join key, not proof of truth, authorization, or sufficiency: each record's native verification, digest recomputation, and receipt verification stay separate results that a verifier must never collapse into one boolean. How it composes with your stack →
anchored tier) means the record, or a digest of it, is registered to a SCITT transparency service, so a party who trusts neither the agent nor the operator can verify its existence and non-equivocation. Composition doesn't mandate the anchored tier — it specifies how any conforming record can reach it.The binding underneath
For records to join on the same digest they need one shared way to compute it. The Agent Action Capsule declares its canonical form through the Canonical Payload Binding (CPB), a separate IETF draft with its own open, cross-organization registry; AAC is one registered artifact type there among peers.
From the bytes. Offline. Trust the log's key — not the operator.
This is what an auditor runs against a ledger of capsules. Tamper any field, or the signature, and the record turns INVALID.
# install capsule-emit (the reference verifier ships with it) pip install capsule-emit # verify a ledger of sealed actions, offline capsule-emit verify --store ledger.jsonl VALID VALID 2/2 VALID # inclusion on the public log: substrate.receipt_verified: True
Inputs & outputs committed by digest
The capsule seals SHA-256 digests of what went in and came out. Raw values stay local; the proof travels.
Signature + inclusion, independently
The signature proves who sealed it; the receipt proves the log included it. Both check from the bytes.
VDS-agnostic
The same statement verifies whether the log uses RFC 9162 Merkle proofs or CCF (Confidential Consortium Framework). The action layer never changes.
One config change per rung — start in ten minutes, scale as you need.
Structured records, offline verify
Set CAPSULE_WITNESS=off. Signed structured records, local ledger file, offline verification. Zero services. The ten-minute first touch — no network, no account.
Page renders: self-attested / not witnessed
Real receipts, your trust domain
Deploy your own capsule-anchor instance. Real inclusion receipts within your org — tamper-evident across teams, honest internal separation of duties.
Policy descriptor tier: self-operated
Third-party verifiable
One config change: point at the public witness. Records become third-party-verifiable; permalinks are shareable. No signup, no key, no cost.
Policy descriptor tier: public
Full adoption guide: ADOPT.md ↗
Only a hash leaves. Witness anywhere — including your own VPC.
A capsule is content-private by construction. When you witness it, the only thing that travels to the log is a digest and a timestamp — never prompts, payloads, reasoning, or PII. The raw content stays under your control.
Zero-setup proofs
Witness at the shared, open log at witness.agentactioncapsule.org. A digest + timestamp leaves; your data never does.
Residency control
Run the open witness log in the region or jurisdiction you need (e.g. EU, Singapore). The hash stays in your jurisdiction.
Nothing leaves
Self-host the witness (capsule-anchor) container inside your own environment — then even the hash never leaves your walls.
The capsule does not depend on any one Transparency Service, so you can move, or register with more than one, without changing the record. One caveat: a digest only hides values that aren't guessable — for low-entropy fields (a short amount, a known ID), salt before hashing. What a digest hides →
Register one action, or checkpoint your whole log — ~300 bytes, any volume.
The witness answers two different requests. POST /register commits one record and hands back a receipt for it. POST /checkpoints — the default route every capsule-emit client uses — commits a signed snapshot of your entire local log (kept by checkpointed-local-log, CLL) at once. A checkpoint's cost doesn't grow with your log: measured against the reference implementation, a signed checkpoint runs roughly 300–500 content-free bytes whether your log holds one record or a million.
POST /register | POST /checkpoints | |
|---|---|---|
| Granularity | one record | the whole log |
| What leaves | a digest per action, and its timing | ~300–500 content-free bytes, covering any number of records |
| You get back | a receipt with an inclusion proof, usable immediately | a receipt for the signed checkpoint, pinning your history outside you |
| Cost shape | one round-trip per action | flat, regardless of volume |
| Needs | nothing extra | your own local log (checkpointed-local-log) |
A checkpoint lets anyone check that your log's history hasn't been rewritten since it was signed — it does not, on its own, hand a third party a per-record proof. To disclose a specific action later, you show the record plus a proof it falls inside a covered checkpoint. Checkpointing doesn't replace registration; it answers a different question.
Tested against other implementations — not just our own.
The statement layer is fixed; the verifiable-data-structure is the log's choice. We've tested that it holds across independent implementations.
Shared test vectors
Our RFC9162_SHA256 cross-implementation vectors were contributed to, and merged into, the SCITT working group's examples repository — a reference anyone can verify against. It's a merged contribution, not a WG endorsement of this profile.
ietf-wg-scitt/examples ↗Two-party, two logs
One capsule, registered to an RFC9162 log and a CCF (Confidential Consortium Framework) development node — both receipts verify with scitt-cose's verify_receipt, which reads both structures. (CCF uses vds=2; RFC 9162 uses vds=1.) Tested against CCF; not an endorsement by the CCF project.
Hackathon / interop report
Presented to the SCITT working group at IETF 126 (Vienna, 24 July 2026): byte and digest agreement across independent implementations, and one signed statement registered with two different Transparency Services — a CCF service and an RFC 9162 log — with both receipts verified independently.
SCITT session slides ↗ · minutes ↗The TRACE Registry
A public, append-only registry of anchors for signed TRACE Trust Records. Its checkpoint layer uses the Checkpointed Local Log: an append-only MMR with signed checkpoints, and a witness receipt over a checkpoint — each checkable with the registry's own published verifier.
How it uses CLL, and how to check an entry → · joint post ↗ · agentrust-io/trace-registry ↗Every layer is open source.
Read it, run it, fork it. The tooling is Apache-2.0; the specification follows the IETF Trust's terms (BCP 78/79, with code components under the Revised BSD License). Built to be donated to a neutral home, not owned.
POST /checkpoints: hash chains, a Merkle Mountain Range, and signed COSE checkpoints.Scope. The open project is the record layer — the profile, the producer (with example constraint manifests), the verifier, and the witness log. Acting on those constraints at runtime (enforcement) is a separate concern you compose with a policy gateway: the capsule records what happened, it doesn't gate. How the project is governed →
Built by an agent developer who wanted proof, not promises.
The Agent Action Capsule was created by Steven Mih — a startup founder and agent developer with a long background in open source, including helping steward openly and neutrally governed software (the Presto Foundation, under the Linux Foundation). He built it after hitting the wall everyone building agents hits: once an agent takes a real action, the only "proof" it happened as intended is the operator's own word. The capsule is the answer — a verifiable record anyone can check, built to be owned by no one.
Why build it this way? Verifiable records of what agents do are infrastructure the whole ecosystem depends on — and we think AI safety and open standards matter far too much for that layer to be controlled by any single company. So the design goal from day one is to give it away.
Where it stands today. The repositories are stewarded by Action State Group, with the stated intent to donate the profile, the trademark, and the reference services to a neutral foundation. Transparency Services are plural by design: a capsule verifies against whichever one registered it, and any party can run one (witness anywhere). The governance model is public and modeled on Linux Foundation practice, and co-maintainers are welcome to join. Read the governance model → · comments on the draft are welcome at spec@actionstate.ai and in the IETF SCITT working group.
An open profile to shape together — not another silo to fork.
The capsule is open and built to be donated. The useful move is to improve one shared profile together — feedback, competing ideas, and adapters all welcome. Ways in:
Seal your agent's actions
Wrap a tool with one decorator, witness each consequential call, and verify from the bytes.
Quickstart → Adopters — start here ↗Add an adapter
Bring your framework's seam onto the shared base, or open a request — so producers stay one format.
capsule-emit ↗Help the standard
The profile is an individual IETF Internet-Draft. Review it, bring test vectors, and engage in SCITT.
Read the draft ↗The Internet-Drafts, their vectors, and the code that implements them.
Each is an individual Internet-Draft submitted for discussion — not adopted by a working group, and not a standard. Revisions are read from the IETF Datatracker when the site is built (last read 2026-10-03 UTC).
- AAC draft-mih-scitt-agent-action-capsule
-05↗ - Selective disclosure draft-mih-scitt-agent-action-capsule-sel-disc
-00↗ - Disclosure envelope draft-mih-agent-disclosure-envelope
-00↗ - Evidence Bundle draft-mih-zhang-agent-disclosure-bundle
-00↗ - Evidence Request draft-mih-agent-evidence-request
-00↗ - Evidence layer — not yet posted
- CPB draft-mih-sokolov-scitt-payload-binding
-05↗ - Settlement records draft-mih-agent-settlement-records
-00↗ - Bilateral attestation draft-mih-agent-bilateral-attestation
-02↗ - Composition draft-mih-sato-agent-accountability-composition
-01↗ - Conformance draft-mih-agent-accountability-conformance
-00↗ - CLL draft-mih-scitt-checkpointed-local-log
-01↗ - MMR receipt profile (dependency) draft-bryce-cose-receipts-mmr-profile
-03↗ - COSE deterministic encodings — not yet posted
Vectors and implementations for each draft →
New here? Read how the project is governed → · Join the conversation on GitHub.