Skip to main content

bellbook (Python)

Python bindings for Bellbook: validate tamper-evident, replay-verifiable records of agent activity offline, from Python.

This is a thin PyO3 wrapper over the Rust bellbook crate, which stays the single source of truth for canonicalization, verification, and the conformance vectors. It is not a reimplementation. (The independent from-scratch validator under conformance/python/ in the repository is a separate thing: a deliberate second implementation that exists to cross-check the specification.)

Status

Validation (with the bellbook-core-v1 baseline profile check), reading, writing, and the read-side query set (issue #13, RFC-0002, RFC-0003).

Validate

validate(bytes) -> Report reaches the same Clean / Tainted / Invalid decision as the bellbook validate CLI, over the same core.

import bellbook

report = bellbook.validate(open("receipt.json", "rb").read())

print(report.status)          # "clean" | "tainted" | "invalid"
print(report.spec_version)    # "0.3"
print(report.record_count)
print(report.head_hash)       # lowercase hex; compare against an anchored head
print(report.rules_hash)      # compare against rules you trust
print(report.retracted, report.tainted)
print(report.standing)        # {"compromised": [...], "unsound": [...], "restorations": {...}}
print(report)                 # the full human-readable report (same as the CLI)

Validation never raises for a bad receipt: an unparseable or failing receipt returns a Report with status == "invalid" and a problem or reason set. As with the CLI, Clean is relative to the rules embedded in the receipt - compare rules_hash against a rule set you trust, or name the shared baseline:

report = bellbook.validate(data, require_profile="bellbook-core-v1")
p = report.profiles[0]
print(p["status"])            # "Conformant" | "NonConformant" | "Unknown"
print(p["hash"])              # hex of the profile's clause-table hash
for c in p["clauses"]:        # [{"id": "B1", "passed": True, "detail": "..."}, ...]
    print(c["id"], c["passed"], c["detail"])

require_profile takes one id or a list; results land in report.profiles in request order, and an unknown id is reported as "Unknown", not raised. A profile result is a report alongside the verdict: it never changes status or reason. The profile itself is documented in docs/profiles/bellbook-core-v1.md.

Read

read(bytes) -> Receipt parses a receipt for inspection. Reading does not verify - call validate for the decision.

import json

receipt = bellbook.read(open("receipt.json", "rb").read())

print(receipt.spec_version, len(receipt))
for r in receipt.records:
    print(r.kind, r.time, r.author_id, r.author_type, r.evidence)
    for ref in r.refs:
        print("  ", ref["type"], "->", ref["target"])
    payload = json.loads(r.payload_json)

A Record exposes id, kind, time, author_id, author_type, signed, evidence, schema, refs, and payload_json. The enum-valued fields (kind, author_type, evidence, and each ref's type) are the record's Rust variant names, e.g. "Candidate", "Provider", "Reported", "Use". read raises ValueError on bytes that are not a parseable receipt.

Write

Writer(log_dir, rules) records evolution to a persistent, single-writer log. It holds the same exclusive lock and runs the same replay-on-commit the Rust LogWriter does. rules is a JSON string: the verifier rules the log is committed under, the same object a receipt embeds under rules.

default_rules(authors, max_context=200, admins=None, reaffirmers=None) builds that string for you - the Python counterpart to bellbook rules init - so you never hand-author a rules object. authors maps an actor id to a role (user, provider, system, executor, or verifier, case-insensitive). admins lists actors allowed to retract records they did not author; reaffirmers, when given, restricts reaffirming selections to the listed actors. Both must also appear in authors. Like rules init, the result carries the bellbook-core-v1 baseline evidence thresholds, so a log committed under it conforms to the baseline profile out of the box:

import bellbook

rules_json = bellbook.default_rules({"agent": "provider", "evaluator": "provider"})
w = bellbook.Writer("./mylog", rules_json)

c0 = w.candidate(author="agent", git_tree="a1b2...")            # a Root candidate
e0 = w.evaluate(author="agent", candidate=c0.id, criterion="builds", passed=True)
s0 = w.select(author="agent", objective="ship it",
              consider=[c0.id], choose=[c0.id], uses_eval=[e0.id])

print(c0.id, c0.accepted, c0.reason)   # each commit returns a Commit

# Export and verify in the same process:
report = bellbook.validate(w.receipt())
assert report.status == "clean"

Each of candidate, evaluate, select, and retract commits one record and returns a Commit (id, accepted, result, reason). A record is durably committed whether accepted or rejected - a rejected record is evidence a proposal was refused - so accepted may be False without an exception. Statically-knowable payload violations (an unregistered author, a score scale above 12, an upgrade whose tree differs from its target) raise ValueError before anything is written.

  • candidate(author, git_tree, *, git_commit=None, algo="sha1", note=None, continues=None, parent=None, derives_from=None, upgrades=None, manifest=None)
    • basis is exactly one of continues (with parent), derives_from (a list of record ids), or upgrades; omit all three for a Root. manifest (a directory path) binds the source by a canonical manifest hash instead of a reported tree. A derives_from member may be a candidate or an evaluation: a repair motivated by an evaluation names it there alongside the candidate it derives from (derives_from=[sound_parent.id, failing_eval.id]), and because Cause carries intent, not taint, retracting that evaluation later does not taint the repair.
  • evaluate(author, candidate, criterion, *, passed=False, failed=False, score=None, scale=None, procedure=None, uses=None) - exactly one of passed, failed, or a score (with scale, a decimal exponent 0-12).
  • select(author, objective, consider, *, choose=None, uses_eval=None, none=False, replaces=None, rationale=None) - exactly one of choose (with uses_eval) or none=True; replaces reaffirms a prior selection. consider, choose, and uses_eval are lists of record ids.

Retract

retract(author, target, reason) asserts a committed record's content is wrong. The target stays in the log; its id enters the retracted set, its epistemic dependents become tainted, and the receipt reports Tainted from then on - permanently. A later reaffirming selection (one that replaces the unsound one on surviving evidence) restores the line's standing, but never turns the receipt Clean again: the episode is part of history, and that is the point.

r = w.retract(author="evaluator", target=e0.id,
              reason="benchmark harness measured the wrong thing")
assert r.accepted
assert bellbook.validate(w.receipt()).status == "tainted"

Retraction is ownership-bound (SPEC section 2): it is accepted only when author is the target's author, or is listed in the rules' admin_retraction_actors (set via default_rules(..., admins=[...])). An Executor may never author a retraction, so Executor-authored records are retractable only through an admin actor. A Verdict or a Retraction cannot be retracted. As with the other verbs, a rejected retraction is still durably committed with accepted == False and the verifier's reason.

The writer is deliberately single-writer (SPEC 5.1): it holds an exclusive lock for the log directory, so a second Writer on the same directory raises. Other useful members: w.head (current head, hex), w.records (the committed records, as Records), len(w), and w.receipt() (portable receipt bytes).

Query

The RFC-0002 named query set - seven deterministic, read-only questions over lineage, evidence, and standing - is available as methods on both Writer (over the live log) and the Receipt returned by read (over an exported receipt). Both return plain dicts/lists in the exact surface JSON shapes the Rust core and the bellbook query CLI emit, so answers are diffable across surfaces.

w.selected("best-of-n")   # selections under that exact objective, with
                          # chosen candidates and their evidence
w.descent(c.id)           # the line of descent back to its roots
w.descendants(c.id)       # everything downstream, in log order
w.siblings(c.id)          # the candidate's generation
w.frontier()              # unconsidered candidates + winners not continued
w.standing(s.id)          # standing, taint, retraction, restorations
w.evidence(c.id)          # what a selection or a whole line rests on

r = bellbook.read(w.receipt())
assert r.frontier() == w.frontier()   # same answers over the receipt

Queries answer only over verified state: a log or receipt that does not verify raises ValueError instead of answering, as do a missing or rejected id and a kind mismatch. Nothing is ranked and nothing is silently filtered - every reported node carries its standing, taint, and retraction annotations, and the reader decides.

Build from source

pip install maturin
maturin develop            # build and install into the current venv
pytest bindings/python/tests

Prebuilt wheels (Linux, macOS, Windows) are published to PyPI, so pip install bellbook needs no Rust toolchain; the steps above are for local development against the working tree.

Licensed under MIT OR Apache-2.0.

Metadata

Release files for bellbook 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bellbook 0.7.0
File Size Uploaded
bellbook-0.7.0.tar.gz 30.3 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for bellbook 0.7.0
File Interpreter ABI Platform
bellbook-0.7.0-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
bellbook-0.7.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details
bellbook-0.7.0-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details

Total release size: 2.6 MB

Release files / bellbook-0.7.0.tar.gz

Download URL bellbook-0.7.0.tar.gz
Size 30.3 kB
Tags Source
SHA-256 checksum
How to use checksums
09e0c2d1598303724e63cadc2b0072dfe6264d20ca8e62c0a074c579ad02ef09
BLAKE2b-256 checksum
How to use checksums
c6e3b819eca1d01562f714e29dd8426df26ea3a8fa740a25ee5563ceb3d82086
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release files / bellbook-0.7.0-cp39-abi3-win_amd64.whl

Download URL bellbook-0.7.0-cp39-abi3-win_amd64.whl
Size 763.1 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
701390edffc4b16663125392c12e94b8f322e4c32aea69ce4e7a5de8768091cf
BLAKE2b-256 checksum
How to use checksums
1d8cb96c8f0b59636c565fdd6e47f59a99b9780b11a520b7c3dfffd3c14743f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release files / bellbook-0.7.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL bellbook-0.7.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 947.1 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
0208c6cbe25d64b9a9d2584f537a83c1b6739a80c4f62ee3f0bd081fb833672c
BLAKE2b-256 checksum
How to use checksums
8882f907b733a72208e3fb577dcae4fce30a98ad63bde06bfde52e942ee642fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release files / bellbook-0.7.0-cp39-abi3-macosx_11_0_arm64.whl

Download URL bellbook-0.7.0-cp39-abi3-macosx_11_0_arm64.whl
Size 844.3 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
9a8305ec66b1fe26b0951e8b2140cfd239a3751a6676e0b69f22dc43fe19bd10
BLAKE2b-256 checksum
How to use checksums
373f4dfdd9df1d4c9ca480f820e218c5347f1723c111a80db2f215db5e7c416a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

4 release files

0.8.0

4 release files

This release

0.7.0 This release

4 release files

0.6.0

4 release files

0.5.0

4 release files

0.4.0

4 release files

0.3.0

4 release 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