Bulla
Create, verify, and reconcile portable receipts for consequential agent actions.
Glyph defines a portable receipt for consequential agent actions. Bulla is the Python reference implementation. It creates and verifies ActionReceipts locally and computes coverage against a separately supplied action record.
Receipt verification detects changes in the records supplied to the verifier. Coverage reports actions in a supplied action record that have no matching receipt. These checks answer different questions and remain separate.
A bulla was the clay envelope sealed around a record so it could survive the absence of the parties who made it. Bulla applies that discipline to agent actions: the action may finish in milliseconds, but a retained receipt keeps its declared authority, evidence, limits, and challenge path available to the next system or institution.
The format is intended for the customer, auditor, dispute forum, or underwriter who arrives after the agent and its runtime are gone and applies its own checks.
Install
Bulla supports Python 3.10 and later. Core receipt creation and digest verification require no hosted service.
python -m pip install bulla
Verify one receipt
Download the constructed canonical payment receipt and verify it locally:
curl -fsSLo constructed-payment-authorization-v0.2.json \
https://glyphstandard.com/examples/payment-authorization-v0.2.json
bulla receipt verify constructed-payment-authorization-v0.2.json --format json
The constructed receipt records a USD 125.00 charge, declares a USD 200.00 limit, and carries an executable convention that recomputes conformance. The checked result is:
integrity VERIFIED
authenticity UNVERIFIED
authority UNAUTHENTICATED
declared_bounds CONFORMS
grounding SELF_ASSERTED
recourse NAMED
reachability UNVERIFIED
reliance_decision NOT_COMPUTED
The same receipt is available offline at
spec/vectors/payment-authorization.json. Its expected result is pinned in
spec/vectors/expected.json and recomputed in CI.
Change amount_minor from 12500 to 12501 without recomputing the hashes.
The verifier returns nonzero, reports a content-hash mismatch, and suppresses
content-dependent conclusions.
Retain the verification kit
The package carries the v0.2 specification, constructed vectors, expected dimensional verdicts, and a zero-dependency checker as one immutable archive:
bulla receipt kit --out action-receipt-v0.2-verification-kit.zip
Expected archive digest:
sha256:2ec524f78885c122fd9e6301c85d757e94cf0c468278d11f1414b19a3a3c313c
After extracting the archive, run python3 verify.py. The checker imports no
Bulla code and makes no network request. The manifest checks the retained
contents; authenticate the archive itself with the detached digest or the
signed release receipt.
Create one receipt
bulla receipt create \
--type demo.write \
--subject path=/tmp/example.txt \
--forum-endpoint https://example.invalid/challenge \
--forum-root fixture:independently-pinned-root \
--out receipt.json
bulla receipt verify receipt.json --format json
The unsigned result reaches the digest verification rung. It does not authenticate the authority or compute a reliance decision.
Check coverage
event_coverage compares valid receipts with an action record supplied outside
the receipt set:
from bulla.action_receipt import verify_receipt
from bulla.coverage import event_coverage
from bulla.wrap import receipt_for
receipt = receipt_for("network.egress", {"event_id": "action-001"})
assert verify_receipt(receipt).ok
complete = event_coverage([{"id": "action-001"}], [receipt])
assert complete["coverage"] == 1.0
assert complete["unreceipted_delta"] == []
with_gap = event_coverage(
[{"id": "action-001"}, {"id": "action-002"}],
[receipt],
)
assert with_gap["coverage"] == 0.5
assert with_gap["unreceipted_delta"] == ["action-002"]
Receipt integrity remains unchanged in the second comparison. The supplied action record contains one action with no matching receipt.
Where Bulla fits
- Payments: record authorization, amount bounds, evidence, and recourse.
- Permissions and writes: bind a consequential operation to its principal and policy.
- Gateways and provider handoffs: retain the action and authority references that crossed the boundary.
ActionReceipt v0.2 remains the normative and default format. ActionReceipt v0.4 is available as an opt-in experimental draft. Source-only research profiles remain inspectable on GitHub but are excluded from the installed package unless the distribution policy explicitly lists them as released.
Limits
- Receipt integrity does not establish the truth of every recorded field.
- Coverage is relative to the supplied action record.
- Bulla does not establish that the supplied action record is complete.
- Unsigned receipts remain unauthenticated.
- Reliance remains
NOT_COMPUTEDunless a reliance policy is supplied.
Multidimensional verification results reject Boolean coercion. Callers must inspect the named dimensions or apply an explicit reliance policy.
Documentation
- Verification-first quickstart
- Bulla documentation
- ActionReceipt standard
- Status and evidence
- Complete capability reference
- Source-only experimental research
- Changelog
- Security policy
License and security
Bulla is licensed under the Apache License 2.0. Report vulnerabilities privately through GitHub Security Advisories or the security policy.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bulla-0.45.1.tar.gz.
File metadata
- Download URL: bulla-0.45.1.tar.gz
- Upload date:
- Size: 5.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4cf6a3b906fef44ff9a319c56ab408b384a0082babfeb904a3e772c5bf5b45e3
|
|
| MD5 |
0a21ac79d940968be2e8e8615721a04a
|
|
| BLAKE2b-256 |
1ca3b95b8096f9ee189e6aa3d07100d6ad6daa47042e979834cbce5f6403d825
|
Provenance
The following attestation bundles were made for bulla-0.45.1.tar.gz:
Publisher:
publish.yml on jkomkov/bulla
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bulla-0.45.1.tar.gz -
Subject digest:
4cf6a3b906fef44ff9a319c56ab408b384a0082babfeb904a3e772c5bf5b45e3 - Sigstore transparency entry: 2335607245
- Sigstore integration time:
-
Permalink:
jkomkov/bulla@31471e0b23b6513dbee89aa9e94a22e53a3a3a9c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/jkomkov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@31471e0b23b6513dbee89aa9e94a22e53a3a3a9c -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file bulla-0.45.1-py3-none-any.whl.
File metadata
- Download URL: bulla-0.45.1-py3-none-any.whl
- Upload date:
- Size: 772.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f05d8f2c5ca6474b4bb7dd86dbf60d8cf9507bc74eb3968ee70640ce9248f8b
|
|
| MD5 |
ccb4ac1209b4986fbe26e99a289d110d
|
|
| BLAKE2b-256 |
ea6969ea08e33f38aeca6c4c46320b0bdfbab33c5811488a536c5b44b63de6d6
|
Provenance
The following attestation bundles were made for bulla-0.45.1-py3-none-any.whl:
Publisher:
publish.yml on jkomkov/bulla
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bulla-0.45.1-py3-none-any.whl -
Subject digest:
1f05d8f2c5ca6474b4bb7dd86dbf60d8cf9507bc74eb3968ee70640ce9248f8b - Sigstore transparency entry: 2335607258
- Sigstore integration time:
-
Permalink:
jkomkov/bulla@31471e0b23b6513dbee89aa9e94a22e53a3a3a9c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/jkomkov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@31471e0b23b6513dbee89aa9e94a22e53a3a3a9c -
Trigger Event:
workflow_dispatch
-
Statement type: