Metadata-Version: 2.5
Name: arcaeon-ledger
Version: 0.2.1
Summary: Tamper-evident, hash-chained action log for AI agents. Prove what your agent did.
Project-URL: Homepage, https://arcaeon.io
Author: Arcaeon
License: MIT
License-File: LICENSE
Keywords: agents,ai,audit,hash-chain,mcp,provenance,tamper-evident
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# arcaeon-ledger

**Observability tools show you what your agent did. `arcaeon-ledger` lets you _prove_ it.**

Every record is hash-chained to the one before it. Edit a row, delete one, or
reorder history, and every later link breaks — `verify` names the exact line.
You own the record, and you can prove it wasn't altered. Zero dependencies, one
JSONL file, two verbs.

```
pip install arcaeon-ledger      # then:  from arcaeon_ledger import Ledger
```

```python
from arcaeon_ledger import Ledger

log = Ledger("agent.log.jsonl")
log.append({"tool": "web.search", "query": "weather in LA", "result_ok": True})
log.append({"tool": "payment", "amount": "49.00", "currency": "USD"})

log.verify()          # VerifyResult(ok=True, rows=2, chained=2, ...)
```

Tampering is caught, not hoped against:

```python
# someone edits row 1's amount in the file by hand...
log.verify()          # VerifyResult(ok=False, first_break="line 1: chain mismatch")
```

CLI (wire it into CI or a pre-ship gate — a tampered log exits nonzero):

```
python -m arcaeon_ledger.cli append agent.log.jsonl '{"tool":"search","ok":true}'
python -m arcaeon_ledger.cli verify agent.log.jsonl        # exit 0 = intact, 1 = broken
```

## Prove *who* acted, not just the order

A hash chain proves sequence integrity — it can't prove who wrote each entry or
whether they were allowed to. Attach an `authority` block to bind the actor and
their permission surface into the chained (tamper-evident) row:

```python
from arcaeon_ledger import Ledger, authority

log = Ledger("agent.log.jsonl")
log.append(
    {"tool": "payment", "amount": "49.00"},
    authority=authority(
        "agent://billing-7",
        capability_version="v3",              # what they were allowed to do
        tool_schema={"name": "payment", "args": ["amount"]},  # hashed, not just named
        time_source="ntp",                    # trust surface of the clock
    ),
)
```

Now the audit question sharpens from *"was this edited?"* to *"was this edited
**and** was the writer authorized?"* — editing the principal, capability, or
schema hash breaks the chain like any other tamper. This composes tamper-evidence
with permission-replay. (Shipped in response to community feedback on launch.)

## Why this exists

The loudest unmet pain for agent builders in 2026 is the reliability/audit gap:
an agent "completes" a task and the result is quietly wrong, and you can't
reconstruct — or prove — what actually happened. Observability platforms trace
runs; none give you a **tamper-evident, portable, ownable** record. Regulations
(EU AI Act Art. 12, tamper-evident AI decision records) are starting to require
exactly this. `arcaeon-ledger` is the smallest honest version: a cryptographically
chained action log you drop in, own, and verify.

## How the chain works

`chain = sha256(prev_chain + canonical_json(row_without_chain))[:32]`

Each row commits to the entire history before it. The first row chains from a
fixed `"genesis"` seed. Rows without a `chain` field are tolerated only before
the first chained row (so you can adopt it on an existing log); an unchained row
appearing *after* the chain begins is itself flagged. On a mismatch, verify
keeps going from the claimed value so it counts later damage honestly instead of
cascading one break into noise.

## What it proves — and the three things it doesn't

Being precise here is the product, not a disclaimer. A hash chain proves the
recorded bytes were not altered *in place* after writing: mid-file edit, delete,
and reorder all break it and `verify` names the row. It does **not** by itself
prove three other things:

**1. Truncation.** Lop off the most recent rows and what remains verifies clean —
no append-only chain catches this alone. Close it by publishing the head somewhere
outside your own control, on a cadence:

```python
pin = log.head().as_pin()
# -> "arcaeon-ledger head chain=9f3c… rows=204 as_of=2026-08-13T17:40:00Z"
# post `pin` to a git commit / public comment / notarization anchor.
# a reader compares a fresh head() against the last pin; a truncated or
# re-minted history disagrees. the MAX gap between pins is your security
# parameter, not the average — an attacker picks the gap.
```

**2. Truth.** The chain notarizes whatever was written — a tamper-evident record
of a hallucination is still a hallucination with a checksum. To make a row speak
about the world, hash a re-fetchable artefact (URL+bytes, a snapshot, tool stdout)
and store that digest in the row, so a third party can re-get it and compare.

**3. Authorship.** `authority()` (above) records who-claimed-what, but it is data
in the row, not a signature — a rewriter who re-mints from genesis re-mints it too.
External head-anchoring (#1) is the thing a re-minter cannot advance.

Scoped honestly, the primitive is *"this file was not rewritten in place"* — small,
true, and testable. The layers above (external anchoring via `head()`, artefact
binding, signed authorship) are how you extend it toward a full evidence claim.

## Drop it into any MCP agent

`arcaeon-ledger` ships a zero-dependency MCP server, so any MCP client (Claude Code,
etc.) can give its agent tamper-evident logging with no code. Wire it in:

```json
{
  "mcpServers": {
    "ledger": {
      "command": "python",
      "args": ["-m", "arcaeon_ledger.mcp_server", "--log", "agent.log.jsonl"]
    }
  }
}
```

The agent then has two tools: `ledger_append(record)` to log an action
(returns its chain hash) and `ledger_verify()` to prove the whole log is
intact (or get the exact tampered line back). MCP is JSON-RPC over stdio and
this server speaks it directly — no SDK, no extra install.

## Status

Core library, CLI, and a drop-in **MCP server**, all tested: the library
against edit / delete / reorder tampering (`test_ledger.py`), the MCP server
through a full initialize → tools/list → append → verify handshake including
tamper detection over the wire. Extracted from a hash-chained action ledger
running in production. External anchoring ships now via `head()` (publish the pin
yourself); a hosted collection tier (retention, an automatic witness, compliance
export) is the next layer.

MIT.
