# The ledger

Every effect in a project, an event received, a key created, an email sent, a decision taken, writes one entry to the project's ledger in the same transaction as the effect itself.

## An entry

```json
{
  "id": "led_…",
  "chain_id": "prj_…",
  "seq": 128,
  "ts": "2026-10-07T09:13:02.551Z",
  "actor": { "key_id": "key_…", "agent": "claude-code", "platform": "claude-code", "human": null },
  "op": "email.queued",
  "subject": "msg_…",
  "input_digest": "sha256:…",
  "outcome": "ok",
  "policy": { "rule": "#2", "effect": "require_approval", "version": 3, "request_id": "req_…" },
  "units": { "events": 0, "emails": 0, "approvals": 0 },
  "prev_hash": "sha256:…",
  "hash": "sha256:…"
}
```

When the email leaves, an `email.sent` entry on the same `msg_…` subject counts `emails: 1`.

The ledger holds ids and digests only: never email addresses, tokens or payloads in clear. `actor` says who acted: the key, the agent and platform it declared (with `session_hello` or the `X-Brynth-Agent` and `X-Brynth-Platform` headers), and the person, for decisions.

## Query

```text
ledger_query({subject: "evt_…"})
ledger_query({agent: "codex", since: "2026-10-01T00:00:00Z", limit: 100})
```

Filters: `op`, `agent`, `platform`, `subject`, `since`, `until`. Pages of 50 by default, up to 500. From the terminal: `npx brynth logs --limit 20`.

## Verify

Each entry carries the hash of the previous one, so changing or removing an entry breaks the chain from that point on.

```text
ledger_verify()
```

returns `{"ok": true, "checked": …, "last_seq": …, "last_hash": …}`, or `{"ok": false, "checked": …, "first_bad_seq": …, "reason": …}` with the first entry that does not match; `reason` is `seq_gap`, `prev_hash_mismatch`, `hash_mismatch` or `head_mismatch`. `from_seq` and `to_seq` check a range.
