Skip to main content

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-proof on PyPI. A library, with no command of its own.
  • TypeScript: @sanning/proof on npm, browser and Node ≥ 20. Ships the proof command.

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 a gateway was unreachable. 3 is deliberately not 1: an unreachable gateway leaves the on-chain half undetermined, and reporting that as failed would say the evidence is bad when what happened is that the network is down.

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.

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/v1 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/v1, the anchor SDK's minimal-disclosure profile. This 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.9.2

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

Source distribution (sdist)

Source distribution for sanning-proof 0.9.2
File Size Uploaded
sanning_proof-0.9.2.tar.gz 727.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sanning-proof 0.9.2
File Interpreter ABI Platform
sanning_proof-0.9.2-py3-none-any.whl Python 3 none any Details

Total release size: 797.1 kB

Release files / sanning_proof-0.9.2.tar.gz

Download URL sanning_proof-0.9.2.tar.gz
Size 727.6 kB
Tags Source
SHA-256 checksum
How to use checksums
60682464336f2bdc62ae0690333270ed4719768ab4efa4d66bfcb84eb69043c7
BLAKE2b-256 checksum
How to use checksums
62340a81b2e5eb7bd2266e732770a91a959f17e560d6898100144eae1192a1e3
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 15, 2026.

Transparency log

Release files / sanning_proof-0.9.2-py3-none-any.whl

Download URL sanning_proof-0.9.2-py3-none-any.whl
Size 69.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a0cf211882f2566fa88c3b972b7bb598d1b5c06f55b4aac93bdb0c8f9e9b5d4a
BLAKE2b-256 checksum
How to use checksums
c66c809f940d1472c7e00543186a2d50f4bb3c2f59781f4fdd93950ed3b4d9c3
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 15, 2026.

Transparency log

Release history Release notifications | RSS feed

0.15.0

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.1

2 release files

0.11.0

2 release files

0.10.0

2 release files

This release

0.9.2 This release

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.4.0

2 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