Metadata-Version: 2.4
Name: incident-ledger
Version: 1.0.0
Summary: An append-only, hash-chained SQLite evidence log for LLM systems, with deterministic replay.
Project-URL: Homepage, https://github.com/MaqAnquor/incident-ledger
Project-URL: Repository, https://github.com/MaqAnquor/incident-ledger
Author: Abhijeet Verma
License: MIT
License-File: LICENSE
Keywords: agentic-ai,ai-accountability,audit-log,deterministic-replay,hash-chain,provenance,sqlite,tamper-evident
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# incident-ledger

An append-only, hash-chained SQLite **evidence log** for LLM systems — with deterministic replay. Pure Python standard library, no dependencies, runs anywhere.

When an AI system takes an action (issues a refund, moves money, files a ticket), "we have logs" is not the same as having *evidence*. `incident-ledger` makes an inference log into evidence by enforcing two properties in code, not prose:

- **Contemporaneity (P2)** — each row is written *at inference time*, stamped with the wall-clock moment it happened. The only mutation is `append`: there is no `update` and no `delete`.
- **Tamper-evidence** — each row's `entry_hash` is a SHA-256 over its content *and the previous row's hash*. Altering or deleting any earlier row — even by editing the SQLite file directly — breaks the chain, and `verify()` reports the exact `seq` where it broke.
- **Reproducibility (P3)** — a sealed decision can be reconstructed into a replay bundle (pinned model version, seed, exact context) and rerun, with an honest boundary: exact match at temperature 0, or a *pre-registered* variance tolerance above it, and an explicit `not-replayable` verdict when no seed was captured.

This is the minimal reference implementation accompanying the paper *"Implementing Agentic-AI Accountability: A Minimal, From-Scratch Reference Stack Mapped to the IMDA MGF, EU AI Act, NIST, and ISO/IEC 42001"* — see [CITATION.cff](CITATION.cff). It implements the logging / audit-trail control common to those frameworks. It is deliberately small: every line is meant to be read.

## Install

```bash
pip install incident-ledger
```

Or from source:

```bash
git clone https://github.com/MaqAnquor/incident-ledger
cd incident-ledger
pip install -e .
```

Requires Python ≥ 3.10. No third-party dependencies.

## Quickstart

```python
from incident_ledger import IncidentLedger

# Open on a file for a durable ledger (":memory:" for tests).
ledger = IncidentLedger("evidence.db")

entry = ledger.append(
    query="Is order #9871244 refundable?",
    answer="Yes — $487 refund issued.",
    confidence=0.91,
    model_version="refund-clf-v3@sha-9c1a2f",   # pin the exact model; never "latest"
    context_snapshot="Policy R-12: refunds within 30 days of delivery.",
    sources=("policy://refunds/R-12", "order://9871244"),
    disclosed_limits=("partial-refund case untested",),
    downstream_action="refund.issue",
    trace={"sampling": {"seed": 482991, "temperature": 0.0}},
)

# The chain is intact...
assert ledger.verify().ok

# ...and tampering is detectable, even done directly against the file.
import sqlite3
raw = sqlite3.connect("evidence.db")
raw.execute("UPDATE ledger SET answer = 'No refund' WHERE seq = 1")
raw.commit(); raw.close()

result = IncidentLedger("evidence.db").verify()
assert not result.ok
print(result.broken_seq, result.reason)   # 1  "altered content at seq 1: seal does not recompute"
```

### Replay a sealed decision

```python
from incident_ledger import replay_from_ledger

# Reconstruct the bundle from a sealed row and rerun it. The attempt itself
# is sealed back into the ledger as a new contemporaneous entry.
outcome = replay_from_ledger(ledger, seq=1)
print(outcome.mode, outcome.reproduced)   # "deterministic" True
```

A row sealed with no captured seed is reported `not-replayable` rather than silently treated as deterministic — a gap you disclose, not one you paper over.

## API

| Symbol | Purpose |
|---|---|
| `IncidentLedger(path=":memory:", *, now=time.time)` | The store. `append(...)`, `verify()`, `get(seq)`, `entries()`, `head_hash`, `len()`. |
| `LedgerEntry` | One immutable sealed row; `payload()`, `recompute_hash()`. |
| `VerificationResult` | Verdict of `verify()`: `ok`, `entries_checked`, `broken_seq`, `reason`. |
| `ReplayBundle` | Everything needed to rerun one decision: model version, seed, context, temperature, prompt. |
| `replay(bundle, ...)` / `replay_from_ledger(ledger, seq, ...)` | Rerun a bundle, or reconstruct one from a sealed row and rerun it. |
| `ReplayResult` | `mode` (`deterministic` \| `bounded-variance` \| `pin-mismatch` \| `not-replayable`), `reproduced`, `agreement`. |

## Measured overhead

Tamper-evidence is cheap. On a modern laptop (file-backed SQLite, one synchronous commit per append), over 1,000 appends:

| Metric | Value |
|---|---|
| Mean append latency | ~0.47 ms (p95 ~0.60 ms) |
| Storage per 1,000 inferences | ~4.0 MB (payload-dominated) |
| Full-chain `verify()` over 1,000 rows | ~31 ms |
| Hash-chain overhead vs. a plain-insert baseline | under 0.1 ms per append (within run-to-run variance) |

The hash chain adds far less than the SQLite commit already costs. Regenerate the numbers on your own machine:

```bash
python benchmarks/ledger_overhead.py --n 1000 --reps 3
```

Figures are measured on a single machine, single thread, one payload shape and one durability setting; they do not extrapolate to production write-concurrency or a different retention policy.

## Tests

```bash
pip install pytest
pytest -q
```

## Citing

If you use this software, please cite both the software and the paper — see [CITATION.cff](CITATION.cff).

- **Software:** [`10.5281/zenodo.21387489`](https://doi.org/10.5281/zenodo.21387489)
- **Paper:** [`10.5281/zenodo.21387487`](https://doi.org/10.5281/zenodo.21387487) — *Implementing Agentic-AI Accountability: A Minimal, From-Scratch Reference Stack Mapped to the IMDA MGF, EU AI Act, NIST, and ISO/IEC 42001*

Both DOIs are **reserved and not yet resolvable** — they begin resolving when the Zenodo records are published. If a link above 404s, the deposit has not landed yet.

## License

MIT — see [LICENSE](LICENSE).
