Skip to main content

Semantic Provenance Inference Format — a binary file format for AI outputs with provenance, uncertainty, and signatures

Project description

SPIF — Semantic Provenance Inference Format

Cryptographically signed, tamper-evident provenance for AI outputs.

SPIF is a binary wire format that wraps any LLM response with a SHA-256 checksum and an ed25519 signature — baked into the format itself, not bolted on. Every document carries: who produced it, which model, at what timestamp, in what chain of prior calls. Any byte changed after signing is detected on decode.

pip install spif

The Problem

AI outputs flow through pipelines — model to application to database to downstream model — as plain JSON strings. There is no standard way to prove:

  • Which model produced a specific response
  • That the response was not modified in transit or at rest
  • That a chain of AI calls traces back to a known origin

SPIF solves this at the wire format level.


Quickstart

Wrap an Anthropic response

from anthropic import Anthropic
from spif.adapters.anthropic_adapter import AnthropicSPIFAdapter
from spif.writer import SPIFWriter

client = Anthropic()
adapter = AnthropicSPIFAdapter(client, model="claude-sonnet-4-6")

doc = adapter.complete("Summarize the EU AI Act in one paragraph.")

# Write to disk — tamper-evident binary
SPIFWriter().write(doc, "response.spif")

Wrap an OpenAI response

from openai import OpenAI
from spif.adapters.openai_adapter import OpenAISPIFAdapter
from spif.writer import SPIFWriter

client = OpenAI()
adapter = OpenAISPIFAdapter(client, model="gpt-4o")

doc = adapter.complete("Summarize the EU AI Act in one paragraph.")
SPIFWriter().write(doc, "response.spif")

Sign the output

from spif.crypto import derive_key_from_mnemonic
from spif.writer import SPIFWriter

key = derive_key_from_mnemonic("your twelve word mnemonic phrase here", passphrase="strong-passphrase")
SPIFWriter(sign_key=key, signer_id="https://yourorg.com/keys/signing-key-1").write(doc, "signed.spif")

Or use a PEM private key:

from spif.crypto import load_pem_private_key

key = load_pem_private_key("private_key.pem")
SPIFWriter(sign_key=key, signer_id="https://yourorg.com/keys/v1").write(doc, "signed.spif")

Verify

from spif.reader import SPIFReader

# Always verifies checksum on decode
doc = SPIFReader().decode(open("signed.spif", "rb").read())

# Strict: reject unsigned documents
doc = SPIFReader(require_signature=True).decode(data)

# Verify against a known signer
doc = SPIFReader().decode(data, verify_signer="https://yourorg.com/keys/v1")

print(doc.provenance.source_model)    # "claude-sonnet-4-6"
print(doc.provenance.timestamp_ms)    # Unix epoch ms
print(doc.provenance.input_hash)      # SHA-256 of the prompt
print(doc.provenance.context_ref)     # content_id of prior doc in chain

CLI

spif validate response.spif          # checksum + signature check
spif inspect response.spif           # show provenance, model, timestamp
spif verify response.spif --signer https://yourorg.com/keys/v1
spif sign response.spif --key "twelve word mnemonic" --signer-id https://yourorg.com/keys/v1

Audit Chains

context_ref links documents into a verifiable call chain. When Agent B calls a model using Agent A's output as context, the resulting SPIF document references A's content_id. An auditor can reconstruct the full decision chain.

from spif.types import content_id

# Agent A produces a document
doc_a = adapter_a.complete("What are the risks?")
ref = content_id(doc_a)  # stable SHA-256 content address

# Agent B continues the chain
adapter_b = AnthropicSPIFAdapter(client, model="claude-sonnet-4-6", context_ref=ref)
doc_b = adapter_b.complete("Given the risks, recommend an action.")

# doc_b.provenance.context_ref == ref
# The chain is auditable end to end

Wire Format

SPIF is a chunked binary container (similar structure to PNG):

\x89SPIF\r\n\x1a\n   — 9-byte magic
0x02                  — version byte (frozen)
flags                 — compression, streaming flags
HEADER chunk          — model, timestamp, flags
PROVENANCE chunk      — source_model, input_hash, context_ref, attempt, task_id
PAYLOAD chunk         — CBOR-encoded output nodes
[SIGNATURE chunk]     — ed25519 signature over all preceding bytes
CHECKSUM chunk        — SHA-256 of everything before it

The format is self-describing: a SPIF reader needs no external schema to parse and verify a document. The checksum covers the signature, so removing the signature chunk is also detected.

Tamper detection is absolute: every byte flip in a signed document raises SPIFChecksumError or SPIFSignatureError on decode. There are no silent corruptions.


Install

# Full install
pip install spif

# Verification only (minimal dependencies)
pip install spif[verify]

# With Anthropic adapter
pip install spif[anthropic]

# With OpenAI adapter
pip install spif[openai]

Language Support

Language Package Status
Python pip install spif Production — writer + reader + adapters
TypeScript/Deno compat/sif_reader.ts Reader only
Node.js / npm coming soon

Why Not JSON + HMAC?

You can bolt an HMAC onto any JSON response. SPIF is different in three ways:

  1. Format-level, not application-level. The checksum and signature are part of the binary framing — not a field in your JSON that someone can delete or forget to check.
  2. Self-describing. Any SPIF reader, in any language, can verify the document without knowing your application schema.
  3. Content-addressed chains. content_id is a stable hash of the document content, making multi-turn audit chains reconstructable without a central registry.

Specification

Full wire format specification: SPEC.md

Cryptographic implementation audit: CRYPTO_AUDIT.md


License

MIT

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

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

spif-1.0.0-py3-none-any.whl (63.9 kB view details)

Uploaded Python 3

File details

Details for the file spif-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: spif-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 63.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for spif-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8f5aaea85c3291c9a8a6423acf93e2919d8e6269e4fa2020b387ea8c4baaf0d5
MD5 1df0dceb4bf74c18d72829499f6b82b3
BLAKE2b-256 4de892765e109824d7bee09245e71e569e45364b3289d9e07d67298f6646ab03

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