Skip to main content

Rust-native data contracts, canonical fingerprints, and proof receipts for DataFrames.

Project description

ProofFrame

ProofFrame

CI Crates.io docs.rs codecov License MSRV

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:

  1. What is this dataset? Profile it and produce a deterministic BLAKE3 fingerprint.
  2. Does it satisfy its contract? Return bounded, row-level evidence—not a vague pass/fail.
  3. 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.0a2 adds 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


Download files

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

Source Distribution

proofframe-0.4.0a3.tar.gz (42.2 kB view details)

Uploaded Source

Built Distributions

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

proofframe-0.4.0a3-cp310-abi3-win_amd64.whl (1.4 MB view details)

Uploaded CPython 3.10+Windows x86-64

proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.4 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.3 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

proofframe-0.4.0a3-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (2.6 MB view details)

Uploaded CPython 3.10+macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

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

Hashes for proofframe-0.4.0a3.tar.gz
Algorithm Hash digest
SHA256 7c61df2a0b52cfeb37480f3f6384ead4f90912d3b683260557b8084bf83badf5
MD5 f4088e05928b19736dcafdbaa64121fb
BLAKE2b-256 d381d647fb5255b54ba276401dd856e1a55c38bf21015ae9b0b34174b10e7808

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on emirhuseynrmx/proofframe

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

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

Hashes for proofframe-0.4.0a3-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 1835d23c91f86652d17723f22ff471052564afa187d745698b7b4ae6f897d984
MD5 cdad0a03ced3067cde699efadb452c98
BLAKE2b-256 1e8e93e3c788afd2acd23f3e78863a20b31e64f58bb5a07ee0f8fc89dadda6d1

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofframe-0.4.0a3-cp310-abi3-win_amd64.whl:

Publisher: publish.yml on emirhuseynrmx/proofframe

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

File details

Details for the file proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 d5243ec68590c7558c2455711433ebf5c9275caf21a7922ccea51e5934b9d171
MD5 34a0f0991d8ed74514dbb12a97bb5b5c
BLAKE2b-256 a40d6a6ca075caf69aa54c25c1e72532ee8453c8bd475e6d698faa174d6e0d2c

See more details on using hashes here.

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

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

File details

Details for the file proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for proofframe-0.4.0a3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 e22a61f50971c8813b7ec53ff313a810e959cb6cb5dc24786ef0d230df6f0633
MD5 7c1ef62627db69452f51ed5d82a2c6f3
BLAKE2b-256 df3e477e58c5ec9121d20a22ed229d6743ae1ba6dab5b80ecd4418db40ec6e63

See more details on using hashes here.

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

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

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

File hashes

Hashes for proofframe-0.4.0a3-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 b46973cb890941b7fa8ab8e01522eb9ede53e005f456a19fc867904cbcf8348b
MD5 85735ddc9a880b594651cffa42ecb57b
BLAKE2b-256 62088975176e1b2a4a76548dbd82a0de43dbb274e9c0928f3e64fbe1203ad0cf

See more details on using hashes here.

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

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

Supported by

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