Action-time proof and delegation verification for MCP agents
Project description
AgentLedger
Your agent executed a tool. AgentLedger records — tamper-evidently — which action ran, on whose authority, and whether it stayed within the delegation it was given.
pip install agentledger-llm
What this is and isn't. AgentLedger is an audit layer, not an enforcement layer. By default a tool call that violates its receipt is recorded as a proof and still runs — it is not blocked. Hash-chaining makes the log tamper-evident; it does not prove an action was authorized at execution time by a cryptographically verified identity. See Security note.
The problem
OAuth tells you who authenticated. It doesn't tell you which specific action was authorized, by whom, through what delegation, with what record.
AgentLedger fills that gap — it records the delegation intent and an attributable, tamper-evident proof of each tool call against it.
20-second quickstart
import asyncio
from agentledger import Ledger
ledger = Ledger()
receipt = ledger.issue_receipt(
principal="user:daniel",
agent="agent:financial-assistant",
permitted_tools=["get_exchange_rates"],
permitted_scopes=["read:rates"],
expires_in=3600,
)
@ledger.record(receipt=receipt)
async def get_exchange_rates(params, context=None):
return {"base": "USD", "GBP": 0.79, "EUR": 0.92}
async def main():
await get_exchange_rates({"base": "USD"})
ledger.report()
ledger.verify(ledger.last().proof_id).print()
asyncio.run(main())
Want violations to block instead of just record? Opt in:
@ledger.record(receipt=receipt, on_violation="raise") # "record" (default) | "warn" | "raise"
async def delete_alert(params, context=None):
...
Cryptographic delegation (v2)
v1 records that an action matched a receipt. v2 adds cryptographic proof
that the grant was real — the principal signs the receipt (Ed25519), and the
verifier checks it against a trusted key. A within_delegation = True verdict on
a signed receipt means the named agent acted under a grant the principal
actually signed — not just that the strings matched.
pip install "agentledger-llm[crypto]"
from agentledger import Ledger, InMemoryKeyProvider
from agentledger.signing import generate_keypair
# Principal holds the private key and signs the grant
priv, pub = generate_keypair()
receipt = ledger.issue_receipt(principal="user:d", agent="agent:a",
permitted_tools=["get_rates"],
permitted_scopes=["read:rates"])
receipt.sign(priv)
# Verifier trusts only public keys it already holds (never one in the receipt)
ledger = Ledger(key_provider=InMemoryKeyProvider({"user:d": pub}))
@ledger.record(receipt=receipt, require_signed=True, scopes=["read:rates"])
async def get_rates(params, context=None):
return {"GBP": 0.79}
- Default is graceful: unsigned (v1-style) receipts still work — recorded
with
signature_verified=None, never reported as verified.require_signed=Truemakes unsigned/unverifiable a violation. - Scopes:
scopes=[...]checks each against the receipt'spermitted_scopes. - Agent identity (optional): pass an
IdentityProvider(e.g.SpiffeIdentityProvider) to bind the workload to the receipt's agent; without one, the verdict recordsidentity_status="unverified".
What v2 does and does not defend against is enumerated in docs/threat-model.md; the design is in docs/v2-design.md.
The three-layer model
AgentLedger sits after authentication, not instead of it:
| Layer | Standard | What it does |
|---|---|---|
| Authentication | OAuth 2.1 | Who are you? |
| Workload identity | WIMSE WPT + SPIFFE/SPIRE | Which agent are you? |
| Action proof | AgentLedger | What did you do, recorded against whom? |
What it records
For every tool call:
- Which tool was called, by which agent, for which principal
- Whether the call was within the delegation receipt
- Any scope violations — each with an explanation and a remediation
- A hash-chained, tamper-evident proof record
- Latency and error state
What it does NOT do
AgentLedger v1 explicitly does not:
- Replace OAuth or any identity provider
- Enforce authorization by default (it records; opt in with
on_violation="raise") - Provide mid-chain revocation (v3 — see roadmap)
- Handle multi-hop delegation chains (v3)
- Provide enterprise compliance (SOC 2, legal)
- Compete with Prefactor (enterprise) or KYA-OS (DID-based)
As of v2, signed receipts + a pluggable identity provider do add cryptographic delegation proof and agent-identity binding — within the limits set out in docs/threat-model.md (a compromised signing key, a malicious principal, and an in-process verifier remain out of scope).
See docs/roadmap.md for v2/v3 scope and entry criteria.
vs alternatives
| Tool | Open source | pip install | Action record | WIMSE-aligned | Developer-first |
|---|---|---|---|---|---|
| AgentLedger | ✅ | ✅ | ✅ | ✅ | ✅ |
| Prefactor | ❌ | ❌ | ✅ | ✅ | ❌ (enterprise) |
| KYA-OS | ✅ | ✅ | ✅ | ✅ | ⚠️ (DID-heavy) |
| FinMCP inline | ✅ | N/A | ✅ | ❌ | ✅ (one server) |
CLI
| Command | What it does | Exit code |
|---|---|---|
agentledger init |
Scaffold agentledger.yaml + working agent_example.py |
0 |
agentledger report [--log PATH] [--format terminal|html] |
Summarize the proof log | 0 always (informational) |
agentledger verify <proof_id> [--log PATH] |
Verdict for one proof | 1 if violations recorded |
agentledger chain [--log PATH] |
Verify hash-chain integrity | 1 if tampered |
TraceForge integration
If agentrace-llm is installed,
AgentLedger automatically attaches proof metadata (agentledger.proof_id,
agentledger.tool, agentledger.within_delegation, …) to the active TraceForge
span. If it isn't installed, enrichment is a silent no-op (a one-time warning
makes the no-op discoverable). Zero configuration required.
pip install "agentledger-llm[traceforge]"
Security note
Hash-chain integrity proves the proof log has not been tampered with after recording — and only while the log is read-only or externally anchored; an attacker with write access to the log can rewrite the whole chain. It does not prove actions were authorized at execution time by a cryptographically verified agent identity. Input/output digests are tamper-evidence, not confidentiality — low-entropy inputs can be recovered by guessing, so treat proof logs as sensitive. For cryptographic identity binding you need SPIFFE/SPIRE + AgentLedger v2. See docs/wimse-alignment.md.
Changelog
See CHANGELOG.md for release history.
Built by
Daniel Blanco · danblanco.dev · hello@danblanco.dev · Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentledger_llm-0.2.0.tar.gz.
File metadata
- Download URL: agentledger_llm-0.2.0.tar.gz
- Upload date:
- Size: 32.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7d402b247ffa31283786c6c3d9727b67341300fd68c8033bcbc6d41d0f54ce7
|
|
| MD5 |
31d70c62ec9ead2cc47bfe22f4399060
|
|
| BLAKE2b-256 |
488a909787fd28f60063f22f5764a9f7d27fb20c483105b86f770ac1094c2132
|
Provenance
The following attestation bundles were made for agentledger_llm-0.2.0.tar.gz:
Publisher:
release.yml on Danultimate/Agent-Ledger
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentledger_llm-0.2.0.tar.gz -
Subject digest:
b7d402b247ffa31283786c6c3d9727b67341300fd68c8033bcbc6d41d0f54ce7 - Sigstore transparency entry: 1754863430
- Sigstore integration time:
-
Permalink:
Danultimate/Agent-Ledger@237a3b683cae7b9771fdcef84f6a7db003453e1d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Danultimate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@237a3b683cae7b9771fdcef84f6a7db003453e1d -
Trigger Event:
release
-
Statement type:
File details
Details for the file agentledger_llm-0.2.0-py3-none-any.whl.
File metadata
- Download URL: agentledger_llm-0.2.0-py3-none-any.whl
- Upload date:
- Size: 30.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4103c9104667a412bf2d088cf227f3d1bff5729c4fcc55a06738bf414409863c
|
|
| MD5 |
746b26bc9d7d898b2875cd3c5cfcd980
|
|
| BLAKE2b-256 |
7e36f62dda9132240b39376dc9f2181cd878d3513cf5eb1670898fa4ee48a22c
|
Provenance
The following attestation bundles were made for agentledger_llm-0.2.0-py3-none-any.whl:
Publisher:
release.yml on Danultimate/Agent-Ledger
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentledger_llm-0.2.0-py3-none-any.whl -
Subject digest:
4103c9104667a412bf2d088cf227f3d1bff5729c4fcc55a06738bf414409863c - Sigstore transparency entry: 1754863435
- Sigstore integration time:
-
Permalink:
Danultimate/Agent-Ledger@237a3b683cae7b9771fdcef84f6a7db003453e1d -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Danultimate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@237a3b683cae7b9771fdcef84f6a7db003453e1d -
Trigger Event:
release
-
Statement type: