Open SCITT profile · early-stage draft (IETF Internet-Draft) · open source

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 →

Built on IETF SCITT (RFC 9943) Verifier + producer, open source Cross-implementation tested (RFC9162 · CCF) Watch the log live →
In plain terms

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? →

The problem

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.

Signed

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.

Transparent

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.

Third-party verifiable

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.

How it works

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.

1 of 2 · Creating capsules

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.

2 of 2 · Using capsules

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.

See both sides, step by step →

Add it to your stack

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:

Goose — pip install capsule-emit · FastMCP extension
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 extension
Goose adapter docs ↗ · verified end-to-end against Goose v1.39.0 ↗
MCP / any callable — pip install capsule-emit
from 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)
MCP adapter docs ↗
LangChain / LangGraph — pip install "capsule-emit[langchain]"
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]})
LangChain adapter docs ↗ · listed in LangChain’s integrations docs ↗
CrewAI — pip install "capsule-emit[crewai]"
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])
CrewAI adapter docs ↗
Hermes / any custom loop — pip install capsule-emit
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
Hermes adapter docs ↗
agentgateway — seal at the gateway chokepoint (one policy point, not N integrations)
# 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 required
agentgateway adapter docs ↗ · verified at ExtMcp gRPC protocol boundary ↗
Google ADK — pip install "capsule-emit[adk]"
from 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
)
ADK adapter docs ↗ · verified against google-adk 2.3.x–2.9.1 ↗
Agno — pip install "capsule-emit[agno]"
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 here
Agno adapter docs ↗ · verified against agno 3.0.0–3.0.10 ↗
Dapr Agents — pip install "capsule-emit[dapr-agents]"
from 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}
Dapr Agents adapter docs ↗ · re-verified against dapr-agents 1.0.5–1.0.6 ↗
LiteLLM proxy — pip install "capsule-emit[litellm]"
# 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.yaml
LiteLLM adapter docs ↗ · verified against litellm 1.99.0–1.101.0 ↗
LlamaIndex — pip install "capsule-emit[llamaindex]"
from 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 needed
LlamaIndex adapter docs ↗ · pin llama-index-workflows<2.24 — 2.24.0 breaks FunctionAgent.run(), tracked in #182 ↗
Microsoft Agent Framework — pip install "capsule-emit[msft-agent-framework]"
from 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"),
)
Microsoft Agent Framework adapter docs ↗ · verified against agent-framework-core 1.16.0–1.18.0 ↗
OpenAI Agents SDK — pip install "capsule-emit[openai-agents]" # openai-agents 0.22+, Python 3.10+
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 span
OpenAI Agents adapter docs ↗ · verified against openai-agents 0.22.0–0.22.2 ↗
Strands Agents — pip install "capsule-emit[strands]" # strands-agents 1.54+
from 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 here
Strands Agents adapter docs ↗ · verified against capsule-emit 0.8.6 · strands-agents 1.54.0–1.56.0 ↗

Don't see your framework? The base is ~30 lines — add an adapter or open a request ↗. Full guides live in capsule-emit/docs/adapters.

What's sealed

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:

Decision

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.

Attestation

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.

Chain

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.

capsule_id (the seal) · sealed ✓
operator 4515…10de · developer po-agent@v1 · decide → executed · input 18e7…ba81 · output 406f…9c54
Explore a capsule →

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 →

The stack

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.

layer 1The profile
the standardagent-action-capsule — the Signed Statement format (COSE_Sign1 over application/agent-action-capsule+json) + the reference verifier.
↓ produced by
layer 2The producer
capsule-emit — seal an action in one call, or wrap an existing tool with one decorator. The agent-developer on-ramp.
↓ registered to · receipt verified by
layer 3The verifier
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).
↓ witnessed by
layer 4The log
capsule-anchor — a neutral, product-free SCITT Transparency Service. Live at witness.agentactioncapsule.org.
↓ optionally checkpointed by
layer 5Your local log
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.
How it fits your stack

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.

