Tamper-evident audit trail library using hash chains and optional HMAC signing
Project description
TrailProof
Tamper-evident audit trail for AI agents and multi-tenant applications.
Trailproof records events in a cryptographic hash chain. Every event commits to all events before it. Tamper anything — you break the chain. Verify any time.
Architecture
graph TD
App["Your Agentic App"] -->|"tp.emit({ type, actor, ... })"| TP
subgraph TP["Trailproof"]
EB["Event Builder<br/>validate & envelope"] --> HC["Hash Chain Engine<br/>prev_hash + SHA-256"]
HC --> Store["Store<br/>(append-only)"]
Store --> QE["Query Engine<br/>filter & paginate"]
Signer["Signer<br/>HMAC-SHA256<br/>(optional)"] -.-> HC
Verifier["Verifier<br/>chain integrity"] --> Store
end
Store --> Mem["Memory Store"]
Store --> JSONL["JSONL File Store"]
What It Does
- Hash chain integrity — SHA-256 chain links every event to the previous one
- Optional HMAC signing — proves events were created by the holder of a secret key
- Dual SDK — identical Python and TypeScript APIs with shared test vectors
- Two stores — in-memory (default) or append-only JSONL file
- Query and pagination — filter by event type, actor, tenant, time range, trace
Install
Python
pip install trailproof
# or
uv add trailproof
TypeScript / Node.js
npm install @kyberon/trailproof
Quickstart
Python
from trailproof import Trailproof
tp = Trailproof()
# Emit an event
event = tp.emit(
event_type="myapp.user.login",
actor_id="user-42",
tenant_id="acme-corp",
payload={"ip": "1.2.3.4", "method": "oauth"},
)
# Query events
result = tp.query(actor_id="user-42", limit=50)
print(result.events) # list[TrailEvent]
# Verify chain integrity
verification = tp.verify()
print(verification.intact) # True
print(verification.total) # 1
print(verification.broken) # []
TypeScript
import { Trailproof } from "@kyberon/trailproof";
const tp = new Trailproof();
// Emit an event
const event = tp.emit({
eventType: "myapp.user.login",
actorId: "user-42",
tenantId: "acme-corp",
payload: { ip: "1.2.3.4", method: "oauth" },
});
// Query events
const result = tp.query({ actorId: "user-42", limit: 50 });
// Verify chain integrity
const verification = tp.verify();
console.log(verification.intact); // true
Features
| Feature | Description |
|---|---|
| Hash chain | SHA-256 links each event to the previous; genesis hash is "0" x 64 |
| HMAC signing | Optional hmac-sha256:<hex> provenance signature |
| In-memory store | Default; events lost on exit; good for testing |
| JSONL file store | Append-only; survives restarts; human-readable with jq |
| Query | Filter by event type, actor, tenant, trace, session, time range |
| Pagination | Cursor-based; next_cursor for large result sets |
| Verify | Walk chain; returns broken: [index, ...] for tampered events |
| Get trace | All events for a trace_id, ordered by time |
| Zero deps | stdlib only (Python); Node.js built-ins only (TypeScript) |
API
Both SDKs expose the same five methods:
| Method | Python | TypeScript | Returns |
|---|---|---|---|
| Emit event | tp.emit(event_type, actor_id, ...) |
tp.emit({ eventType, actorId, ... }) |
TrailEvent |
| Query events | tp.query(event_type, actor_id, ...) |
tp.query({ eventType, actorId, ... }) |
QueryResult |
| Verify chain | tp.verify() |
tp.verify() |
VerifyResult |
| Get by trace | tp.get_trace(trace_id) |
tp.getTrace(traceId) |
TrailEvent[] |
| Flush to disk | tp.flush() |
tp.flush() |
void |
Configuration
Python
from trailproof import Trailproof
# In-memory (default)
tp = Trailproof()
# JSONL file persistence
tp = Trailproof(store="jsonl", path="events.jsonl")
# HMAC signing
tp = Trailproof(signing_key="your-secret")
# Default tenant applied to every event
tp = Trailproof(default_tenant_id="acme-corp")
# All options combined
tp = Trailproof(
store="jsonl",
path=".trailproof/events.jsonl",
signing_key="your-secret",
default_tenant_id="acme-corp",
)
TypeScript
import { Trailproof } from "@kyberon/trailproof";
// In-memory (default)
const tp = new Trailproof();
// JSONL file persistence
const tp = new Trailproof({ store: "jsonl", path: "events.jsonl" });
// HMAC signing
const tp = new Trailproof({ signingKey: "your-secret" });
// Default tenant
const tp = new Trailproof({ defaultTenantId: "acme-corp" });
Releasing
For maintainers only.
- Update version in
python/pyproject.toml - Update version in
typescript/package.json - Regenerate changelog:
git-cliff -o CHANGELOG.md - Commit:
git commit -m "chore: bump version to vX.Y.Z" - Tag:
git tag vX.Y.Z - Push:
git push origin main --tags - Approve the
releaseenvironment in GitHub Actions
Both publish-python.yml and publish-npm.yml trigger on v* tags and require manual approval.
Links
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 trailproof-0.1.0.tar.gz.
File metadata
- Download URL: trailproof-0.1.0.tar.gz
- Upload date:
- Size: 35.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dacedda015fe8321732c7759a3960c06d8668a98e0ee17b5e1a8718bd17eee36
|
|
| MD5 |
f77c0aca4c08a299b2dfa1a0f0dc9cf3
|
|
| BLAKE2b-256 |
d3e41cf4bd88e8ffe39b054fbe22dbf603d86e22060e2fd1caf51051e6b0a59b
|
Provenance
The following attestation bundles were made for trailproof-0.1.0.tar.gz:
Publisher:
publish-python.yml on KyberonAi/trailproof
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
trailproof-0.1.0.tar.gz -
Subject digest:
dacedda015fe8321732c7759a3960c06d8668a98e0ee17b5e1a8718bd17eee36 - Sigstore transparency entry: 978766524
- Sigstore integration time:
-
Permalink:
KyberonAi/trailproof@3247d08db7a5d122192f0e3c32e7524f8ca65a70 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/KyberonAi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-python.yml@3247d08db7a5d122192f0e3c32e7524f8ca65a70 -
Trigger Event:
push
-
Statement type:
File details
Details for the file trailproof-0.1.0-py3-none-any.whl.
File metadata
- Download URL: trailproof-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48a56f748262108f824f50be86f446078a21fb3125854575b5602391e2ee70df
|
|
| MD5 |
25c354c3097978366ae2dd303bc5ca4b
|
|
| BLAKE2b-256 |
946905589038f8532cebdd10f3cd1baaddf97977f48f3181559011136bc79c54
|
Provenance
The following attestation bundles were made for trailproof-0.1.0-py3-none-any.whl:
Publisher:
publish-python.yml on KyberonAi/trailproof
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
trailproof-0.1.0-py3-none-any.whl -
Subject digest:
48a56f748262108f824f50be86f446078a21fb3125854575b5602391e2ee70df - Sigstore transparency entry: 978766588
- Sigstore integration time:
-
Permalink:
KyberonAi/trailproof@3247d08db7a5d122192f0e3c32e7524f8ca65a70 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/KyberonAi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-python.yml@3247d08db7a5d122192f0e3c32e7524f8ca65a70 -
Trigger Event:
push
-
Statement type: