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 Sponsor

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.0a5 adds portable manylinux/macOS/Windows wheel publishing on top of the 0.4.0a4 standalone fingerprint API, optional exact distinct profiling, typed Arrow validation hot paths, lazy finding rendering, rule-by-rule benchmarks, and the canonical proof/diff/receipt foundations from earlier 0.4 alphas. The receipt schema and detector taxonomy may still change before 0.4 stable.

The 30-second demo

pip install proofframe==0.4.0a5
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 production validation gates, prefer the typed fast path:

gate = pf.validate(users, contract, include_profile=False)

That path keeps bounded row evidence and violation_count, but skips profile, fingerprint, and exact distinct state. The default include_profile=True path is useful when you want validation and a profile/fingerprint artifact from the same scan, but it is intentionally heavier.

Dataset fingerprints

snapshot = pf.profile(users)
fingerprint = pf.fingerprint(users)

print(snapshot["fingerprint"])
assert snapshot["fingerprint"] == 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.

For large datasets where exact cardinality is not needed, prefer:

snapshot = pf.profile(data, distinct="none")
fingerprint = pf.fingerprint(data)

pf.profile(data, distinct="exact") remains available for exact cardinality, but it is deliberately more expensive. The standalone pf.fingerprint(data) path computes only the schema, null/value boundaries, ordered canonical data, and BLAKE3 hash without profile or distinct state.

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 0.4.0a4 rule-matrix harness records required/not-null, min/max, unique, full-contract, fingerprint-only, and exact-distinct profile cases separately with raw timings, rows/sec, Arrow schema, package versions, and isolated-process RSS deltas. A local Windows 7.6M-row run is committed as benchmarks/results/windows-7_6m-a4.json. The older windows-7_6m-baseline.json is a historical 0.4.0a3 baseline from a different notebook-style run and should not be treated as a strict apples-to-apples comparison.

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: extracted Miri-compatible core, fuzz targets, pinned cross-platform benchmarks, Criterion microbenchmarks, allocation tracking, 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.0a5.tar.gz (45.6 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.0a5-cp310-abi3-win_amd64.whl (1.3 MB view details)

Uploaded CPython 3.10+Windows x86-64

proofframe-0.4.0a5-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.0a5-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.0a5-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (2.5 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.0a5.tar.gz.

File metadata

  • Download URL: proofframe-0.4.0a5.tar.gz
  • Upload date:
  • Size: 45.6 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.0a5.tar.gz
Algorithm Hash digest
SHA256 f9a4a56ef81a7527dca5d9363616151b246ddb3afdfd609e5ed7536224457f8f
MD5 f1f61775038c5c711139e321b0feb338
BLAKE2b-256 cf6919c637b42a23f6c7ea3bea03c354b1af68d6818b40702e314fd98ebaf253

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofframe-0.4.0a5.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.0a5-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: proofframe-0.4.0a5-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 1.3 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.0a5-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 1c029cc7826ba2d876a5b3eb3fa82dde47ccd2de84647619a51f5760c97b7ba4
MD5 b1b6b8c2921dbf498159c66e13c2a593
BLAKE2b-256 11f84c239151e39ad63ede5ec98fbf0b1fa9453d4690462e6060664c3a99de06

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofframe-0.4.0a5-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.0a5-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for proofframe-0.4.0a5-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 75ea547641f3e59023b62cd4cf2b69127435a76a5699af9fb1063c8afc3d8477
MD5 3b428604c08480b7a3ea0e4cbc2659e3
BLAKE2b-256 f713ca4635f8f6dd674270b86b6537118c472b04c46fc289cec3767e4bbf276d

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofframe-0.4.0a5-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.0a5-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for proofframe-0.4.0a5-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 9465f968cb1c2b21d439d5ed354ce962306f031dbcac1e927100962ffe310dbd
MD5 4dc820d61ebc34b20947567f0f05974c
BLAKE2b-256 b3b32075a75e52fa5f39ef308a129365f619dd9b358283544f7b307b4d72d7cf

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofframe-0.4.0a5-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.0a5-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.0a5-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 05db00842754cf79e5922ded61a0ee00eec6be2876095e1f2b9acb0642f535f3
MD5 b50822bcd66c06bae9f710dc2b15040d
BLAKE2b-256 b6a15f06a87d2303ab1e92a8ce2ed2265f38489403fd90681285af10c71feaef

See more details on using hashes here.

Provenance

The following attestation bundles were made for proofframe-0.4.0a5-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