Skip to main content

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 Broker commits salted hashes of tool payloads instead of cleartext, on allow and deny, so PHI/secrets never enter the receipt; cleartext stays in an org-held Vault, disclosed one event at a time with an inclusion proof (vitnify.redact). Pass allow_cleartext=True for 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 output tokens (per-op / activation records — a stronger commitment — are an available mode the shipped binary does not emit). 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 it False.
  • authority_ok — was the signer an approved runtime? Needs a trust root, so it is True / False / None (unestablished when no anchor is supplied — a stranger offline can never answer it, and is told so rather than given a bare False that looks forged).
  • ok = integrity_ok and 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. Pass require_authority=False to make ok the 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 Broker redacts (no tool payload enters the receipt) and verify_certificate requires 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_std engine that produces the bit-identical model-computation digest this SDK binds.
  • vitnify-receipt-spec — the canonical vitnify-receipt v2 format 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

vitnify-0.4.4.tar.gz (63.6 kB view details)

Uploaded Source

Built Distribution

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

vitnify-0.4.4-py3-none-any.whl (45.4 kB view details)

Uploaded Python 3

File details

Details for the file vitnify-0.4.4.tar.gz.

File metadata

  • Download URL: vitnify-0.4.4.tar.gz
  • Upload date:
  • Size: 63.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for vitnify-0.4.4.tar.gz
Algorithm Hash digest
SHA256 d7346793e028c8c3cbac6c3f47b1b2be3daf2d71feaf78d5baeb820d4a2d4d74
MD5 524d78616fe2bafcd9ffcdcf6fbccdc9
BLAKE2b-256 2f29fc1e2ef9d41aadcbf2c87e8befbf4210b66df9176aad1f28103d94c52cf1

See more details on using hashes here.

Provenance

The following attestation bundles were made for vitnify-0.4.4.tar.gz:

Publisher: publish.yml on vitnify/vitnify

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

File details

Details for the file vitnify-0.4.4-py3-none-any.whl.

File metadata

  • Download URL: vitnify-0.4.4-py3-none-any.whl
  • Upload date:
  • Size: 45.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for vitnify-0.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 b664be73456f58d692739c620b07be9eec28e3632f3f457902344f8ac94e616a
MD5 0d4a4bd7753e14f8c98ce5b33abf3365
BLAKE2b-256 acb9f3ae16f176f8c06fc206412967be59f3da7065f662654af12f206cf7fcfc

See more details on using hashes here.

Provenance

The following attestation bundles were made for vitnify-0.4.4-py3-none-any.whl:

Publisher: publish.yml on vitnify/vitnify

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

Release history Release notifications | RSS feed

0.4.6

2 files

This release

0.4.4 This release

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.14

2 files

0.2.13

2 files

0.2.12

2 files

0.2.11

2 files

0.2.10

2 files

0.2.9

2 files

0.2.8

2 files

0.2.7

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.0

2 files

0.0.0

2 files

Supported by

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