Ed25519 receipt signing plugin for Google ADK agents. Every tool call produces a cryptographic receipt following the IETF Internet-Draft format.
pip install protect-mcp-adkfrom google.adk import Agent
from google.adk.tools import FunctionTool
from protect_mcp_adk import ReceiptPlugin, ReceiptSigner
# Generate signing keys (or load from file)
signer = ReceiptSigner.generate()
# Add the plugin to your agent
agent = Agent(
model="gemini-2.0-flash",
tools=[FunctionTool(my_tool)],
plugins=[ReceiptPlugin(signer, auto_export_path="receipts.jsonl")],
)
# After execution, verify the receipt chain:
# npx @veritasacta/verify@0.2.5 receipts.jsonl --key <public-key-hex>Every tool call your ADK agent makes produces a signed receipt:
{
"payload": {
"type": "protectmcp:decision",
"spec": "draft-farley-acta-signed-receipts-01",
"tool_name": "search",
"tool_input_hash": "sha256:ff7e27...",
"decision": "allow",
"output_hash": "sha256:a3f8c9...",
"issued_at": "2026-04-06T21:30:00.000Z",
"session_id": "sess_a1b2c3d4e5f6",
"sequence": 1,
"previousReceiptHash": null,
"agent_name": "research_agent",
"invocation_id": "inv_xyz"
},
"signature": {
"alg": "EdDSA",
"kid": "sb:adk:de073ae64e43",
"sig": "3da3162da83e..."
}
}Privacy-preserving: Tool inputs and outputs are SHA-256 hashed, not stored raw. The receipt proves what happened without exposing what was said.
Chain-linked: Each receipt includes previousReceiptHash, creating a tamper-evident chain. If any receipt is modified or removed, the chain breaks.
Offline verifiable: Receipts verify without contacting any server:
npx @veritasacta/verify@0.2.5 receipts.jsonl --key <public-key-hex>
# Exit 0 = valid, 1 = invalid (tampered), 2 = error (malformed)# Generate a new keypair
signer = ReceiptSigner.generate()
# Load from a key file
signer = ReceiptSigner.from_key_file("keys/agent.json")
# Save keys for reuse
signer.save_key("keys/agent.json")
# Access public key (for verification)
print(signer.public_key_hex)
print(signer.kid)plugin = ReceiptPlugin(
signer,
auto_export_path="receipts.jsonl", # Auto-export as receipts are produced
log_receipts=True, # Log each receipt to Python logger
)
# After execution
print(plugin.receipt_count)
plugin.export_receipts("audit-bundle.jsonl")
print(plugin.get_verification_command())| Callback | When | What it signs |
|---|---|---|
after_tool_callback |
After every tool execution | Tool name, input hash, output hash, decision |
on_tool_error_callback |
On tool execution error | Tool name, input hash, error reason |
before_tool_callback |
Before tool execution | Override for policy evaluation (Cedar) |
Receipts produced by this plugin verify against the same tooling as:
- protect-mcp (TypeScript/MCP)
- Agent Passport System (TypeScript)
- Any implementation following the IETF draft envelope format
Three independent implementations have been verified to produce interoperable receipts.
This plugin uses Ed25519 (RFC 8032) for signing. The receipt envelope's signature.alg field supports algorithm negotiation. For environments requiring post-quantum signatures (NIST FIPS 204), the same envelope format works with ML-DSA-65 by changing the alg field. See the IETF draft for algorithm agility details.
- IETF Draft: Signed Receipts
- IETF Draft: Knowledge Units
- protect-mcp (TypeScript, MCP/Claude Code)
- ScopeBlind
- Veritas Acta