Skip to main content

Local-first, tamper-evident evidence layer for AI agent activity — hash-chained capture sealed into an RFC 6962 Merkle commitment.

Project description

attestroot

Local-first, tamper-evident evidence for AI agent activity.

Captures what an AI agent did, hash-chains it, seals it into an RFC 6962 Merkle commitment, and produces a bundle anyone can verify — on a clean machine, with no dependencies, without our software and without contacting us.

Status: pre-release. M0 and M1 are functionally complete and pass their exit tests. Nothing is published to PyPI yet.


What it claims, and what it does not

It attests that a record existed at a given time and has not been altered since.

It does not prove the record is a truthful account of what the agent did. A party controlling its own capture could record something false, and it would seal and verify perfectly. Integrity and existence-in-time is a real, checkable property — it is simply not the same property as truth.

That distinction is enforced in code, not just in wording: every adapter declares its own blind spots, and evidence lacking independent ground truth is capped at a lower grade with the reason recorded.

Current state

Component Status
RFC 6962 Merkle tree ✅ verified against the spec, incl. a path-length guard most implementations omit
Ed25519 (RFC 8032) ✅ pure-Python, all official vectors pass; uses cryptography when present
Hash-chained ledger ✅ 0.47 ms/append, names the exact sequence number where a chain breaks
Capture hook ✅ fail-open, p99 0.68 ms, exits 0 on every hostile input tested
Adapter contract ✅ capability manifest drives grade ceilings mechanically
Claude Code adapter ✅ validated against a real 442-tool-call session
Seal / bundle / verify ✅ end-to-end, 9/9 tamper attacks detected
Conformance suite ✅ 8 cases, all passing — clean, resumed, compacted, crashed, gapped, concurrent, subagent, malformed
Grader (D0–D4) ✅ deterministic, 11/11 fixture bundles match the hand-computed rubric
Standalone verifier ✅ one file, no dependencies, output identical to the CLI
Countersignature ⬜ M3 — nothing is live

Exit test

The M0 gate was "seal a real 50-turn session; verify in a clean container."

session      real Claude Code transcript — 1661 lines, 442 tool calls, 1 compaction
captured     442 events @ 0.75 ms
bundle       2103 leaves, 447 members, 7.7 MB
gaps         0  (every ground-truth tool_use id matched a captured one)
clean room   13/13 checks pass — no dependencies, no signing key, no local state
tampering    9/9 attacks detected

scripts/clean_room_verify.sh runs it. Where Docker is unavailable it builds a hermetic room with python3 -I -S, which is stricter on the dependency question than a container: site-packages is unreachable, so cryptography cannot be imported even when installed, forcing the pure-Python signature path.

Try it

PYTHONPATH=src python3 -m attestroot.cli init
PYTHONPATH=src python3 -m attestroot.cli status
PYTHONPATH=src python3 -m attestroot.cli seal <session-id>
PYTHONPATH=src python3 -m attestroot.cli verify <bundle> --offline-bundle
PYTHONPATH=src python3 -m attestroot.cli grade <bundle>
PYTHONPATH=src python3 -m attestroot.cli conformance

Verifying without installing anything

dist/attestroot_verify.py is a single file with no dependencies. Email it alongside the bundle:

python3 attestroot_verify.py evidence.arbundle

It produces byte-identical output to attestroot verify, and prints its own SHA-256 at build time so a recipient can check the copy they were sent against the one published here.

This exists because "clone the repo and set PYTHONPATH" is already too much friction for someone who was emailed an evidence file and has thirty minutes. Evidence nobody checks is decoration.

Adapters

An adapter is a translator from a source's native events to canonical W Events. It declares what it can see in a capability manifest, and the grader acts on that declaration: an adapter reporting has_ground_truth: false caps the grade at D1 with the reason recorded, because completeness cannot be evaluated without an independent record to diff against.

Adding one means implementing the contract in adapters/base.py and submitting conformance fixtures with the change. An adapter is certified only when its suite passes — including a check that its declared capabilities match what its output can actually support. Claiming ground truth while producing none fails.

Design constraints

These are load-bearing, not preferences:

  • Fail-open capture. The hook never blocks or slows the agent. Every failure path writes a gap record and exits 0. A bug in evidence collection must not stop someone's work.
  • No network in the free path. Ever. Not for telemetry, not for time.
  • Store raw. Payloads are persisted verbatim and interpreted at read time. Tool schemas change; today's normalisation is tomorrow's lost evidence.
  • Zero dependencies for verification. An auditor must be able to verify on a machine where they cannot install anything.
  • Gaps are a feature. Missing captures are detected by diffing against the source's own record, never from self-reported gap records — otherwise hiding a gap is as easy as not writing one.

Built on

incident-ledger — the append-only hash-chained evidence log this extends to agent activity. pip install incident-ledger

Published standard

Tamper-evident logging, deterministic replay and evidence-maturity grading are cited prior art, not claims of this work. Hash chains and Merkle trees are 1979 and 1991 public cryptography.

Not supported

Windows. Paths, file locking and fsync semantics differ enough that half-support would be worse than none. WSL works.


MIT · attestroot.com · AVATROX TECHNOLOGY PRIVATE LIMITED

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

attestroot-0.1.0.tar.gz (57.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

attestroot-0.1.0-py3-none-any.whl (62.5 kB view details)

Uploaded Python 3

File details

Details for the file attestroot-0.1.0.tar.gz.

File metadata

  • Download URL: attestroot-0.1.0.tar.gz
  • Upload date:
  • Size: 57.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.9

File hashes

Hashes for attestroot-0.1.0.tar.gz
Algorithm Hash digest
SHA256 51b0f7e21c859f450f73ca02925e04499bb139ad47663753fcec4305d4880152
MD5 856fd64ba464e4171a3a961b6349bdb1
BLAKE2b-256 c97ac6a5d254fb82c8e9cad704ed82444b8888ab2f2197451f5f13e8db624e1c

See more details on using hashes here.

File details

Details for the file attestroot-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: attestroot-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 62.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.9

File hashes

Hashes for attestroot-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 90bdf51c2a8b814359cb8223937b938bf08f4ea5da92b6f24373297fdee438a4
MD5 95e34f6598393256624bb3b9080d5288
BLAKE2b-256 dbf0ef0a978bcd30500eb40a2601aa58f5040430b162b850007b6a8cab5570a7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page