Skip to main content

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.

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_at is 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 <logs.json>]

--logs binds disclosed raw logs to their committed hashes (evidence bundles only).

Compose an attested export — 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 export <source-bundle.json> --attestations <att.json> --key <exporter.hex> -o export.json
npx @sanning/proof verify export.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:

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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sanning_proof-0.4.0.tar.gz (375.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sanning_proof-0.4.0-py3-none-any.whl (34.6 kB view details)

Uploaded Python 3

File details

Details for the file sanning_proof-0.4.0.tar.gz.

File metadata

  • Download URL: sanning_proof-0.4.0.tar.gz
  • Upload date:
  • Size: 375.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sanning_proof-0.4.0.tar.gz
Algorithm Hash digest
SHA256 90913ed2c7a223dc3170e75b02ff80ce66c916629096c0c976998ffbb93837d6
MD5 1db9dc46984721e2299418a0090797ad
BLAKE2b-256 aacbab59cb808401eea1500dd6eb8591cd9a55c7a8610b3718145e4746999dd2

See more details on using hashes here.

Provenance

The following attestation bundles were made for sanning_proof-0.4.0.tar.gz:

Publisher: release.yml on sanning-io/proof

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sanning_proof-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: sanning_proof-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 34.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sanning_proof-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f634103e081cad4f1830bb4d22f9fac02e96d13b858132cec7995dd51d2e2865
MD5 60165bcd6ec9bfad1d655f5198010ef1
BLAKE2b-256 611696fafa7b22ef6dcde938d94696de648d1f741793cf96d45ce6aa08e370b1

See more details on using hashes here.

Provenance

The following attestation bundles were made for sanning_proof-0.4.0-py3-none-any.whl:

Publisher: release.yml on sanning-io/proof

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page