Sanning Proof
Offline verification for the Sanning evidence plane: standalone, dependency-light verification kernels in Python and TypeScript, plus the ratified specs and the conformance corpus both kernels are gated against, byte for byte. No network, no accounts, no vendor code in the trust path — anyone can check a piece of anchored evidence with nothing but this package.
- Python —
sanning_proof(PyPI:sanning-proof), at the repo root. - TypeScript —
@sanning/proof(npm), ints/— browser + Node ≥ 20, ESM.
Verify a signed envelope fetched from any Arweave gateway:
import json
import urllib.request
from sanning_proof import verify_envelope
raw = urllib.request.urlopen("https://arweave.net/raw/<tx_id>").read()
result = verify_envelope(json.loads(raw))
assert result.ok # spec_version + payload binding + Ed25519 signature
import { verifyEnvelope } from "@sanning/proof";
const env = await (await fetch("https://arweave.net/raw/<tx_id>")).json();
const result = await verifyEnvelope(env);
result.ok; // spec_version + payload binding + Ed25519 signature
[!NOTE] Verification never needs an account or a server. If you are producing evidence — anchoring agent steps, running a fleet under continuous tamper-evidence — the hosted plane is console.sanning.io and the write SDK is
@sanning/anchor.
Install
pip install sanning-proof # Python
npm install @sanning/proof # TypeScript
What a verdict proves — and what it doesn't
result.ok proves exactly this: the holder of the private key matching the envelope's public_key signed exactly these bytes, and the payload binding holds.
It deliberately does not prove:
- Whose key that is. The key ↔ identity binding comes from out of band — for example an enrollment roster. The kernel checks cryptography, not identity.
- That the envelope is on-chain. Fetch the transaction from a gateway yourself and re-verify — which is exactly what the example above does.
- When it happened.
signed_atis the signer's claim; witnessed time comes from the Arweave block the envelope landed in. - What the raw data was. Envelopes are content-blind: they commit to hashes. Raw bytes never pass through this kernel unless the producer explicitly disclosed them alongside the proof.
For a verification tool, the guarantee boundary is the product — a verifier that overclaims is worse than no verifier at all.
Verify more things
Bind an artifact you hold to the provenance an envelope commits to (reverse lookup):
import hashlib
artifact_hash = hashlib.sha256(open("model.pkl", "rb").read()).hexdigest()
result = verify_envelope(envelope, expected_content_hash=artifact_hash)
print(result.content_hash_ok, result.content_role) # True, "asset"
Verify an external-commitment envelope (sanning.mlflow/v1) against the committed bytes:
result = verify_envelope(envelope, payload_bytes=canonical_bytes)
Verify an inclusion-proof bundle — proves a leaf event was in a signed checkpoint:
from sanning_proof import verify_proof_bundle
bundle = json.load(open("proof-bundle.json"))
result = verify_proof_bundle(bundle)
assert result.ok and result.inclusion_ok
The TypeScript kernel has full parity, including the RFC 9162 Merkle primitives (leafHash / merkleRoot / auditPath / verifyInclusion) — see ts/README.md.
The CLI
The TypeScript package ships a turnkey CLI. Pinned exit codes — 0 verified · 1 failed · 2 malformed · 3 gateway-unavailable — safe to gate CI on.
Verify any evidence or agent-proof bundle, fully offline; optionally re-fetch checkpoints on-chain by passing gateways:
npx @sanning/proof verify <bundle.json> [gateway1,gateway2,...] [--logs <path>]
--logs binds disclosed raw logs to their committed hashes (evidence bundles only). It takes a directory — a materialized pack's logs/<event_id>.json, whose raw bytes are the disclosure — or the JSON side-input map { event_id: bytes }. Which one you passed is decided by stat, not by the extension. So a pack verifies as it ships, with no side input to construct:
npx @sanning/proof verify pack/bundle.json --logs pack/logs
An event with no disclosed bytes is undetermined, never a failure — absence of a disclosure is not evidence of tampering. Details and edge cases: ts/README.md.
Create an attested bundle — turn a source trace bundle plus operator attestation records into one signed, offline-verifiable sanning.evidence/v1 bundle that verifies like any other:
npx @sanning/proof bundle <source-bundle.json> --attestations <att.json> --key <exporter.hex> -o bundle.json
npx @sanning/proof verify bundle.json
Programmatic equivalent: composeExport(sourceBundle, attestations, { privateKey }). Full format: specs/evidence-export.md.
Signing (producers)
Most producers never call the kernel directly — the write SDK (@sanning/anchor), the agent daemon, and the MLflow plugin all sign through it. For a custom producer:
from sanning_proof import sign_envelope, signing_key_from_seed_hex
key = signing_key_from_seed_hex("<32-byte seed hex>")
envelope = sign_envelope({...}, key) # the envelope minus `signature`, per the spec
The standard
This repo is the authoritative home of the evidence plane's standards layer — the contract is public alongside the reference verifiers:
specs/envelope-spec.md— the producer-neutral Verifiable Event Envelope family contract (ratified v1.0, amended through v1.4, 2026-07-15).specs/evidence-bundle.md— thesanning.evidence/v1report wrapper.specs/evidence-export.md— thesanning.evidence.export/v1wire format behindproof bundle. The spec keeps its ratified name; only the command customers type moved.specs/architecture.md— the kernel / producer / connector / transport factoring standard.specs/governance.md— who decides, and how.test-vectors/— the conformance corpus (current cut: tagtest-vectors-v3.2, per-file SHA-256 inCORPUS-v3.md); generated bytools/gen-vectors/, never hand-edited.ts/vectors/— conformance tables that ship inside the published npm package, so a downstream surface can check itself against them without repo access. Currently one:pack-grading.json, the rule deciding whether a pack (a bundle plus the disclosed bytes beside it) isverified/failed/incomplete, implemented byts/src/grading.ts. Deliberately not intest-vectors/: that corpus is a tagged artifact whose whole file set — including its ownREADME.md— is digest-pinned, so adding to it is a re-cut ceremony, and files outsidets/cannot ride in the npm tarball.
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). The Python kernel accepts all three; the TypeScript kernel accepts sanning.agent/v1 and sanning.events/v1 (the mlflow dialect is Python-only). Additive minors are accepted within a major; unknown majors and malformed versions fail closed.
Conformance discipline: both kernels reproduce the corpus byte for byte — JCS-canonical bytes, payload hashes, envelope-for-signature bytes, deterministic signatures, Merkle roots, audit paths. If this package disagrees with a vector, the package is wrong — never the vector.
Kernel scope
The kernel is deliberately small: canonicalization (RFC 8785), SHA-256 hashing, Ed25519 sign/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. Dependencies are deliberately minimal: Python has exactly two (PyNaCl, jcs); TypeScript has exactly two (@noble/ed25519, canonicalize).
Development
python3 -m venv .venv && .venv/bin/pip install -e .[dev]
.venv/bin/pytest -q # the conformance gate is the contract
.venv/bin/black src tests
The TypeScript kernel lives in ts/ and is conformance-gated in CI against the same test-vectors/ directory.
Contributing
Issues and pull requests are welcome. If you believe a kernel disagrees with a spec or a corpus vector, open an issue titled contract conflict: <spec> §<section> with the smallest reproduction — see specs/governance.md §6.
Security
Please report vulnerabilities privately via GitHub Security Advisories ("Report a vulnerability" under this repo's Security tab) and do not open a public issue.
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.8.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.8.0.tar.gz | 597.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sanning_proof-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 654.4 kB
Release files / sanning_proof-0.8.0.tar.gz
| Download URL | sanning_proof-0.8.0.tar.gz |
|---|---|
| Size | 597.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
32456623ad98c0f5c2ee5f5f2a63d83e1fcaa40820834996cdb40450b2e65757
|
|
BLAKE2b-256 checksum How to use checksums |
84ae509258e8d4fe00872228dd51b8b4c0f1bfdab8e6739f0a90aa6cd2bc68ab
|
| 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 9, 2026.
Transparency logRelease files / sanning_proof-0.8.0-py3-none-any.whl
| Download URL | sanning_proof-0.8.0-py3-none-any.whl |
|---|---|
| Size | 57.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2fb031303acff1a1ee4f3af636c783d31ebc06126f7bf00c91aa58d4aca5f4ec
|
|
BLAKE2b-256 checksum How to use checksums |
8a7269ff33bfe394820fd35db2df109401d3bc360525e64395c8ae5567b6d149
|
| 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 9, 2026.
Transparency log