A tamper-evident
record of what
your AI did
An HMAC-SHA256-chained, append-only audit log built for evidence. Change one byte and the chain breaks at that exact record — and a checkpoint you hand off catches even a keyholder rewrite.
Verify it. Then try to fool it.
A four-event log from a legal matter. Verify and the whole chain checks out. Tamper a byte and it breaks at the exact record. Rewrite the history with the key and plain verification is fooled — only the checkpoint catches it.
A single-byte edit is easy to catch. The hard case is an attacker who holds the key — that's the one the checkpoint is for.
Five things a log-to-a-file can't promise.
A plain log tells you what someone chose to write down. An HMAC-chained, checkpointed log tells you what actually happened — and proves it, byte for byte.
Cannot be silently edited
Every record is HMAC-chained to the one before it. Any edit by someone without the key breaks the chain — and the verifier reports the exact (segment, record_id) of the first violation, with a structured evidence payload an auditor can act on.
Can prove it wasn't rewritten
Chained HMACs verify the log against itself — which someone holding the key also satisfies after re-chaining fabricated history. checkpoint pins the current head; verify --checkpoint proves the log still extends the head you saw before.
Only works if the checkpoint is held by someone who can't rewrite the log. A copy stored beside the log proves nothing.
Survives crashes
Append-only, with an atomic flush + F_FULLFSYNC on macOS. A partial write never yields a half-record; a torn tail is detected and truncated with a structured RecoveryReport.
Travels across languages
A byte-for-byte documented on-disk format, with committed golden vectors that any conforming implementation must round-trip. Rust + Python ship today; the format is implementable in any language with HMAC-SHA256.
Is court-defensible
A paired threat model and court-defensibility brief; the CLI ships a bit-reproducible export --pdf command for self-contained evidence packages.
The brief is engineering's read; legal-team sign-off is pending (pre-GA).
One frozen format, documented byte by byte.
Not a database, not a blockchain — a single-writer, append-only file format. Each record is canonical CBOR and carries the HMAC of the record before it, so the whole log is one continuous chain.
- Chain
- HMAC-SHA256 (FIPS 198-1), each record bound to prev_hash
- Encoding
- Canonical CBOR (RFC 8949 §4.2)
- Integrity
- CRC32 header · BLAKE3-256 key fingerprint
- Compare
- constant-time (subtle)
- Genesis
- first record's HMAC bound to the segment header
- Layout
- append-only, per-segment files
The on-disk format is frozen at v0.1 ↗ (spec + committed golden vectors). The library is v0.3.0 — a separate version line.
Anywhere an action has to be provable later.
Any system that takes consequential actions — a legal workflow, an autonomous agent, a compliance pipeline — needs a record that a motivated insider can't quietly rewrite.
A defensible record of a matter
Every file open, model call, and disclosure on a legal matter, chained and checkpointed — an evidence trail that stands up when the matter is contested.
What the agent actually did
Tool calls, approvals, and side effects recorded append-only. When an agent misbehaves, the log shows exactly what happened — and can't be quietly cleaned up after.
Continuous, tamper-evident events
Access grants, approvals, and policy decisions streamed to an append-only log. Auditors verify the whole chain, or spot-check a single segment.
Who opened what, and when
Unlock, read, and lock events for a secrets vault — the sibling record to the vault itself, provable independent of whoever administered it.
Append, anchor, prove.
Five steps from a live event to a self-contained evidence package. The one that makes it defensible is the checkpoint — the moment you hand the current head to someone who can't rewrite the log.
appendAppend a record
Each event is written append-only and HMAC-chained to the previous record's hash — single-writer, microsecond-latency.
flushFlush to disk
Atomic flush + F_FULLFSYNC (macOS). A crash can never leave a half-record; a torn tail is detected and truncated.
checkpointHand off the head
Pin the current (segment, record_id, hmac) and give it to a party who can't rewrite the log — a customer, a regulator, a counterpart.
verify --checkpointVerify
Walk the chain to confirm nothing was edited — and confirm the log still extends the head you checkpointed.
export --pdfPackage the evidence
Emit a bit-reproducible, self-contained evidence package for handoff to counsel or a regulator.
append → flush → checkpoint → verify --checkpoint → export --pdf// A checkpoint stored beside the log proves nothing — whoever rewrites one rewrites the other. Security comes from where you hand it.
One KeyHandle trait, three places to keep the key.
The HMAC key is the root of trust. Where it lives is a deployment choice — swap the key source without touching the log format. Same trait, from a unit test to a KMS.
In-memory
A raw key held in process. Zero setup — for tests, CI, and reproducible golden vectors.
- tests / CI
- golden vectors
- no OS dependency
OS keychain
The signing key lives in the OS keychain — macOS Keychain, Linux Secret Service, Windows Credential Manager. For desktop.
- macOS / Linux / Windows
- key never in your code
- desktop apps
KMS
The key stays in a managed KMS and never leaves it; signing happens there. For servers and multi-tenant deployments.
- AWS KMS (v0.1)
- GCP / Azure later
- server-side / multi-tenant
One matter, as written and tampered.
A four-event log from a legal matter. On the left, verification passes and prints the chain head. On the right, one byte is flipped inside the gpt-4o approval — and the verifier stops at that exact record.
The matter-2024-CV-3047 sample ships in the repo, with a clean log and a byte-identical tampered companion — run ogentic-audit verify against either.
Rust core, Python bindings, one CLI.
The core is Rust (edition 2024, MSRV 1.85); abi3 wheels wrap it for Python 3.9+; the CLI drives it. Same frozen format, same golden vectors — byte for byte.
from ogentic_audit import Writer, Reader, KeyHandle, verify
key = KeyHandle.from_env("OGENTIC_AUDIT_KEY_HEX") # 64 hex chars
with Writer.open("./audit-logs", key=key) as w:
w.append({"actor": "user:alice", "event": "vault.unlocked",
"payload": {"vault_id": "v-001"}})
for record in Reader.open("./audit-logs"):
print(record["record_id"], record["actor"], record["event"])
report = verify("./audit-logs", key=key)
assert report.okexport OGENTIC_AUDIT_KEY_HEX=$(openssl rand -hex 32) # exit 0 verified, 1 on the first violation ogentic-audit verify ./audit-logs # pin the head, then prove the log still extends it ogentic-audit checkpoint ./audit-logs --out head.json ogentic-audit verify ./audit-logs --checkpoint head.json --summary
use ogentic_audit_core::{InMemoryKey, RecordInput, Writer, Verifier};
let key = InMemoryKey::from_hex(env!("OGENTIC_AUDIT_KEY_HEX"))?;
let mut writer = Writer::open("./audit-logs", Box::new(key), session_id)?;
writer.append(RecordInput::new("user:alice", "vault.unlocked"))?;
let verifier = Verifier::new(Box::new(key));
let report = verifier.verify("./audit-logs")?;
assert!(report.ok());pip install ogentic-audit # Python cargo add ogentic-audit-core # Rust library cargo install ogentic-audit # CLI binary
Shield decides what's sensitive. Audit proves what happened.
Composable Apache-2.0 primitives for regulated AI. Shield is the input-side classifier; Audit is the evidence layer. Two are live today; the rest are on the way.
shield → route → audit · classify on the way in, prove on the way out. Audit is the evidence sibling to ogentic-shield ↗ — part of the OgenticAI ↗ OSS suite.
Install it. Verify in three lines.
Rust core, Python bindings, or the CLI — one Apache-2.0 library, one frozen format. No account, no telemetry.
cargo add ogentic-audit-core · cargo install ogentic-audit