Per-repo documentation — each repo's docs/ and README, ingested and associated with the repo. Rendered through the <<<RepoDocs>>> tag.
← chain docs · README.md
A local, tamper-evident spine for Conductor. Where telemetry.db is fast-and-local and notes_db is durable-and-shell-readable, the ledger is immutable-and-ordered: once a meaningful Conductor moment is anchored it cannot be silently altered or removed, and the whole history is a single hash-chained log you can verify with one call.
It runs on a local Anvil node (Foundry) — no real network, no gas cost, no keys to protect. It is purely a local content-addressed + ordered attestation layer, the on-chain twin of the signals jazz already produces.
./ledger.sh setup && ./ledger.sh deploy # stand it all up
./ledger.sh up # anvil+build+test+deploy+selftest
./ledger.sh mcp # start the MCP server
---
The Conductor data-plane already content-addresses every volume snapshot (volctl serialize → sha256 + IPFS CID) and the control-plane already signals every dispatch (agent_status) and every confirmation (auth_gate). Those records are mutable (sqlite, notes_db). The ledger adds the one property they lack: immutability + global ordering. After the fact you can prove:
- these exact bytes existed and were attested at block N (provenance), - this dispatch session opened, ran, and ended with this outcome (sessions), - this human approved this scoped decision, at this time (gates),
and that none of it was edited after the fact — the hash chain breaks if any entry is changed.
---
┌──────────────────────────────────────────┐
data-plane │ ~/code/chain (Foundry) │
volctl serialize ─┐ │ │
(sha256 + CID) │ │ Provenance.sol ── content anchors │
│ │ Sessions.sol ── dispatch lifecycle │
control-plane ├──▶│ ConductorLedger ── hash-chained log + │
coordinator │ │ registry (knows the │
worker.spawn/ │ │ other two addresses)│
worker.finish │ │ │
│ │ local Anvil :8545 │
auth_gate │ └──────────────────────────────────────────┘
answer_gate ──────┘ ▲ ▲
│ │
jazz_telemetry.ledger ───────┘ │ web3.py, fire-and-forget
(anchor_*, read_*, verify) │ degrades to a telemetry row
│
ledger_mcp (FastMCP) ────────────────────┘ thin client over the lib
src/)| Contract | Anchors | Key | |---|---|---| | Provenance.sol | volctl artifacts: sha256, IPFS cid, volume, mode, size | bytes32 sha256 (append-only set; duplicate sha rejected) | | Sessions.sol | coordinator dispatch: task, model, agent, outcome | bytes32 session id; open() once → close() once | | ConductorLedger.sol | the spine: a hash-chained append-only log + a registry of the other two contracts; also where auth-gate decisions land | seq (each entry links prevHash→entryHash) |
ConductorLedger is the single address every consumer needs: it exposes provenance() and sessions() so the rest is discoverable, and verifyChain() walks the log and returns the first broken link (or a sentinel if intact).
jazz_telemetry/ledger/)Lives inside jazz_telemetry so every module that already does from jazz_telemetry import telemetry gets the ledger for free.
- client.py — lazy, cached web3 connection; ABI loading (prefers the Foundry out/ build, falls back to bundled ledger/abi/*.json); zero-config address discovery from chain/deployments/<chainid>.json. - anchor.py — the fire-and-forget write path. Every anchor drops a local telemetry row first, then tries the chain; any failure (no web3, node down, revert) is swallowed and recorded as a *.fail telemetry row. Never blocks, never raises — exactly like agent_status / auth_gate. - read.py — the read / verification path. These do raise LedgerError on an explicit problem (a reader that silently returns empty hides failures).
Public API (re-exported from jazz_telemetry):
from jazz_telemetry import (
anchor_provenance, record_session_open, record_session_close,
anchor_gate, log_entry, # writes (fire-and-forget)
is_anchored, get_artifact, get_session, # reads
ledger_count, ledger_entry, recent_entries, verify_chain,
chain_status, get_addresses, # status / discovery
)
~/code/ledger_mcp/)A thin FastMCP server — same shape as notes_db_mcp (thin client, returns JSON strings). Tools: status, addresses, anchor_provenance, is_anchored, get_artifact, record_session_open, record_session_close, get_session, anchor_gate, log, log_count, recent, log_entry, verify_chain.
Start it: ./ledger.sh mcp (or python -m ledger_mcp with JAZZ_LEDGER_* env).
---
All three are fire-and-forget and individually killable — none can break the host operation, and each respects an env switch.
1. volctl → provenance. volctl/core.py::serialize() calls _anchor_provenance_safe() right after it emits the volume.serialize telemetry row, anchoring the same (sha256, cid). Disable: VOLCTL_LEDGER=0.
2. coordinator → sessions. coordinator/dispatch.py calls _ledger_session_open() at the worker.spawn moment and _ledger_session_close() at worker.finish, keyed by <pkg>:<token.id>. Disable: COORDINATOR_LEDGER=0.
3. auth_gate → gate decisions. auth_gate.py::answer_gate() anchors the resolved decision (gate id, type, agent, response) on the ledger log the moment a human answers. Disable: JAZZ_GATE_LEDGER=0.
Because the lib degrades to a telemetry row when the chain is absent, these hooks are safe to ship before the chain is even deployed — they simply no-op until ./ledger.sh deploy has run.
---
| Var | Default | Meaning | |---|---|---| | JAZZ_LEDGER | 1 | 0 makes all anchors no-ops | | JAZZ_LEDGER_RPC | http://127.0.0.1:8545 | Anvil RPC | | JAZZ_LEDGER_KEY | Anvil acct #0 | signing key (local dev only) | | JAZZ_LEDGER_DIR | ~/code/chain | where deployments/<id>.json lives | | JAZZ_LEDGER_ADDRESSES | (discovery) | explicit address-book path override |
The default key is Anvil's well-known deterministic account #0 — it is a public test key, only ever used against the local node. Do not point this at a real network.
---
ledger.sh commands| Command | Does | |---|---| | setup | install Foundry, vendor forge-std, pip install web3 into .venv | | anvil | start a background Anvil node on :8545 (pid+log under run/) | | build | forge build | | test | forge test -vv (Solidity unit tests in test/Ledger.t.sol) | | deploy | start Anvil, deploy all three contracts, write the address book | | mcp | run the ledger_mcp FastMCP server (stdio) | | selftest | live Python E2E: anchor → read → verify the hash chain | | status | Anvil + deployment + chain_status() | | stop | stop the background Anvil node | | up | anvil + build + test + deploy + selftest (everything) |
./ledger.sh setup # one-time: foundry + forge-std + web3
./ledger.sh deploy # starts anvil, deploys, writes deployments/31337.json
./ledger.sh selftest # anchors live, reads back, verifies the chain → PASSED
./ledger.sh test # the Solidity unit tests
forge test proves the contracts in isolation; selftest proves the whole Python → web3 → chain → read-back path against the deployed node.