Ed25519-signed, independently verifiable receipts for DeerFlow's memory, MCP config, and tool calls.
Three self-contained Python modules that add cryptographic integrity to DeerFlow's control plane. Each produces receipts conforming to IETF Internet-Draft draft-farley-acta-signed-receipts-01.
Why receipts? Authentication prevents unauthorized access. Receipt signing prevents undetected tampering — even by authorized parties. These address the integrity gaps exposed in DeerFlow #1646 (MCP config disclosure) and #1648 (memory disclosure).
# Clone and enter
cd examples/deerflow-receipts
# Install signing (optional — works unsigned without it)
pip install pynacl
# Run the end-to-end demo
python demo.py
# Verify receipts offline
python verify.py example_receipts.jsonl --verboseFollowing the DeerFlow discussion feedback, the repo now treats cryptographic integrity as separate evidence objects rather than one generic receipt feature.
schemas/memory-receipt.schema.json- memory item content and metadata integrityschemas/mcp-config-receipt.schema.json- authorized MCP config state and revocationschemas/delegation-receipt.schema.json- parent/user authorization for sub-agent workschemas/subagent-completion-receipt.schema.json- sub-agent output bound to a delegationNEGATIVE-TEST-MATRIX.md- adversarial acceptance criteria for the RFCtests/negative_cases.py- runnable stdlib-only harness for the five negative cases
Run the negative acceptance tests:
python tests/negative_cases.pyThe tests cover replay after revocation, metadata-only memory tampering, middle-chain deletion, cross-run receipt reuse, and signing-key rotation with historical verification.
Signs DeerFlow's memory.json writes with hash-chain receipts. Each receipt includes:
- SHA-256 hash of the memory state (content + file)
- Hash chain link to previous state
- Fact count and metadata
- Ed25519 signature
from memory_integrity import MemoryIntegrity, SigningConfig
mi = MemoryIntegrity(
memory_path=".deer-flow/memory.json",
output_path="memory_receipts.jsonl",
signing_config=SigningConfig(
private_key_hex="...",
public_key_hex="...",
kid="deerflow-prod-01",
issuer="deerflow-memory-integrity",
),
)
# Sign current state (call after each memory update)
receipt = mi.sign_current_state()
# Verify current state hasn't been tampered with
result = mi.verify_current_state()
assert result["status"] == "verified"
# Watch mode: continuously sign memory changes
# python memory_integrity.py watchUse case: Prove a memory entry hasn't been modified since it was written. An admin or auditor can verify the hash chain offline to detect any tampering — even by the server operator.
Signs MCP configuration changes with receipts that record which servers were enabled/disabled and who authorized the change.
from mcp_config_signer import MCPConfigSigner, add_mcp_signing_middleware
# Standalone
signer = MCPConfigSigner(config_path="extensions_config.json")
receipt = signer.sign_current_config(
authorization_context="admin_cli",
change_source="User enabled brave-search via settings",
)
# As FastAPI middleware (wraps DeerFlow's Gateway API)
from fastapi import FastAPI
app = FastAPI()
add_mcp_signing_middleware(app, signer)
# Now every PUT to /api/mcp/config produces a signed receiptEach receipt includes:
- Config hash (SHA-256)
- Which MCP servers are enabled/disabled
- Authorization context (who/what approved this config)
- Ed25519 signature
Use case: Prove the MCP configuration was authorized. When an auditor asks "was this MCP server enabled by an admin or by a compromised sub-agent?", the receipt chain provides the answer.
LangGraph/LangChain callback handler that signs tool calls and sub-agent completions.
from receipt_middleware import ReceiptMiddleware, SigningConfig
middleware = ReceiptMiddleware(
output_path="receipts.jsonl",
policy={"bash": "deny"}, # Block shell access
agent_id="deerflow-lead-agent",
signing_config=SigningConfig(...),
)
# Use with any LangGraph/LangChain agent
agent = create_react_agent(llm, tools, callbacks=[middleware])
result = agent.invoke({"input": "..."})
# Every tool call now has a signed receipt
print(middleware.get_stats())Receipt types:
deerflow:tool_call— tool invocations (allow, deny, error)deerflow:subagent_completion— sub-agent attestation
Features:
- Receipt chaining via
parent_receipt_id(Section 4.3 of IETF draft) - Policy enforcement at
on_tool_startboundary - Input/output hashing (not raw content — privacy preserving)
- Duration tracking
- Graceful degradation without langchain-core
Use case: Prove which sub-agent produced which output, and that no tool call was added or removed from the record after the fact.
Zero-dependency verifier for receipt files.
# Structural + hash chain verification (no PyNaCl needed)
python verify.py receipts.jsonl
# Full Ed25519 signature verification
pip install pynacl
python verify.py receipts.jsonl --verbose
# Also works with the npm verifier
npx @veritasacta/verify receipts.jsonlExposes receipt verification as an MCP tool so any agent in a DeerFlow workflow can verify another agent's receipts before trusting its output.
# Run as a standalone MCP server (stdio)
python verify_mcp_tool.pyConfigure in your MCP client (DeerFlow, Claude Desktop, etc.):
{
"mcpServers": {
"deerflow-verify": {
"command": "python",
"args": ["verify_mcp_tool.py"],
"type": "stdio"
}
}
}Or register with an existing MCP server:
from mcp.server import Server
from verify_mcp_tool import register_verify_tools
server = Server("my-server")
register_verify_tools(server)Three tools are exposed:
verify_receipt- Verify a single receipt's integrity and signatureverify_chain- Verify an ordered array of receipts for hash continuityverify_receipt_file- Verify all receipts in a JSONL file on disk
This closes the cross-agent trust loop: agent A calls verify_receipt to validate agent B's output receipt before accepting it as input.
# Start DeerFlow normally, then in a separate terminal:
python memory_integrity.py watchThis monitors .deer-flow/memory.json and signs every change. No changes to DeerFlow code required.
Add to DeerFlow's Gateway API startup:
# In backend gateway startup
from mcp_config_signer import MCPConfigSigner, add_mcp_signing_middleware
signer = MCPConfigSigner()
add_mcp_signing_middleware(app, signer)Add to DeerFlow's agent config:
# In agent initialization
from receipt_middleware import ReceiptMiddleware
middleware = ReceiptMiddleware(agent_id="deerflow-lead-agent")
# Add to LangGraph callbacksAll receipts conform to IETF Internet-Draft draft-farley-acta-signed-receipts-01:
{
"type": "deerflow:memory_write",
"receipt_id": "a1b2c3d4e5f6",
"content_hash": "sha256:abc123...",
"previous_hash": "sha256:def456...",
"timestamp": 1712789400.0,
"facts_count": 42,
"spec": "draft-farley-acta-signed-receipts-01",
"issuer_certification": "self-signed",
"signature": "ed25519:...",
"public_key": "ed25519:..."
}| Dependency | Required? | Purpose |
|---|---|---|
| Python 3.12+ | ✓ | Runtime |
| PyNaCl | Optional | Ed25519 signing + verification |
| langchain-core | Optional | LangGraph callback integration |
| starlette | Optional | FastAPI middleware integration |
| mcp | Optional | MCP server SDK for verify tool |
Without PyNaCl, receipts are emitted unsigned (structural integrity + hash chains only). Without langchain-core, the middleware module still exports classes but uses stub base classes. Without the mcp package, verify_mcp_tool.py falls back to a manual JSON-RPC stdio server.
- DeerFlow RFC #2099 — Cryptographic integrity for persistence layer and MCP config
- protect-mcp — Receipt signing for MCP tool calls (Node.js)
- LangChain receipts — Receipt signing for LangChain agents
- IETF draft-farley-acta-signed-receipts-01 — Receipt format specification
MIT