Verification kernel for the ar.io verification stack: RFC 8785 canonicalization, SHA-256, Ed25519 envelope sign/verify, RFC 9162 Merkle inclusion proofs.
Project description
ar-io-proof
The polyglot verification home of the ar.io verification stack: RFC 8785 (JCS) canonicalization, SHA-256 hashing, Ed25519 envelope sign/verify, and RFC 9162 binary Merkle inclusion proofs — as standalone, dependency-light kernels in two languages, plus the family specs and the one authoritative conformance corpus. An app never owns a kernel; producers and verifiers import these.
- Python —
ario_proof(PyPIar-io-proof), at the repo root. - TypeScript —
@ar.io/proof(npm), ints/.
Both reproduce the same test-vectors-v1.0 corpus byte-for-byte; that mutual conformance
gate is the point — anyone can verify ar.io provenance with no ar.io code in the trust path.
pip install ar-io-proof # Python
npm install @ar.io/proof # TypeScript
Quickstart
Verify a signed envelope fetched from any Arweave gateway:
import json
import urllib.request
from ario_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
print(result.to_dict())
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 (ario.mlflow/v1) envelope against the committed bytes:
result = verify_envelope(envelope, payload_bytes=canonical_bytes)
Verify an inclusion-proof bundle (ariod proof output — proves a leaf was in a signed
daily checkpoint):
from ario_proof import verify_proof_bundle
bundle = json.load(open("proof-bundle.json"))
result = verify_proof_bundle(bundle)
assert result.ok and result.inclusion_ok
Sign an envelope (producers):
from ario_proof import sign_envelope, signing_key_from_seed_hex
key = signing_key_from_seed_hex("<32-byte seed hex>")
envelope = sign_envelope(
{
"spec_version": "ario.mlflow/v1",
"event_id": "...",
"event_type": "training_complete",
"subject": {"type": "mlflow_run", "run_id": "..."},
"payload_hash": "<sha256 of the committed canonical bytes>",
"previous_hash": "GENESIS",
"signed_at": "2026-06-10T00:00:00.000Z",
},
key,
)
TypeScript (@ar.io/proof)
The same algorithm, conformance-gated against the same corpus (ts/test/conformance.test.ts
reads the authoritative test-vectors/ directly). Browser + Node ≥ 20, ESM, two small deps
(@noble/ed25519, canonicalize):
import { verifyEnvelope, sha256Hex, contentHashes } from "@ar.io/proof";
const env = await (await fetch("https://arweave.net/raw/<tx_id>")).json();
const result = await verifyEnvelope(env);
result.ok; // spec_version + payload_hash + Ed25519 all passed
// Reverse lookup: bind an artifact you hold to what an envelope commits to.
const bound = await verifyEnvelope(env, await sha256Hex(fileBytes));
bound.contentHashOk; // true ⇔ the envelope commits to exactly these bytes
It also ships the RFC 9162 Merkle primitives (leafHash / merkleRoot / auditPath /
verifyInclusion) for full parity with the Python kernel. See ts/README.md.
What these kernels implement
- The Verifiable Event Envelope family contract,
envelope-spec.mdv1.1 (ratified v1.0 2026-06-10, amended 2026-06-11 — additive, same conformance corpus), for two profiles:ario.agent/v1— inline-payload envelopes minted byar-io-agent(byte-level format:docs/artifact.md).ario.mlflow/v1— external-commitment envelopes minted byar-io-mlflow.
- The RFC 9162 binary Merkle tree (leaf/node domain separation, audit paths, pinned
empty-tree root) and the
ario.agent.proof/v1inclusion-proof bundle behind agent verification checkpoints. - The accepted-version registry:
{ario.agent/v1, ario.mlflow/v1}, matched on thev<major>token boundary per envelope-spec §2 — additive minors (ario.agent/v1.3) are accepted within a major; different majors (ario.agent/v10) and malformed minors fail closed. Envelopes that predatespec_versionverify only with an explicitallow_legacy=True. - The signed scope per the ratified contract: the envelope minus
signature, minus the reservedco_signaturesfield (envelope-spec §7.1), and — forario.mlflow/v1and legacy envelopes only — minus underscore-prefixed annotation keys. Theario.agent/v1signed scope is minussignature/co_signaturesonly, matching the Go reference byte-for-byte.
The kernel is exactly the five primitives in the stack architecture's kernel scope — no I/O, no networking, no key lifecycle. Gateway fetching, attestation polling, and key storage belong to the products that import this.
Conformance
This package is conformance-gated against the ario.agent/v1 corpus at tag
test-vectors-v1.0, vendored under test-vectors/ byte-for-byte (see
test-vectors/VENDORING.md for provenance). CI asserts, for
every vector: JCS-canonical bytes, payload hashes, envelope-for-signature bytes,
deterministic signatures, Merkle roots, and audit paths — exact to the byte. If this
package disagrees with a vector, the package is wrong — never the vector.
mlflow-profile-specific behaviors (external commitment, underscore stripping, legacy
acceptance) are covered by unit tests; the bidirectional cross-product gate against
ar-io-mlflow's production verifier lands when mlflow migrates to import this package.
Trust model
result.ok proves: the holder of the private key matching the envelope's public_key
signed exactly these bytes, and the payload binding holds. It does not prove whose key
that is, or that the envelope is on Arweave — trust in the key comes from out of band (e.g.
the agent's registration chain), and on-chain presence comes from fetching the TX yourself
and re-verifying, which is exactly what the quickstart does. signed_at is the signer's
claim, not a trusted timestamp; witnessed time comes from the Arweave block.
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
Dependencies are deliberately two: PyNaCl (Ed25519,
strict RFC 8032 — matches Go crypto/ed25519 and the JS sibling verifier) and
jcs (reference RFC 8785).
License
MIT — verifier-relevant code is deliberately MIT-licensed so third-party auditors can verify independently of ar.io.
Standards home
As of 2026-06-11 this repo is the authoritative home of the verification stack's
standards layer (moved from ar-io-agent so the contract is public alongside the
reference verifiers) — and, with the TypeScript kernel's move from the proof-checker app,
the single home of every conformant kernel the stack ships (the Go reference stays in
ar-io-agent/pkg/proof, MIT-carved):
specs/envelope-spec.md— the producer-neutral Verifiable Event Envelope family contract (ratified v1.0, amended v1.1)specs/evidence-bundle.md—ario.evidence/v1report wrapper (ratified v1.0, not yet implemented)specs/architecture.md— kernel / producer / connector / transport factoring standard (ratified v1.0)specs/governance.md— who decides, how (BDFL, async docs-PR, corpus-tag governance, amendment log)test-vectors/— the conformance corpus, locked attest-vectors-v1.0(per-file SHA-256 inCORPUS-v1.md), generated bytools/gen-vectors/— generated, never hand-editedts/— the TypeScript kernel (@ar.io/proofon npm), conformance-gated against the sametest-vectors/corpus; moved here 2026-06-11 from the proof-checker app (an app never owns a kernel — the same principle that extracted the Python kernel from mlflow). The proof-checker now consumes it as an ordinary npm dependency.- Producer profiles stay with their producers:
ario.agent/v1isartifact.mdinar-io-agent;ario.mlflow/v1lives inar-io-mlflow.
Tag namespaces (no overlap): v* = Python release · ts-v* = TypeScript release ·
test-vectors-v* = corpus. Python publishes to PyPI via release.yml; TypeScript publishes
to npm via release-ts.yml (OIDC trusted publishing, provenance on).
Contract conflicts: open an issue here titled contract conflict: <spec> §<section> with
the smallest reproduction (see specs/governance.md §6).
Project details
Release history Release notifications | RSS feed
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 ar_io_proof-0.2.0.tar.gz.
File metadata
- Download URL: ar_io_proof-0.2.0.tar.gz
- Upload date:
- Size: 192.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
34b4fcab20147c8bc911f33afd661ec8db63fc47ebc69be365f104decfe648e1
|
|
| MD5 |
f6bbaaf29cfd2dd645d6b3e51c0eadd8
|
|
| BLAKE2b-256 |
d41f3fa2590a9fa2697a78becf988a45f41976d2df3b841dc82dcbf6b62a5a71
|
Provenance
The following attestation bundles were made for ar_io_proof-0.2.0.tar.gz:
Publisher:
release.yml on ar-io/ar-io-proof
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ar_io_proof-0.2.0.tar.gz -
Subject digest:
34b4fcab20147c8bc911f33afd661ec8db63fc47ebc69be365f104decfe648e1 - Sigstore transparency entry: 1828251212
- Sigstore integration time:
-
Permalink:
ar-io/ar-io-proof@202327401fde9cfbbf43ed380fafe4736185271c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ar-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@202327401fde9cfbbf43ed380fafe4736185271c -
Trigger Event:
push
-
Statement type:
File details
Details for the file ar_io_proof-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ar_io_proof-0.2.0-py3-none-any.whl
- Upload date:
- Size: 18.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df78607eb7f7f3f0655f8ec7b27ce1ecd770e3b609bd10c4d1b65d638e617d62
|
|
| MD5 |
9048ac6b3b5e81deea678921612634e0
|
|
| BLAKE2b-256 |
c38a67061e9937f66feef48380fb2bb4fd66a099e74751f45fc9472e94a04882
|
Provenance
The following attestation bundles were made for ar_io_proof-0.2.0-py3-none-any.whl:
Publisher:
release.yml on ar-io/ar-io-proof
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ar_io_proof-0.2.0-py3-none-any.whl -
Subject digest:
df78607eb7f7f3f0655f8ec7b27ce1ecd770e3b609bd10c4d1b65d638e617d62 - Sigstore transparency entry: 1828251257
- Sigstore integration time:
-
Permalink:
ar-io/ar-io-proof@202327401fde9cfbbf43ed380fafe4736185271c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ar-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@202327401fde9cfbbf43ed380fafe4736185271c -
Trigger Event:
push
-
Statement type: