Rust-native data contracts, canonical fingerprints, and proof receipts for DataFrames.
Project description
ProofFrame
Ruff for data. Git-style evidence for DataFrames.
Why ProofFrame
Most data checks answer one narrow question: did this table pass? ProofFrame is built for the harder production question: what exactly was checked, why did it fail, and can we prove that later?
- Proof, not vibes: every profile includes a versioned canonical BLAKE3 fingerprint
(
pf-fp-v1) so the checked dataset can be identified again. - Bounded evidence: validation returns row-level findings, total violation counts, and truncation status instead of hiding failures behind a boolean.
- One Arrow-native engine: Rust core, Python bindings, PyArrow/Pandas/Polars input, no Python row materialization in the hot path.
- CI-friendly contracts: deterministic JSON, explicit CLI exit codes, signed proof receipts, PII scanning, leakage checks, and keyed diffs.
- Fast Python package, real Rust core: installable from PyPI with ABI-stable wheels, while the same engine is available to Rust users through crates.io.
ProofFrame is a Rust crate with Python bindings for answering three questions before bad data ships:
- What is this dataset? Profile it and produce a deterministic BLAKE3 fingerprint.
- Does it satisfy its contract? Return bounded, row-level evidence—not a vague pass/fail.
- What changed? Diff two datasets by business key and name the changed columns.
It accepts PyArrow tables and streams directly through the Arrow C Stream interface. Pandas and Polars DataFrames use the same Arrow-native path. The validation engine processes record batches in one pass without converting rows into Python objects.
Alpha software:
0.4.0a2adds canonical proof fingerprints, disk-backed exact diffs, privacy-preserving PII findings, train/test leakage checks, and signed proof receipts. The receipt schema and detector taxonomy may still change before 0.4 stable.
The 30-second demo
pip install proofframe==0.4.0a2
import pyarrow as pa
import proofframe as pf
users = pa.table({
"id": [1, 1, 3],
"email": ["a@example.com", None, "not-an-email"],
"score": [0.91, 1.40, 0.73],
})
report = pf.validate(users, {
"columns": {
"id": {"required": True, "unique": True},
"email": {"not_null": True, "pattern": r"^[^@]+@[^@]+$"},
"score": {"min": 0, "max": 1},
}
})
assert not report["valid"]
for finding in report["findings"]:
print(finding)
ProofFrame reports the duplicate ID, null email, malformed email, and out-of-range score with their
row numbers. The same pass returns valid, violation_count, truncated, a content fingerprint,
and a per-column profile.
For a rules-only pipeline gate that keeps bounded row evidence but skips profile and fingerprint
work, use pf.validate(users, contract, include_profile=False).
Dataset fingerprints
snapshot = pf.profile(users)
print(snapshot["fingerprint"])
print(snapshot["rows"])
print(snapshot["columns"])
The fingerprint is emitted as pf-fp-v1:<blake3>. It is deterministic for the ordered Arrow data
and distinguishes nulls, column positions, schema fields, type tags, and value boundaries. The hash
input uses ProofFrame's canonical byte encoding rather than Arrow's display formatting, so upgrading
Arrow cannot silently change fingerprints through prettier string rendering. Store the fingerprint
in CI metadata to prove exactly which data was checked.
Row-level diff
before = pa.table({"id": [1, 2, 3], "plan": ["free", "pro", "pro"]})
after = pa.table({"id": [1, 2, 4], "plan": ["free", "team", "pro"]})
change = pf.diff(before, after, keys="id")
assert change["added_keys"] == ["4"]
assert change["removed_keys"] == ["3"]
assert change["changed"] == [{"key": "2", "columns": ["plan"]}]
Composite keys work too: keys=["tenant_id", "user_id"]. Duplicate keys fail loudly instead of
silently producing a misleading diff. The diff engine uses disk-backed hash partitions, so it keeps
exact changed-column output without materializing both full datasets in memory.
PII and leakage checks
pii = pf.scan_pii(users)
overlap = pf.detect_leakage(train, test, keys="user_id")
PII findings include the class, column, row, confidence, and a short domain-separated BLAKE3 fingerprint. They never include the matched value. Leakage reports support key overlap or exact full-row overlap and expose only hashed sample identifiers.
The built-in detector recognizes email, IPv4, phone, payment-card (Luhn), and IBAN patterns. It is a high-signal scanner, not a legal-compliance guarantee; structured identifiers and locale-specific formats should be covered by explicit contracts too. Bare digit matches from numeric columns are downgraded to low confidence so Luhn-valid order IDs are not reported as high-confidence payment cards without context.
Signed proof receipts
keys = pf.generate_keypair()
report = pf.validate(users, contract)
receipt = pf.sign_receipt(report, private_key=keys["private_key"])
assert pf.verify_receipt(receipt)["valid"]
Receipts use Ed25519 signatures, RFC 8785 JSON canonicalization, and a BLAKE3 report hash. Private keys are generated locally and are never embedded in receipts. Store them in a secret manager, not in source control. To avoid ambiguous JSON number canonicalization, integers outside the I-JSON safe range are rejected.
CLI
ProofFrame reads CSV and Parquet files:
proofframe profile users.parquet
proofframe validate users.parquet --contract examples/contract.json
proofframe diff yesterday.parquet today.parquet --key tenant_id --key user_id
Every command prints stable JSON and uses exit-safe parsing, making it suitable for CI, agents, and data pipeline gates.
proofframe validate exits with 0 for valid data, 1 for contract violations, 2 for input or
configuration errors, and 3 for unexpected internal failures.
Contract format
{
"columns": {
"user_id": { "required": true, "unique": true, "not_null": true },
"email": { "not_null": true, "pattern": "^[^@]+@[^@]+$" },
"score": { "min": 0.0, "max": 1.0 },
"status": { "allowed": ["active", "paused", "deleted"] }
},
"max_findings": 100
}
max_findings bounds the number of finding examples, not correctness. violation_count still
counts every violation, truncated tells you whether examples were omitted, and the profile and
fingerprint still cover the full stream.
Why Rust + Arrow
Python data-quality libraries often materialize Python rows or bind themselves to one DataFrame implementation. ProofFrame accepts the Arrow C Stream protocol, so PyArrow, Pandas, Polars, and any compatible producer can feed the same native engine.
Pandas / Polars / PyArrow / Arrow C Stream
|
v
Arrow record batches
|
+-----------+-----------+
v v v
profile contracts keyed diff
| | |
+-----------+-----------+
v
deterministic JSON evidence
The Rust core currently provides:
- single-pass profiling and validation;
- versioned canonical BLAKE3 dataset fingerprints;
- null, distinct, numeric min/max profiles;
- required, not-null, unique, numeric range, regex, and allowlist rules;
- disk-backed key diff with exact added/removed/changed-column evidence;
- bounded evidence reports;
- a crates.io package with a default Rust API and no required Python linkage;
- Python 3.10+ ABI-stable wheels through PyO3/maturin.
#![forbid(unsafe_code)]in the ProofFrame crate.
Rust users get a separate crates.io-focused README via README-crates.md.
Development
python -m venv .venv
.venv/Scripts/pip install -e ".[dev]" # Windows
.venv/Scripts/maturin develop
.venv/Scripts/pytest -q
cargo test
Run the local throughput harness:
python benchmarks/profile.py --rows 1000000
The Python API and CLI coverage gate is 85%; the current local branch-and-line result is 98.23%. Rust correctness is gated separately by unit tests, Proptest, and Clippy on the declared Rust 1.85 MSRV so Python coverage cannot hide a native-core failure.
Run the same-rule local benchmark harness:
pip install -e ".[benchmark]"
python benchmarks/compare_frameworks.py --rows 1000000 --repeats 5
The script runs the same non-null, uniqueness, and range predicates across a small set of validation
engines; excludes setup/import time; and records raw samples plus exact package versions. It uses
ProofFrame's include_profile=False rules-only path because benchmark peers are not asked to
compute a BLAKE3 dataset fingerprint or exact per-column profile. See docs/testing.md.
Local 0.4 alpha benchmark snapshot
On the development machine (Windows 11, Python 3.12), 1,000,000 valid rows, one warmup, and seven
measured repetitions produced a ProofFrame median of 0.0162 s (61.65M rows/s) for the
rules-only path. Raw peer samples and exact versions are committed in
benchmarks/results/windows-1m-fast.json; this is a reproducible machine-local result, not a
universal performance guarantee or a marketing claim.
The benchmark prints measured rows/second for the current machine; this README intentionally makes no unverified performance claim.
Local v0.1 baseline
On the development machine (Windows x86-64, Python 3.12, release wheel), profiling and fingerprinting a generated Arrow table with 1,000,000 rows × 3 columns took 2.257 seconds (443,060 rows/second). The profile used one integer, one float, and one string column with exact distinct counts. This is a reproducible baseline, not a cross-library comparison; run the harness on your own hardware before drawing performance conclusions.
Roadmap
- 0.4 stable: validation-only fast path, extracted Miri-compatible core, fuzz targets, pinned cross-platform benchmarks, and a stabilized receipt schema.
- 0.5: reversible dataset patches, Parquet predicate pushdown, and configurable diff partition tuning.
- 1.0: stable contract schema and cross-language Rust/Python compatibility.
License
Apache-2.0
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 Distributions
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 proofframe-0.4.0a3.tar.gz.
File metadata
- Download URL: proofframe-0.4.0a3.tar.gz
- Upload date:
- Size: 42.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c61df2a0b52cfeb37480f3f6384ead4f90912d3b683260557b8084bf83badf5
|
|
| MD5 |
f4088e05928b19736dcafdbaa64121fb
|
|
| BLAKE2b-256 |
d381d647fb5255b54ba276401dd856e1a55c38bf21015ae9b0b34174b10e7808
|
Provenance
The following attestation bundles were made for proofframe-0.4.0a3.tar.gz:
Publisher:
publish.yml on emirhuseynrmx/proofframe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
proofframe-0.4.0a3.tar.gz -
Subject digest:
7c61df2a0b52cfeb37480f3f6384ead4f90912d3b683260557b8084bf83badf5 - Sigstore transparency entry: 2171520993
- Sigstore integration time:
-
Permalink:
emirhuseynrmx/proofframe@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Branch / Tag:
refs/tags/v0.4.0-alpha.3 - Owner: https://github.com/emirhuseynrmx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Trigger Event:
push
-
Statement type:
File details
Details for the file proofframe-0.4.0a3-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: proofframe-0.4.0a3-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1835d23c91f86652d17723f22ff471052564afa187d745698b7b4ae6f897d984
|
|
| MD5 |
cdad0a03ced3067cde699efadb452c98
|
|
| BLAKE2b-256 |
1e8e93e3c788afd2acd23f3e78863a20b31e64f58bb5a07ee0f8fc89dadda6d1
|
Provenance
The following attestation bundles were made for proofframe-0.4.0a3-cp310-abi3-win_amd64.whl:
Publisher:
publish.yml on emirhuseynrmx/proofframe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
proofframe-0.4.0a3-cp310-abi3-win_amd64.whl -
Subject digest:
1835d23c91f86652d17723f22ff471052564afa187d745698b7b4ae6f897d984 - Sigstore transparency entry: 2171521004
- Sigstore integration time:
-
Permalink:
emirhuseynrmx/proofframe@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Branch / Tag:
refs/tags/v0.4.0-alpha.3 - Owner: https://github.com/emirhuseynrmx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Trigger Event:
push
-
Statement type:
File details
Details for the file proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5243ec68590c7558c2455711433ebf5c9275caf21a7922ccea51e5934b9d171
|
|
| MD5 |
34a0f0991d8ed74514dbb12a97bb5b5c
|
|
| BLAKE2b-256 |
a40d6a6ca075caf69aa54c25c1e72532ee8453c8bd475e6d698faa174d6e0d2c
|
Provenance
The following attestation bundles were made for proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish.yml on emirhuseynrmx/proofframe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
d5243ec68590c7558c2455711433ebf5c9275caf21a7922ccea51e5934b9d171 - Sigstore transparency entry: 2171521008
- Sigstore integration time:
-
Permalink:
emirhuseynrmx/proofframe@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Branch / Tag:
refs/tags/v0.4.0-alpha.3 - Owner: https://github.com/emirhuseynrmx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Trigger Event:
push
-
Statement type:
File details
Details for the file proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 1.3 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e22a61f50971c8813b7ec53ff313a810e959cb6cb5dc24786ef0d230df6f0633
|
|
| MD5 |
7c1ef62627db69452f51ed5d82a2c6f3
|
|
| BLAKE2b-256 |
df3e477e58c5ec9121d20a22ed229d6743ae1ba6dab5b80ecd4418db40ec6e63
|
Provenance
The following attestation bundles were made for proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
publish.yml on emirhuseynrmx/proofframe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
e22a61f50971c8813b7ec53ff313a810e959cb6cb5dc24786ef0d230df6f0633 - Sigstore transparency entry: 2171521015
- Sigstore integration time:
-
Permalink:
emirhuseynrmx/proofframe@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Branch / Tag:
refs/tags/v0.4.0-alpha.3 - Owner: https://github.com/emirhuseynrmx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Trigger Event:
push
-
Statement type:
File details
Details for the file proofframe-0.4.0a3-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.
File metadata
- Download URL: proofframe-0.4.0a3-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
- Upload date:
- Size: 2.6 MB
- Tags: CPython 3.10+, macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b46973cb890941b7fa8ab8e01522eb9ede53e005f456a19fc867904cbcf8348b
|
|
| MD5 |
85735ddc9a880b594651cffa42ecb57b
|
|
| BLAKE2b-256 |
62088975176e1b2a4a76548dbd82a0de43dbb274e9c0928f3e64fbe1203ad0cf
|
Provenance
The following attestation bundles were made for proofframe-0.4.0a3-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:
Publisher:
publish.yml on emirhuseynrmx/proofframe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
proofframe-0.4.0a3-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl -
Subject digest:
b46973cb890941b7fa8ab8e01522eb9ede53e005f456a19fc867904cbcf8348b - Sigstore transparency entry: 2171520997
- Sigstore integration time:
-
Permalink:
emirhuseynrmx/proofframe@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Branch / Tag:
refs/tags/v0.4.0-alpha.3 - Owner: https://github.com/emirhuseynrmx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d98c3a7711e1515460c33b5eecd8a070f01b935c -
Trigger Event:
push
-
Statement type: