Skip to main content

Evidence infrastructure for high-stakes AI. Tamper-evident, hash-chained records for every model inference.

Project description

Inferify

tests pypi python

Evidence infrastructure for high-stakes AI.

Seal every model decision into a tamper-evident, SHA-256 hash-chained record. When a decision is questioned months later, the proof already exists, and anyone can verify it was never altered.

pip install inferify

Quickstart

import inferify

inferify.configure(store="evidence.db", envelope="envelope.yaml")

verdict = inferify.capture(
    model="novadx-v2.3.1",
    input=xray_512,                  # hashed, never stored
    output={"pneumonia": 0.91},
    input_type="chest_xray_512",
)

if not verdict:                      # falsy when the regime is FLAGGED
    escalate(verdict)                # verdict.reasons tells you why

capture() returns the regime verdict synchronously, because your code needs it before the decision leaves the system. The record is sealed on a background writer thread, so hashing and disk IO stay off your inference path.

The envelope

An envelope encodes the conditions your model was actually validated under.

model_versions: [novadx-v2.3.1, novadx-v2.3.2]
input_types:    [chest_xray_512]
confidence_min: 0.75
allowed_labels: [normal, pneumonia, effusion, nodule]
feature_bounds:
  patient_age: [0, 120]

Inside the envelope, the record is sealed VALID. Outside, it is sealed FLAGGED together with the reasons.

Flagging never blocks the seal. A flagged record is a truthful, permanent record of a risky decision. It has to stay in the chain, otherwise you could not prove the flag ever happened. FLAGGED is a verdict about the model. A broken chain is a verdict about whoever edited history. They are different things.

Verifying

inferify-verify evidence.db            # verify the live store
inferify-verify bundle.json            # verify an exported bundle
inferify-verify bundle.json --json     # machine readable, for CI

Exit code is 0 when the chain is intact and 1 when it is not, so this drops straight into a pipeline.

Verification recomputes every record from genesis. It never trusts a stored hash. Change one byte of any record and that record fails, and every record after it fails with it, because each one committed to the hash before it.

hash = sha256(prev_hash_ascii + canonical_json(body))

canonical_json is sorted-key, minimal-separator, UTF-8 JSON. Using canonical JSON rather than a delimiter join means no user-controlled string can change how a record is framed.

What is actually guaranteed

Guaranteed:

  • Raw inputs are never written to the store. Only a SHA-256 fingerprint. There is a test that greps the database file for the raw bytes.
  • Records are append-only, enforced by SQLite triggers rather than application code. UPDATE and DELETE abort at the database level.
  • Concurrent writers produce a contiguous, linear chain. Verified with 8 threads writing 200 records.
  • Any edit, reorder, or deletion in an exported bundle is caught, and identified down to the exact record id.

Not guaranteed by the hash chain alone:

A hash chain proves nobody edited history without rebuilding the chain. It does not stop the party who holds the chain from regenerating it end to end. That attack verifies perfectly.

The fix is anchoring. Publish a checkpoint hash on a schedule to somewhere the chain's owner cannot rewrite:

cp, receipt = inferify.publish(store, witness)

Once a checkpoint is out in the world, every record sealed before it is frozen, because a rebuilt chain produces a head that no longer matches the published checkpoint. FileWitness is a development stand-in. A production witness is a customer-held key, a transparency log, a notary, or any third-party append-only service. See tests/test_inferify.py::test_published_checkpoint_detects_a_rebuilt_chain.

Measured performance

On a 512x512 uint8 input, single shard, commodity hardware:

p50 p95 p99 sustained
async writer (default) 0.69 ms 1.11 ms 1.25 ms ~626 rec/sec
sync write, fsync each 1.69 ms 2.63 ms 3.46 ms ~512 rec/sec

Input hashing alone is ~0.65 ms, which dominates capture(). Reproduce with python examples/bench.py.

Throughput is per shard, because a chain serializes writes by construction. Scale with shard= (one chain per shard, verified independently).

Anchoring, and why the SDK alone is not enough

A hash chain proves nobody edited history without rebuilding the chain. It does not stop the party who holds the chain from regenerating it end to end. The rebuilt chain verifies perfectly.

The fix is publishing a checkpoint to a witness the chain's owner does not control. You cannot self-host the thing that makes your evidence credible against you.

from inferify.witness import HttpWitness

w = HttpWitness("https://api.inferify.io", api_key="ink_...")
cp, receipt = inferify.publish(store, w)

ok, detail = inferify.check_consistency(store, w.receipts())

HttpWitness never trusts the server. It recomputes the receipt hash, verifies the Ed25519 signature against the published public key, and confirms the checkpoint in the receipt is the one it submitted. FileWitness is a local stand-in with no trust boundary, useful for development.

The witness service lives at https://github.com/inferify/inferify-cloud.

Status

Version 0.1.0, alpha. Implemented and tested (28 tests): fingerprinting, the envelope, the append-only hash-chained store, the exporter, the verifier CLI, and the witness client.

Not built: TypeScript SDK, Merkle batching for high-throughput shards.

Layout

inferify/
  fingerprint.py   deterministic hashing and canonical serialization
  envelope.py      the validated operating envelope and regime verdicts
  store.py         append-only, hash-chained SQLite evidence store
  client.py        capture() and the background writer
  verify.py        chain verification, bundle export, CLI
  anchor.py        checkpoints and external witnesses

Tests

pip install -e ".[dev]"
pytest -q

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

inferify-0.1.0.tar.gz (21.2 kB view details)

Uploaded Source

Built Distribution

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

inferify-0.1.0-py3-none-any.whl (19.7 kB view details)

Uploaded Python 3

File details

Details for the file inferify-0.1.0.tar.gz.

File metadata

  • Download URL: inferify-0.1.0.tar.gz
  • Upload date:
  • Size: 21.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for inferify-0.1.0.tar.gz
Algorithm Hash digest
SHA256 26309025341e2de0f38df03fe743c76d05c7ef0fbc9c31a48213d917c49b90f2
MD5 08fb4e63c2bb5b02f3ace706c31f7064
BLAKE2b-256 0272bbbaaf0b55301089b142ff8aa1cae6d8622b2742fd334f8340f845f7a40f

See more details on using hashes here.

File details

Details for the file inferify-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: inferify-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for inferify-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 be35b7bfdb263e463176b4b77f514644e8cd0328aea29aeb0cc66f07c766808c
MD5 1a2ed7bdea35ab6d3ddd76d6267dcfdb
BLAKE2b-256 7d9b2cd3b9c228a95a38f9b35ab60773e8e0c0f79edaf9d4d93aacd53080b720

See more details on using hashes here.

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