Skip to main content

merkle-audit

Tamper-evident audit logging for AI agent tool calls.

Every action your agent takes — web searches, file writes, API calls, payments — is committed to an append-only hash chain. If any historical entry is modified, all subsequent entries become cryptographically invalid. You get a verifiable record of exactly what your agent did and in what order, with no external infrastructure required.

from merkle_audit import log_event

entry = log_event(
    "tool_executed",
    {"tool": "web_search", "query": "current ETH price"},
    actor="research-agent-1",
)
print(entry.leaf_hash)   # sha256 of (prev_root + action + payload + timestamp)
print(entry.mmr_root)    # running root — commits to the entire history

Why this matters for AI agents

When an agent takes actions in the world — spending money, sending messages, modifying files — you want a record that:

  1. Can't be quietly edited. If an agent (or anyone with DB access) modifies a past entry, the hash chain breaks from that point forward.
  2. Is ordered. The chain encodes sequence: entry N depends on entry N-1's root.
  3. Is lightweight. No external services, no consensus protocol. Just SHA-256 and a list.
  4. Persists across restarts. Attach SQLite persistence with one line.

Installation

pip install merkle-audit

No required dependencies — pure Python stdlib.

Optional: SQLite persistence is included. FastAPI integration is available as an extra:

pip install merkle-audit[fastapi]

Usage

Basic

from merkle_audit import AuditChain

chain = AuditChain()

e1 = chain.append("tool_executed", {"tool": "read_file", "path": "/etc/hosts"})
e2 = chain.append("tool_executed", {"tool": "write_file", "path": "/tmp/out.txt"})
e3 = chain.append("approval_requested", {"reason": "about to send email"})

print(f"{chain.leaf_count()} entries, root: {chain.current_root()[:16]}...")

Persist to SQLite

from merkle_audit import get_chain
from merkle_audit.persister import install_sqlite_persister

# Call once at startup — all subsequent log_event() calls are persisted
install_sqlite_persister("/var/lib/myapp/audit.db")

from merkle_audit import log_event
log_event("payment_sent", {"to": "0xabc...", "amount_usd": 50.00}, actor="billing-agent")

Verify an entry

from merkle_audit import AuditChain

chain = AuditChain()
e0 = chain.append("first_action", {"data": "hello"})
e1 = chain.append("second_action", {"data": "world"})

genesis = "0" * 64
assert chain.verify_entry(e0, prev_root=genesis)
assert chain.verify_entry(e1, prev_root=e0.mmr_root)

# Simulate tampering
from dataclasses import replace
tampered = replace(e0, action_type="something_else")
assert not chain.verify_entry(tampered, prev_root=genesis)  # False — detected

FastAPI endpoint

from fastapi import FastAPI
from merkle_audit.fastapi import router

app = FastAPI()
app.include_router(router, prefix="/audit")
# GET /audit/chain → {"leaf_count": 42, "current_root": "a3f9..."}

How it works

Each entry commits to everything that came before it:

leaf_hash = SHA256(prev_root + ":" + action_type + ":" + payload_hash + ":" + timestamp)
new_root  = SHA256(prev_root + ":" + leaf_hash)

The mmr_root after N entries is a single 32-byte value that cryptographically summarises the entire history. To verify entry K, you only need its stored leaf_hash and the mmr_root from the entry immediately before it.

This is a simplified Merkle Mountain Range: append-only, no peak merging, optimised for sequential audit logging rather than inclusion proofs in large trees.

ChainEntry fields

Field Type Description
leaf_index int Sequential position (0-based)
leaf_hash str SHA-256 hex — tamper-evident commitment
mmr_root str SHA-256 hex — running root over all entries
payload_hash str SHA-256 hex of the serialised payload
action_type str Caller-defined event label
actor str Who performed the action
created_at float Unix timestamp

Running tests

pip install merkle-audit[dev]
pytest

License

MIT

Metadata

Release files for merkle-audit 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for merkle-audit 0.1.0
File Size Uploaded Provenance Actions
merkle_audit-0.1.0.tar.gz 9.3 kB Without provenance Details for merkle_audit-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for merkle-audit 0.1.0
File Interpreter ABI Platform Actions
merkle_audit-0.1.0-py3-none-any.whl Python 3 none any Details for merkle_audit-0.1.0-py3-none-any.whl

Total release size: 18.3 kB

Release files / merkle_audit-0.1.0.tar.gz

Download URL merkle_audit-0.1.0.tar.gz
Size 9.3 kB
Tags Source
SHA-256 checksum
How to use checksums
e5e33df2323799e860bccc674a799ad6ab8ad9acfcaf3ad4a6483082bcbf67b8
BLAKE2b-256 checksum
How to use checksums
1aa59c083446984cbbabb63ae9ba6ce0a1cfd878378f0c6cc9f4775c04eaaaca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via Hatch/1.16.5 cpython/3.12.3 HTTPX/0.28.1

Release files / merkle_audit-0.1.0-py3-none-any.whl

Download URL merkle_audit-0.1.0-py3-none-any.whl
Size 9.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a637b5693ac6be8535ccb22843db64178566879cd314472739e696b09ef0dfce
BLAKE2b-256 checksum
How to use checksums
6c135e47abb636adab6ab5e9f122d96c30fa28fab04e85798ec748bc56c3708e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via Hatch/1.16.5 cpython/3.12.3 HTTPX/0.28.1

Security for merkle-audit 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page