Vitnify your agents
Logs tell you what your agent did. Vitnify proves it — a cryptographic, independently-reconstructable record of what an agent computed and did.
Contain what an agent may do, deterministically reconstruct the model behind every decision, and seal the whole run into one bit-for-bit receipt anyone can verify offline — long after it happened.
vitnify (v.) — to turn an agent run into a receipt anyone can reproduce and verify, offline.
vitnify isn't detection. It gives you the primitives to prove exactly what an agent
did: a vitnify-receipt v2 binds the model's computation, the granted capabilities,
every tool call and result, the entropy, and the order into a single ed25519-signed,
self-verifying object.
Install
pip install vitnify
Quickstart
from vitnify.events import EventLog, Kind
from vitnify.engine import Engine, prompt_hash
from vitnify.certificate import issue_certificate, verify_authorized, gen_ed25519
eng = Engine("model.gguf", model_id="my-model") # deterministic backend
log = EventLog()
step = eng.run(prompt_tokens=[1, 2, 3], n_new=20) # a model step
log.append_llm_call(prompt_hash([1, 2, 3]), step["tokens"], seed=0,
model_digest=step["model_digest"], # bind the model computation
regime=step.get("regime"), # + regime and weights_hash: bound in the
weights_hash=step.get("weights_hash")) # digest, now readable in the receipt too
log.append(Kind.TOOL_CALL, {"tool": "read_docs", "decision": "allow"})
log.append(Kind.TOOL_CALL, {"tool": "send_email", "decision": "deny"}) # ungranted → blocked
priv, pub = gen_ed25519()
cert, _ = issue_certificate("program_hash", ["read_docs"], log, priv=priv)
checks = verify_authorized(cert, log, pinned_pubkeys=[pub]) # L1: offline, no model/secret; authorised signer
assert checks["ok"] # signed by a trusted key, unaltered, no ungranted tool ran
assert checks["containment_enforced"] # every tool call was GATED, not merely observed
# A receipt can be ok=True yet containment_enforced=False — a valid transcript from a
# watch-only integration proves what ran, not that anything was contained. A containment
# claim requires BOTH. (level 2: re-run each step through the engine; every model_digest
# reproduces bit-for-bit.)
See the receipt format spec
(canonical — this repo does not vendor a copy, so the two can't drift), and
examples/demo_receipt_e2e.py for the full loop.
What you get
- Capability containment — ungranted tools are structurally unreachable.
- Deterministic replay — re-run a contested run and get the identical result, bit-for-bit.
- Bit-for-bit receipts — the model's exact computation, bound and signed.
- Redaction by default — the
Brokercommits salted hashes of tool payloads instead of cleartext, on allow and deny, so PHI/secrets never enter the receipt; cleartext stays in an org-heldVault, disclosed one event at a time with an inclusion proof (vitnify.redact). Passallow_cleartext=Truefor the old behaviour (non-sensitive data only). - Offline verification — anyone verifies a receipt's integrity (
integrity_ok) with no model, network, or secret; authority (that an approved runtime signed it) is a separate verdict that needs a pinned trust root. - Drop-in — wraps existing LangGraph and MCP agents (
pip install vitnify[langgraph]/[mcp]).
Two verification levels — and when to use each. Level 1 (integrity) is offline, instant, and needs no model — recompute the Merkle root and check the signature; this is the default for every receipt, and it's what proves containment and tamper-evidence. Level 2 (recompute) additionally re-runs the model to reproduce the committed logits. It is the dispute path — run on a contested subset when someone challenges a specific decision, not on every receipt inline. It is deliberately slow: the pinned-order deterministic engine trades throughput for bit-exactness, roughly two orders of magnitude below native inference (~0.45 tok/s vs ~58 for Mistral-7B Q4_K_M on the same Metal box). Fleet throughput still scales the normal way — L2 is embarrassingly parallel across receipts; a single recompute is simply not something you do on the hot path.
The verdict is split (0.4.1) — a receipt answers two different questions, and a verifier reports them separately instead of collapsing them into one boolean:
integrity_ok— is the transcript internally consistent and validly signed by whoever signed it? Answerable by anyone, offline, no secret. Tampering, a forged chain, an ungranted tool, a bad signature all set itFalse.authority_ok— was the signer an approved runtime? Needs a trust root, so it isTrue/False/None(unestablished when no anchor is supplied — a stranger offline can never answer it, and is told so rather than given a bareFalsethat looks forged).ok=integrity_okand an authorised signer. Pin the trusted key(s) —verify_authorized(cert, log, pinned_pubkeys=…)is the production entry point; anchor it in a TPM/enclave for the strongest form. Passrequire_authority=Falseto makeokthe integrity-only verdict (the answer a stranger can compute offline).
Program binding. program_hash is caller-asserted unless you bind it. Pass
derive_program_hash(paths_or_bytes) at issue time and verify_certificate(..., program=…)
at verify time to make the receipt bind the actual program, not a label.
✅ Safe by default (0.4.0)
The
Brokerredacts (no tool payload enters the receipt) andverify_certificaterequires signer authority (a re-signed forgery can't verify without a trusted pin). Relax either only where appropriate, and do it explicitly:broker = Broker(caps, tools, log, allow_cleartext=True) # record payloads in cleartext (non-sensitive only) verify_certificate(cert, log, require_authority=False) # integrity-only verdict (continuity, not authority)The production verify pins the trusted signer(s) and can bind the program:
from vitnify.certificate import verify_authorized, derive_program_hash cert, _ = issue_certificate(derive_program_hash(SRC), caps, log, priv=priv) # bind the real code checks = verify_authorized(cert, log, pinned_pubkeys=[trusted_key], program=SRC) # authority + binding
The deterministic engine is vitni-tensor;
the vitni-receipt binary is the model backend (point VITNI_RECEIPT_BIN at it).
License
Apache-2.0. "vitnify" and "vitnify-verified" are trademarks — see TRADEMARKS.md. A fork may use the code, but not the name or issue vitnify-verified receipts.
Part of Vitnify
This SDK is one of three open repos:
- vitni-tensor — the deterministic,
no_stdengine that produces the bit-identical model-computation digest this SDK binds. - vitnify-receipt-spec — the
canonical
vitnify-receipt v2format the SDK implements. - vitnify.com — the project.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 vitnify-0.4.2.tar.gz.
File metadata
- Download URL: vitnify-0.4.2.tar.gz
- Upload date:
- Size: 62.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8fe5713d9b89694cf66ffd685e78dbdb720481e2c1a4370a5b5cf5d0ceb1ffd
|
|
| MD5 |
63059306ed3497af5c5c551c99da1730
|
|
| BLAKE2b-256 |
b91eb1798023476c53622fee499ff0baabdd712c6f542529082f24300dc1c754
|
Provenance
The following attestation bundles were made for vitnify-0.4.2.tar.gz:
Publisher:
publish.yml on vitnify/vitnify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vitnify-0.4.2.tar.gz -
Subject digest:
e8fe5713d9b89694cf66ffd685e78dbdb720481e2c1a4370a5b5cf5d0ceb1ffd - Sigstore transparency entry: 2581974455
- Sigstore integration time:
-
Permalink:
vitnify/vitnify@f4bc86537430f2f22a0ddf3201f27106428adade -
Branch / Tag:
refs/tags/v0.4.2 - Owner: https://github.com/vitnify
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f4bc86537430f2f22a0ddf3201f27106428adade -
Trigger Event:
release
-
Statement type:
File details
Details for the file vitnify-0.4.2-py3-none-any.whl.
File metadata
- Download URL: vitnify-0.4.2-py3-none-any.whl
- Upload date:
- Size: 44.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ee2a44df5c9e45b2512f0189e13e4bbd1c43b6bc51852be9078c409f36a3b8c
|
|
| MD5 |
4d536c17038233d2008bb9a20211cd92
|
|
| BLAKE2b-256 |
c84b92dacbcf4072f7fdc1e1cf863ca9317e26f9d774e9ad12865218c6e89fe4
|
Provenance
The following attestation bundles were made for vitnify-0.4.2-py3-none-any.whl:
Publisher:
publish.yml on vitnify/vitnify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vitnify-0.4.2-py3-none-any.whl -
Subject digest:
9ee2a44df5c9e45b2512f0189e13e4bbd1c43b6bc51852be9078c409f36a3b8c - Sigstore transparency entry: 2581974459
- Sigstore integration time:
-
Permalink:
vitnify/vitnify@f4bc86537430f2f22a0ddf3201f27106428adade -
Branch / Tag:
refs/tags/v0.4.2 - Owner: https://github.com/vitnify
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f4bc86537430f2f22a0ddf3201f27106428adade -
Trigger Event:
release
-
Statement type: