Sanning Proof
Offline verification for the Sanning evidence plane. If someone handed you an evidence pack and asked you to check it, this package is the whole answer: no account, no API key, no network call, and no relationship with Sanning. Sanning is never in the trust path.
Two kernels, one contract. This page covers the Python one.
- Python:
sanning-proofon PyPI. A library, with no command of its own. - TypeScript:
@sanning/proofon npm, browser and Node ≥ 20. Ships theproofcommand.
If instead you are producing evidence, the write SDK is
sanning-anchor and the hosted
plane is console.sanning.io. For what the
evidence plane is and how anchoring, reading and verifying fit together, see
the Sanning documentation.
Install
pip install sanning-proof
Check a pack you were handed
A pack is one zip holding bundle.json beside a logs/ directory. Unzip it
and check it from your terminal:
sanning-proof verify pack/bundle.json --logs pack/logs
Exit codes are the contract, so a script can read them: 0 verified, 1
failed, 2 malformed or usage, 3 undetermined. 3 is deliberately not
1: undetermined means a check could not be run, and reporting that as failed
would say the evidence is bad when it is not.
Two things leave a run undetermined. An unreachable gateway leaves the on-chain
half unanswered, which is the network being down rather than the evidence being
bad. And a record nobody hashed leaves the PACK incomplete: the headline is
the verdict on the pack, the signed bundle plus whatever disclosed bytes
travelled beside it, which is the same verdict the verify page prints. A pack
can be incomplete while the bundle it carries is impeccable, so both verdicts
are printed whenever they differ, along with every reason the pack is not
verified.
Add gateway URLs as a second argument to check the on-chain half as well; omit
them for a fully offline verification, which needs no account, no API key and
no network. Only arweave witnesses are re-fetched. A witness of any other
kind is not checked, and with gateways passed the run answers exit 3.
The same command exists in the TypeScript kernel as
npx @sanning/proof verify. A cross-kernel leg runs both over the same
bundles and compares their exit codes, including the --logs lanes, so a
verdict cannot drift between them unnoticed. Their printed output is not
identical: the TypeScript command reports more per-event detail.
Or from Python, if you would rather have the result as an object than as an exit code:
import json
import pathlib
from sanning_proof import verify_evidence_bundle
pack = pathlib.Path("pack")
bundle = json.loads((pack / "bundle.json").read_text())
content = {p.stem: p.read_bytes() for p in (pack / "logs").glob("*.json")}
result = verify_evidence_bundle(bundle, content=content)
print(result.status) # "verified"
print(sum(1 for e in result.events if e.ok), "verified")
content= binds each disclosed raw log to the commitment inside its own signed
record. Omit it and every event's content check is undetermined rather than
failed: absence of a disclosure is not evidence of tampering.
Nothing leaves your machine. The kernel does no I/O of its own.
What a verdict claims
A verdict is recomputed from the artifact, never read off it. A bundle whose own asserted verdict disagrees with the recomputed one is reported as the recomputed one.
A verified verdict is offline, and it does not mean the chain was
consulted. A bundle names the witness that holds its bytes; asking that
witness whether it does is a separate, online act.
What a verifier can prove, and what it must not claim, is specified in
specs/evidence-bundle.md §5.3.4, which ships inside this package, under
"The standard". It draws four boundaries, covering
completeness, the chain, identity and per-agent slices. Read it before you
write down what a pack proved. A verifier that overclaims is worth less than no
verifier. The plain reading is at
what a verdict means.
The rest of the family
To check one signed envelope on its own, rather than a whole bundle:
from sanning_proof import verify_envelope
result = verify_envelope(envelope)
result.ok # spec_version accepted + payload binding + Ed25519 signature
Three arguments cover the rest:
verify_envelope(envelope, expected_content_hash=...)binds an artifact you already hold to the provenance an envelope commits to, and reports which role it matched.verify_envelope(envelope, payload_bytes=...)checks an external-commitment envelope against the committed bytes.verify_proof_bundle(bundle)proves a leaf event was in a signed checkpoint.
The TypeScript kernel has parity, including the RFC 9162 Merkle primitives.
Sign an envelope
Most producers never call this kernel to sign: the write SDK, the agent daemon
and the MLflow plugin all sign through it, and reaching for
sanning-anchor is the shorter
path. For a custom producer:
from sanning_proof import canonical_json, sha256_hex, sign_envelope, signing_key_from_seed_hex
key = signing_key_from_seed_hex(SEED_HEX)
envelope = sign_envelope({
"spec_version": "sanning.agent/v1",
"event_id": EVENT_ID,
"signed_at": "2026-01-01T00:00:00Z",
"payload": payload,
"payload_hash": sha256_hex(canonical_json(payload)),
}, key)
Replace SEED_HEX with a 32-byte hex Ed25519 seed and EVENT_ID with the
event's UUID. sign_envelope takes the envelope minus signature and returns
it with public_key and signature filled in, per the spec.
The standard
The specifications ship inside this package, beside the kernel that implements them, so the contract and the implementation claiming to satisfy it arrive together and can be read against each other:
pip download --no-deps --no-binary :all: sanning-proof
tar xzf sanning_proof-*.tar.gz && ls sanning_proof-*/specs/
| Specification | Covers |
|---|---|
envelope-spec.md |
The producer-neutral Verifiable Event Envelope family contract |
evidence-bundle.md |
The sanning.evidence/v1 report wrapper, its body types, and the verdict boundaries in §5.3.4 |
evidence-export.md |
The sanning.evidence.export/v2 wire format |
log-store.md |
The sanning.logstore/v1 store a pack's logs/ is materialized from |
architecture.md |
The kernel, producer, connector and transport factoring standard |
governance.md |
Who decides, and how |
Three envelope profiles are registered against the family contract:
sanning.agent/v1, the agent daemon's inline-payload profile;
sanning.mlflow/v1, the MLflow plugin's external-commitment profile; and
sanning.events/v2, the anchor SDK's minimal-disclosure profile (sanning.events/v1
until the producer-to-agent cut, and no longer accepted). This Python kernel
accepts all three. Additive minors are accepted within a major, while unknown
majors and malformed versions fail closed.
Conformance discipline: both kernels reproduce one pinned corpus byte for byte, across JCS-canonical bytes, payload hashes, envelope-for-signature bytes, deterministic signatures, Merkle roots and audit paths. Neither passes by agreeing with the other. If this package disagrees with a vector, the package is wrong and the vector is not.
Kernel scope
Deliberately small: canonicalization (RFC 8785), SHA-256 hashing, Ed25519 sign and verify, RFC 9162 binary Merkle inclusion proofs, and the profile registry. No I/O, no networking, no key lifecycle. Gateway fetching, attestation polling and key storage belong to the products that import it.
Three dependencies, and adding to them is a design decision rather than a
convenience: PyNaCl and
jcs for the kernel primitives, and
cryptography for RSA-PSS
attestation verification.
A malformed envelope returns a failed result rather than raising. A verifier that crashes on a hostile artifact has handed that artifact a way to avoid being checked, so both kernels treat adversarial input as something to report on, not something to fall over on.
Security
Report vulnerabilities to security@sanning.io. We support responsible disclosure and ask for a reasonable opportunity to investigate before public disclosure.
License
MIT. The verifier is deliberately open-licensed so anyone can audit it and verify evidence independently of Sanning.
Release files for sanning-proof 0.11.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sanning_proof-0.11.0.tar.gz | 1.5 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sanning_proof-0.11.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.7 MB
Release files / sanning_proof-0.11.0.tar.gz
| Download URL | sanning_proof-0.11.0.tar.gz |
|---|---|
| Size | 1.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
22809086720f846930a02074802573c7aad6bcdf556a43dd760353a114a6cdf2
|
|
BLAKE2b-256 checksum How to use checksums |
f05f957a022478f5ccd6fe13d665afb8fd9e75b54affbc2ba21066085939a15f
|
| 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 23, 2026.
Transparency logRelease files / sanning_proof-0.11.0-py3-none-any.whl
| Download URL | sanning_proof-0.11.0-py3-none-any.whl |
|---|---|
| Size | 176.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
62f0a41340ed5fd288b8832eef9a557b3c4a48a43b89bf1de93e4e8531422ccf
|
|
BLAKE2b-256 checksum How to use checksums |
5d19f857e855886e9896bf80ccfcccadd28e17f679506ce1adde9b88aa7ec1ca
|
| 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 23, 2026.
Transparency log