Tamra is a tamper-evident evidence ledger for AI agents. It seals every LLM call, tool call, and human approval into a cryptographic hash chain you can hand to an auditor — who can verify it offline, without trusting you.
Self-hosted on your own Postgres. Signed with your own keys. No cloud, no API key, no per-event cost.
The problem
A row in a database is mutable by design. Anyone with write access — an engineer, a script, an attacker — can change what your agent "did" after the fact, and leave no trace. So when it matters most, your logs are just claims.
They say your agent promised a refund. You say it didn't. Both of you are pointing at a table that either side could have edited.
EU AI Act Article 12 requires automatic logging over a high-risk system's lifetime. Exporting a CSV proves you have data, not that it's unaltered.
You need to know exactly which tool the agent called, with what arguments, and whether a human approved it — and you need that answer to hold up later.
The fix isn't better logging — it's a log that can't lie. Here's how Tamra builds one.
How Tamra works
Three moving parts, and you only ever touch the first one.
A Spring AI advisor records every LLM_CALL and TOOL_CALL automatically — no changes to your agent code. Human sign-offs go in through the HumanApprovalRecorder API.
Each event is canonicalised (RFC 8785), hashed with SHA-256, and linked to the hash of the event before it. Every hour, the chain head is signed with an Ed25519 key that never leaves your infrastructure.
Export a date range as a signed .tamrapatra bundle: events, checkpoints, your public key, compliance mappings, and an HTML report. Your auditor verifies it on their own laptop.
By default Tamra stores hashes only — not your raw prompts or tool arguments. You can prove an event happened and hasn't changed without the ledger ever holding sensitive content. Turn on full-payload when you need the text itself.
See it catch a tamper
This is the part that matters. Someone with full database access changes one event — exactly the scenario ordinary logs can't survive. Here's what happens next.
$ tamra verify ✓ Chain VALID events: 500 head sequence: 499 head hash: 834b6373a932008ea91524fd928b88348f5c96fd4dcf9935bbb7bea76b9a5119 # tamper with event #250 as a Postgres superuser — past the append-only trigger $ tamra verify ✗ Chain BROKEN at sequence 250 reason: content hash mismatch: the event has been modified since it was sealed
No diff tool, no backup to compare against — the ledger detects its own wound and names the exact sequence where it happened. To hide the edit, a tamperer would have to recompute every hash from 250 onward and forge an Ed25519 signature with a key that never left your infrastructure.
verify exits 0 when the chain is clean and 1 on a break — so you can wire it into CI or a cron job and get paged the moment a record changes.
Why it's called Tamra
When an ancient Indian king granted land, the record wasn't written on palm leaf — it was engraved into copper plates called tamrapatras (tamra = copper, patra = document).
Copper was chosen for one reason: you cannot quietly change it. It doesn't rot, and re-engraving leaves scars anyone can see. Plates were often riveted together under a seal, so no page could be added or removed. If a claim was ever challenged, the holder produced the plate.
A thousand years on — long after the issuing kingdom ceased to exist — those plates are still legible, and still carry the evidence of whether anyone tampered with them. The kingdom's authority expired. The record's didn't.
That's the whole idea behind Tamra: evidence that outlives the trust you placed in whoever produced it. We just swapped the copper for a hash chain.
.tamrapatra file.
Quickstart
If you're on Spring Boot 3.4 and Spring AI 1.0, capture starts the moment the app boots.
<dependency> <groupId>dev.tamra</groupId> <artifactId>tamra-spring-boot-starter</artifactId> <version>0.1.2</version> </dependency>
tamra: keystore: path: .tamra/keystore # your Ed25519 signing key capture: full-payload: false # hash-only by default — keeps prompts out of the ledger checkpoint: interval: 1h
# the CLI ships as a signed jar on the GitHub release — grab it, then alias for readability $ curl -LO https://github.com/tamra-dev/tamra-agent-ledger/releases/latest/download/tamra.jar $ alias tamra='java -jar tamra.jar' # 1. generate the signing key you control $ tamra keygen --keystore .tamra/keystore # 2. run your agent as usual — events are captured automatically # 3. export a signed evidence bundle for a date range $ tamra export --from 2026-06-01 --to 2026-07-01 --edict eu-ai-act \ --out my-evidence.tamrapatra --keystore .tamra/keystore ✓ Wrote my-evidence.tamrapatra events: 500 (sequence 0 to 499) edict: eu-ai-act v0.1 signed: checkpoint over the segment head (Ed25519) # 4. anyone can verify it offline — no database, no Tamra server $ tamra verify --bundle my-evidence.tamrapatra ✓ Signatures verified (Ed25519) edict: eu-ai-act v0.1 (EU AI Act) signer key: 2095…d69a # compare against the operator's key out of band ✓ Chain VALID — 500 events, head sequence 499
Scope
v0.1.2 is an early open release, and the MVP is deliberately narrow: one framework, one database, one regulation. Here's exactly where the edges are.
| Area | Today | Status |
|---|---|---|
| Frameworks | Spring Boot 3.4.x + Spring AI 1.0.x | Shipped |
| Storage | PostgreSQL 15+ (append-only, Flyway) | Shipped |
| Compliance mapping | EU AI Act | Shipped |
| Signatures | Ed25519, keys you hold | Shipped |
| Other frameworks | LangChain4j, Python, MCP gateway | Planned |
| Other standards | ISO 42001, SOC 2, DPDP | Planned |
| Streaming capture | ChatClient.stream(), tool loops | Planned |
| Post-quantum | ML-DSA signatures | Planned |
| Dashboard / ClickHouse | UI and alternate backend | Planned |
Tamra is fully open source and self-hosted. There is no account to create, no key to request, and nothing phones home.
A copper plate for your agent.