LegAnswersDoesn't answer
Identity e.g. SPIFFE/WIMSE, agent IDswhich agent is actingwhich human authorized the action (WHO), or what it did
CAN — Authorization e.g. OAuth/AAuth, GNAP, policywhat it may dowhat it actually did, and the outcome
Payments / settlement e.g. AP2whether a human authorized the spendwhat was actually done, end-to-end
AUDIT — Runtime gate / observability e.g. a policy gateway, your logswhat you say happenednothing to a party who doesn't trust you — it's self-attested
WHAT — Agent Action Capsulewhat it verifiably didcomposes 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 →

Composition

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.

SlotAnswersFilled by
CAN — Authorization e.g. OAuth/AAuth, GNAP, policywas the agent permitted to act?an authorization record
WHO — Named-human authorization e.g. a device-bound authorization receiptwhich accountable human authorized this exact action, before it ran?an offline-verifiable authorization receipt
WHAT — Agent Action Capsulewhat did the agent actually do? (verdict-complete, byte-stable)the Agent Action Capsule
AUDIT — Runtime gate e.g. a policy gateway, observability logdid 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 →

A record can be produced at different assurance levels. Self-attested (signed by the agent or operator) is a valid baseline. Anchored / third-party-verifiable (the spec's 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.

canonicalpayloadbinding.org ↗

Verify

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
1

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.

2

Signature + inclusion, independently

The signature proves who sealed it; the receipt proves the log included it. Both check from the bytes.

3

VDS-agnostic

The same statement verifies whether the log uses RFC 9162 Merkle proofs or CCF (Confidential Consortium Framework). The action layer never changes.

Disclosure

Hand someone the record — not your word for it.

Because a capsule verifies from the bytes, you can give an auditor, a reviewer, or a regulator the capsules for what your agent did and say: here's what happened — don't trust me, check it yourself. They confirm each seal and the chain links with no access to your systems, and the contents stay private — digests, not raw data.

Adoption path

One config change per rung — start in ten minutes, scale as you need.

Rung 1 · Stub

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

Rung 2 · Self-hosted witness

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

Rung 3 · Public witness

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 ↗

What leaves your walls

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.

Public log

Zero-setup proofs

Witness at the shared, open log at witness.agentactioncapsule.org. A digest + timestamp leaves; your data never does.

Your region

Residency control

Run the open witness log in the region or jurisdiction you need (e.g. EU, Singapore). The hash stays in your jurisdiction.

Your VPC

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 →

Checkpointed local log

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 /registerPOST /checkpoints
Granularityone recordthe whole log
What leavesa digest per action, and its timing~300–500 content-free bytes, covering any number of records
You get backa receipt with an inclusion proof, usable immediatelya receipt for the signed checkpoint, pinning your history outside you
Cost shapeone round-trip per actionflat, regardless of volume
Needsnothing extrayour 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.

Interop

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.

TEST VECTORS · CONTRIBUTED UPSTREAM · NOT A WG ENDORSEMENT

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 ↗
TESTED AGAINST · CCF

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.

scitt-cose ↗
DELIVERED · IETF 126

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 ↗
INDEPENDENT PROJECT · USES CLL

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 ↗
Open source

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.

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 →

Who's behind this

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.

Get involved

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:

Adopt

Seal your agent's actions

Wrap a tool with one decorator, witness each consequential call, and verify from the bytes.

Quickstart → Adopters — start here ↗
Contribute

Add an adapter

Bring your framework's seam onto the shared base, or open a request — so producers stay one format.

capsule-emit ↗
Shape it

Help the standard

The profile is an individual IETF Internet-Draft. Review it, bring test vectors, and engage in SCITT.

Read the draft ↗
Specifications

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).

Vectors and implementations for each draft →

New here? Read how the project is governed → · Join the conversation on GitHub.