Skip to main content

A git-backed, append-only, machine-partitioned CloudEvents 1.0 event ledger (JSONL) — library, CLI, MCP server, and read-only web visualizer. Stdlib-only core.

Project description

evledger

A git-backed, append-only, machine-partitioned CloudEvents 1.0 event ledger (JSONL). Pure-stdlib core; optional CLI, MCP server, and a zero-build web visualizer.

  • Append-only, never compress — events are immutable; the ledger is built for long-lived storage and replay.
  • Machine-partitioned — events live at <root>/<machine>/<YYYY-MM>.jsonl, with a monotonic per-machine seq assigned at append under a file lock.
  • CloudEvents 1.0 envelope — every record is a valid CloudEvent (specversion/id/source/type/time + machine/seq); the data attribute is an open channel for arbitrary JSON.
  • Optional per-type schemas — register a data schema and validation is advisory (a slightly-off payload is still logged, never dropped).
  • Stdlib-only core — the library and the visualizer have zero runtime dependencies. The CLI adds click; the MCP server adds the mcp SDK; both are optional extras.

Install

pip install evledger                # core library (stdlib only) + the `evledger` CLI*
pip install "evledger[mcp]"         # + the MCP server (`evledger-mcp`)
pip install "evledger[dev]"         # + pytest / ruff / mypy for development

* the evledger console script needs click; pip install "evledger[cli]" pulls it explicitly (it's also included by [dev]).

Library

The core is pure functions over an immutable event stream — easy to test, no hidden I/O beyond the store.

from evledger import LedgerStore, Query, new_event, query_events
from evledger.stats import counts_by_type, pair_events

store = LedgerStore(root="./ledger")

# Append — `seq` is assigned per-machine, monotonic, under a lock.
store.append(new_event(
    source="/laptop/app", type="com.example.job.start",
    machine="laptop", time="2026-05-30T10:00:00Z", data={"job": "sync"},
))

events = store.read_all().events                       # ordered by (time, seq)
starts = query_events(events, Query(type="com.example.job.*"))   # glob match
counts = counts_by_type(events)                        # {type: n}
durations = pair_events(events).durations              # pair *.start with *.end

See examples/quickstart.py for a runnable end-to-end walkthrough (python examples/quickstart.py).

Layers

Module What it gives you
evledger.schema LedgerEvent, new_event(...), parse_time(...) — the CloudEvents envelope
evledger.store LedgerStore — append (locked, seq-assigning) + read_all() / iter_events()
evledger.query Query + query_events(...) — filter by source/type(glob)/machine/time window
evledger.stats counts_by_type, event_rate, pair_events (→ *.start/*.end durations)
evledger.registry TypeRegistry — register per-type data schemas, validate advisorily
evledger.digest deterministic rollup + rule-based anomaly flags over a window
evledger.transcript / evledger.reconstruct optional model-assisted audit layers (stdlib loader + injectable model client)

CLI

The evledger command exposes the ledger over a small group:

evledger log --source /laptop/app --type com.example.job.start \
  --machine laptop --data '{"job":"sync"}'

evledger show --type 'com.example.*' --since 2026-05-01 --limit 50
evledger stats --by type --pair          # counts + paired *.start/*.end durations
evledger digest --since 2026-05-01        # deterministic rollup + anomaly flags
evledger serve                            # read-only web visualizer (localhost)

The ledger root resolves via --ledger-root$CLAUDE_LEDGER_ROOT<cwd>/ledger.

MCP server

The same log / query / stats operations are exposed to any MCP client (Claude Desktop, other agents) over stdio. The MCP SDK is imported lazily, so the core and CLI stay dependency-free.

# zero-install via uvx:
uvx --from "evledger[mcp]" evledger-mcp --ledger-root ~/.local/share/evledger

# or installed:
pipx install "evledger[mcp]"
evledger-mcp --ledger-root ~/.local/share/evledger

The ledger root is server-side configuration (captured at launch via --ledger-root$CLAUDE_LEDGER_ROOT<cwd>/ledger), not a tool argument.

Web visualizer

evledger serve starts a read-only, zero-build single-page app (stdlib http.server, no framework) bound to localhost: a filter panel, a wheel-zoom / drag-pan canvas timeline laned by source or type, a zoomable flame graph of derived *.start/*.end spans, and a sortable event table. JSON APIs (/api/events, /api/spans, /api/meta) back it. It never writes the ledger.

Storage layout

<root>/
  <machine-a>/
    2026-05.jsonl     # one CloudEvent per line, append-only
    2026-06.jsonl
  <machine-b>/
    2026-05.jsonl

Each line is a complete CloudEvent. Files are never rewritten or compressed — roll-up/aggregation happens at read time, so the raw record is preserved for the life of the ledger.

Development

pip install -e ".[dev,mcp]"
ruff check .
mypy evledger
pytest -q

CI (GitHub Actions) runs ruff + mypy + pytest across Python 3.10–3.13.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

evledger-0.1.0.tar.gz (117.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

evledger-0.1.0-py3-none-any.whl (95.8 kB view details)

Uploaded Python 3

File details

Details for the file evledger-0.1.0.tar.gz.

File metadata

  • Download URL: evledger-0.1.0.tar.gz
  • Upload date:
  • Size: 117.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for evledger-0.1.0.tar.gz
Algorithm Hash digest
SHA256 efcfe48dabc77989cffb89d9de95ec4834485bf910848d2bca3339c3e077300a
MD5 d071267bcdd089b09a9cdfca992c0fed
BLAKE2b-256 ecf2a55a9e185e3f7d03b07f8fae76c3728da44eeafbf278091d19dbb765f47e

See more details on using hashes here.

File details

Details for the file evledger-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: evledger-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 95.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for evledger-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 237888422ef40f8bd9b82820972985222e691da71b37c379176c9e9da0b04bff
MD5 ae0dae799cd9d3e8e0a330923f77edb7
BLAKE2b-256 7b8b24132fb55c32348a5cfc6f97b55552402aa82497932090e45cb0841536f2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page