Skip to main content

langgraph-ledger

Full, tamper-evident traceability for LangGraph agents — a port of DeepSeek Harness (dsh)'s traceability design: content-addressed labels on every tool call, a hash-chained append-only event ledger, the execution DAG, replay, crash recovery, and first-class fork/rollback.

English · 中文

Why

LangGraph already checkpoints state and can time-travel. What it does not give you is an audit-grade record: were these events edited after the fact? Which tool call exactly — same name, or same content? What did the run look like as a graph, and can I fork from any point of it?

This plugin ports the traceability design of DeepSeek Harness (dsh) — append-only session log as the single source of truth, format versioning, fork lineage — onto LangGraph's checkpointer contract, and adds what neither has out of the box: content hashing.

The combination is the point:

Capability Mechanism
Tool-call identity content-addressed label tl_<hash> — same (name, input) ⇒ same label
Tamper evidence every event hash-chained to its predecessor (merkle-style)
Execution structure DAG over chain + call/result + checkpoint-parent edges
Rollback time_travel_config (native fork) and fork_thread (dsh-style seeded fork with lineage event)
Failure analysis error timeline, exact-repeat loop detection (falls out of the labels)
Verification verify_log re-checks the chain; verify_thread re-hashes stored checkpoints against logged claims

Differentiation (as of 2026-08)

Honest snapshot of the neighborhood — see POSITIONING for details:

Project Hash chain Execution DAG State rollback/fork LangGraph-native
langgraph-ledger ✅ (checkpointer drop-in)
LangSmith / Langfuse / AgentOps ✗ (hosted observability) trace view SDK
CONTINUUM crash recovery focus ✗ (MCP server)
burnout / VeritasAgent / memtrail partial
langgraph checkpointers (redis/mysql/…) parent links only time-travel only

Nobody else ships hash-labels + DAG + rollback as one LangGraph-native unit. That is the slot this project occupies.

Install

pip install langgraph-ledger

Quickstart

from langgraph.checkpoint.memory import InMemorySaver
from langgraph_ledger import TracingCheckpointSaver, DshTraceCallbackHandler

saver = TracingCheckpointSaver(InMemorySaver(), trace_root="./traces")
graph = builder.compile(checkpointer=saver)          # your graph, unchanged

graph.invoke(input, config={
    "configurable": {"thread_id": "run-42"},
    "callbacks": [DshTraceCallbackHandler()],         # LLM/tool/node events
})

Every run now leaves ./traces/run-42.jsonl — one hash-chained JSON event per line:

{"v":0,"seq":7,"ts":"…","kind":"tool/call","payload":{"label":"tl_9f2e…","name":"search",},"id":"…","prev":"…"}
{"v":0,"seq":8,"ts":"…","kind":"state/snapshot","payload":{"checkpoint_id":"…","label":"cp_2c01…","parent_checkpoint_id":"…","checkpoint_sha256":"…"},"id":"…","prev":"…"}

Rollback & fork

from langgraph_ledger import time_travel_config, fork_thread

# resume/fork from any recorded checkpoint (LangGraph-native time travel)
cfg = time_travel_config("run-42", checkpoint_id="<past-id>")
graph.update_state(cfg, {"count": 100})              # forks from that point

# dsh-style: a new thread seeded with the ancestry up to a checkpoint
new_tid = fork_thread(saver, "run-42", at_checkpoint_id="<past-id>")

Audit

python -m langgraph_ledger verify  traces/run-42.jsonl   # hash chain intact?
python -m langgraph_ledger analyze traces/run-42.jsonl   # errors, loops, timeline
python -m langgraph_ledger dag     traces/run-42.jsonl --mermaid
python -m langgraph_ledger repair  traces/               # close crash-orphaned runs
python -m langgraph_ledger replay  traces/run-42.jsonl   # rebuild the message timeline
from langgraph_ledger import verify_thread
report = verify_thread(saver, "traces/run-42.jsonl")  # stored state == logged claim?
assert report["ok"]

Crash recovery & replay

If the process dies mid-run, the log ends with an unclosed run/start. verify flags it as an open run; repair appends an honest run/end {status: "interrupted"} through the hash chain (idempotent, never rewrites history). replay_messages() rebuilds the conversation timeline from the log — digests by default, full text when the handler was created with record_full=True. For fail-closed operation (a run that cannot record must not proceed), use TraceRecorder(..., strict=True).

Design mapping from DeepSeek Harness

A design study, not a port of code (dsh is TypeScript/Node; this is Python/LangGraph):

dsh concept here
SessionEvent append-only log, single source of truth one hash-chained JSONL per thread
SESSION_FORMAT_VERSION + ignorable marker v field; unknown kinds counted, not rejected
turn / step hierarchy run/* / node/* events
parentSession + seedLength fork lineage fork event payload
sourceEventSeqs provenance DAG edges: chain, call→result, snapshot→parent
Model-Visible ⟺ Logged invariant payload digests (sha256 + head); strict=True enforces fail-closed recording
crash-orphaned turns closed as interrupted on reload repair — appends run/end {status: interrupted} through the hash chain
deriveMessages (log → conversation) replay_messages — digests or full text (record_full=True)
fail-closed invariants append-time strict JSON validation; verify_* refuse on mismatch

Deliberate deviations: prompt/response full text is not stored by default (digest only, opt-in via record_full=True); dsh's byte-level replay and compaction are out of scope.

What it is NOT

  • Not a hosted observability platform — the log is a local file you own.
  • Not byte-level replay of model streams — it is for error localization, audit and state rollback.
  • Rollback restores agent state; side effects your tools made in the world are yours to undo (pair tool calls with your own preimages if you need that).

Development

pip install -e ".[test]"
pytest tests/

License

MIT — see LICENSE.

Download files

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

Source Distribution

langgraph_ledger-0.2.0.tar.gz (33.3 kB view details)

Uploaded Source

Built Distribution

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

langgraph_ledger-0.2.0-py3-none-any.whl (31.8 kB view details)

Uploaded Python 3

File details

Details for the file langgraph_ledger-0.2.0.tar.gz.

File metadata

  • Download URL: langgraph_ledger-0.2.0.tar.gz
  • Upload date:
  • Size: 33.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for langgraph_ledger-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6448c26ac072b38b7e672000eb56ceefa8d8fcb2a0652501c0a546a2d844a454
MD5 38e4260e15d467e13cd1f05b6f25ddea
BLAKE2b-256 12ed966fbeb130f82728d43f2b7f83f7bbdd40401b1bcf4308353fb951579e1b

See more details on using hashes here.

Provenance

The following attestation bundles were made for langgraph_ledger-0.2.0.tar.gz:

Publisher: publish.yml on hty8870/langgraph-ledger

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file langgraph_ledger-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for langgraph_ledger-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ab16ff8499f9b53e2f980389ebae5b0eeb35929e70056fb851b13259f79d04c6
MD5 b7b80caa15afb38579615f0b10490b98
BLAKE2b-256 da0c99b6680daa9e6db32353ae44a9940893ed8e072ab52988a2cc666be99f54

See more details on using hashes here.

Provenance

The following attestation bundles were made for langgraph_ledger-0.2.0-py3-none-any.whl:

Publisher: publish.yml on hty8870/langgraph-ledger

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.0

2 files

0.2.1

2 files

This release

0.2.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page