Provenrail
Guardrails for AI agents, with a receipt. Destructive tool calls are blocked before they run, and every decision is signed into an off-box, hash-chained record that anyone can verify, trusting neither the agent nor the sink.
30 seconds, no code: guard Claude Code
Inside Claude Code:
/plugin marketplace add pofky/provenrail
/plugin install provenrail-guard@provenrail
Then once per project:
uv tool install provenrail # or: pip install provenrail
pr quickstart # local sink, no account, nothing leaves your machine
pr guard install # arms destructive + secrets + production
rm -rf, terraform destroy, git push --force, DROP TABLE, chmod 777 and leaked
API keys are now denied at the tool boundary, and the agent is told which rule fired.
Actions that need a human (touching .env, deploying, migrating) become a permission
prompt instead of a hard block, and the approval is recorded as human oversight. Run
pr guard receipt and pr verify guard-receipt.json to check the record yourself.
Full guide: https://provenrail.com/claude-code-guardrails
The record underneath
Install the SDK, capture model calls and tool calls, and every record is pushed to an
append-only sink where it cannot be silently altered, reordered, deleted, or back-dated.
Anyone can verify the record with the open-source pr-verify tool.
This is the self-serve Provenrail SKU. It is a forensic black box, not a compliance certifier. The regulated Audit Trail SKU (cross-account WORM, witnessed anchors, TEE-backed keys, regime attestation reports) is a separate, sales-assisted tier.
What it guarantees, and what it does not
Read this before trusting it. Honesty about the boundary is the product.
| Property | Guaranteed? | Mechanism |
|---|---|---|
| A flushed record cannot be altered without detection | Yes | client Ed25519 signature + hash chain; server re-chains on receipt |
| Records cannot be reordered or deleted mid-stream without detection | Yes | dual chain (client prev_hash links + server receipt chain) |
| Records existed no later than a trusted time | Yes, when anchored | RFC 3161 trusted timestamp over a Merkle root of the receipt chain |
| The sink cannot fabricate agent actions | Yes | only the device key can sign; the sink does not hold it |
| Completeness (a hostile agent cannot skip recording) | No, never claimed | an in-process logger can refuse to emit; the out-of-process sidecar narrows this (see below) but only egress control makes capture mandatory |
| The sink cannot equivocate (show two anchor histories) or drop published anchors | Yes, when witnessed | per-account RFC 6962 transparency log + independent witness cosignatures (SPEC.md section 9) |
| Tail records after the last anchor cannot be truncated | Bounded | anchor frequency for the unanchored tail, plus client pin; published anchors are witness-protected |
| Protection against the machine owner (B2C single host) | No | the owner holds the key and can choose not to flush |
The honest one-line claim: "Anything your agent did, once it reaches the sink, is immutable and verifiable. Completeness is never claimed."
Threat model
The adversary is the agent (or a prompt-injection of it). The agent can refuse to log,
but it cannot forge, alter, reorder, or delete anything that already reached the sink, nor
back-date it past a trusted timestamp, without pr-verify reporting it. Defense against a
malicious sink operator (who could truncate the tail or show different histories to
different parties) is the transparency / witness log: a per-account append-only RFC 6962
Merkle log over the anchor receipts, with inclusion and consistency proofs an auditor checks
offline and independent witness cosignatures on each head. A witness will not cosign a fork or
a rollback, so a sink cannot equivocate without compromising a quorum of witnesses. Honest
boundary: witnessing proves append-only-ness and anti-equivocation, never completeness; an
un-cosigned checkpoint is reported amber, not green (SPEC.md section 9.8). The client pin still
covers the unanchored tail between the last cosigned checkpoint and now.
Capture topology and the completeness gap. In-process capture (the SDK and the drop-in
instrumenters) is the easiest to adopt but shares the agent's address space, so a hostile agent
can skip the logger. The capture sidecar (pr sidecar) is a recording reverse proxy: the
agent points its model client at the proxy, which forwards to the real provider and records each
call from a separate process the agent does not control. To dodge recording the agent must then
avoid the proxy entirely, which means getting no model response when egress is locked to the
proxy. The sidecar does not by itself prove completeness; its strength equals the egress control
around it. The deployment contract is "force all model egress through the sidecar" (network
policy / firewall). With that, capture is mandatory; without it, the sidecar is a strong default,
not a proof. We never overstate this.
Architecture
agent process off-box sink auditor
------------- ------------ -------
SDK (capture) ingest API (append-only) pr-verify
model calls ---> server receipt chain ---> recompute every
tool dispatch (re-chains on arrival) hash, signature,
hash-not-content RFC 3161 anchor over chain link, Merkle
sign + chain Merkle(receipt heads) root, timestamp
Two independent chains: the client signs and links each record (prev_hash), and the
server independently re-chains records in arrival order. A coherent re-chain by a malicious
sink is still caught by the client chain; a client edit is caught by the server's stored
bytes. Anchoring binds the whole thing to a trusted external time.
Quick start: guard your coding agent (30 seconds, no code)
Repeated from the top for readers who arrived here from the table of contents. Either install
the Claude Code plugin (/plugin marketplace add pofky/provenrail,
then /plugin install provenrail-guard@provenrail), or wire the hooks directly:
uv tool install provenrail
pr quickstart # local sink, no account, nothing leaves your machine
pr guard install # arms destructive + secrets + production, installs Claude Code hooks
From your next Claude Code session in that folder:
rm -rf,terraform destroy,git push --force,DROP TABLE,chmod 777and leaked API keys are blocked before they run, with the rule that fired shown to the agent.- An action that needs a human (touching
.env, a deploy, a migration) becomes a permission prompt, and your approval is recorded as human oversight rather than being blocked outright. - Every decision is Ed25519-signed and hash-chained.
pr guard status # what is armed, and what it blocked
pr guard receipt # export the proof, then verify it yourself
The verdict is computed offline, before anything touches the network, so a sink that is down can never turn a deny into an allow. If a decision cannot be recorded it is journalled locally and reported as unsigned and pending, never as proof.
Honest scope: this covers tool calls Claude Code routes through its hooks. It cannot constrain a process that never calls them. Other agent hosts are not claimed until their hook contract has been read and tested.
Quick start: record your own agent
Simplest possible: one command to set up, two lines in your code.
pip install -e ".[anchor]"
pr quickstart # starts a local sink and writes .provenrail.json (no tokens to copy)
import provenrail as fr
with fr.record("my-agent"):
... # your agent runs; model and tool calls are captured automatically
That is the whole setup. fr.record(...) provisions a stream, builds the recorder, opens a
signed session, and seals + drains it off-box on exit. Connection details come from
.provenrail.json (or env vars, or fr.configure(...)), so your code carries no URLs or
tokens. A decorator form exists too: @fr.recorded("nightly-job"). Stop the local sink with
pr quickstart --stop. Point at your own or a hosted sink with pr quickstart --url <URL>.
Run your agent as many times as you like: each run becomes its own sealed session on the same
stream, and one export verifies them all. The first run creates .provenrail.key, the device
signing key reused by every later run; keep it out of version control (gitignore it next to
.provenrail.json). Losing it only means future runs sign under a new identity.
TypeScript / Node
The same capture is available for TypeScript agents (Node 20+), in sdk-js/:
npm install provenrail
import { record } from "provenrail";
await record("my-agent", async (pr) => {
await pr.recordModelCall("openai", "gpt-5", { prompt }, out, { usage });
});
A run recorded in TypeScript is byte-for-byte compatible with one recorded in Python: the same sink
accepts it and the same two verifiers (pr verify and the in-browser verify.js) prove it. This is
enforced by a cross-language test (tests/test_js_sdk.py): records signed by the JS SDK are verified
by the Python verifier and the JavaScript verifier, and tampering is caught by both.
Other entry points:
pr demo # self-contained: records a session, anchors it, writes bundle.json + pin.json
pr verify bundle.json --pin pin.json # verify, trusting neither agent nor sink
pr report --regime eu-ai-act bundle.json --md # regulatory attestation
pr diff run-a.json run-b.json # diff two runs with provable fidelity
fr serve --anchor rfc3161 # run the sink yourself (real trusted timestamps)
pr sidecar --upstream https://api.openai.com # out-of-process capture proxy (point base_url here)
pr witness --log <origin>=<pubkey> # run an independent witness on separate infrastructure
# or: docker compose up
Out-of-process capture (harder to skip)
pr sidecar --upstream https://api.openai.com --port 8788
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8788") # the only change in your code
Every model call now flows through a recorder in a separate process. Lock outbound model egress
to the sidecar (firewall / network policy) and capture becomes mandatory rather than a default.
Add --fail-closed to refuse any call that cannot be recorded.
Drop-in capture (one line per SDK)
from provenrail.integrations import instrument_openai, instrument_anthropic, instrument_mcp
instrument_openai(openai_client, fr) # every model call now captured automatically
instrument_anthropic(anthropic_client, fr)
instrument_mcp(mcp_session, fr) # every MCP call_tool captured as an mcp_call
from provenrail.ingest_client import provision_stream
from provenrail.sdk import FlightRecorder
prov = provision_stream("http://127.0.0.1:8000", label="my-agent")
fr = FlightRecorder("http://127.0.0.1:8000", prov["write_token"], prov["stream_id"])
@fr.tool("search")
def search(q): ...
with fr.session({"agent": "demo"}):
fr.record_model_call("anthropic", "claude", request="...", response="...")
search("hello") # captured at the tool-dispatch boundary
fr.record_decision("ship it")
Share a read-only proof: GET /share/<share_token>. Export + verify:
curl -H "Authorization: Bearer $READ_TOKEN" \
http://127.0.0.1:8000/v1/streams/$STREAM/export > bundle.json
pr verify bundle.json
Privacy default is store-hash-not-content: prompts and outputs are SHA-256 hashed, not
stored, unless you pass capture_content=True. Tokens are scoped strictly: write can only
append, read can only read, share is public read-only, and no delete scope exists.
What is in the box
Integrity core
- Capture SDK with sessions, heartbeats, store-hash-not-content default, a
@tooldecorator for the local tool-dispatch boundary a network proxy never sees, and a non-blocking async transport (background batched flush with retry and an ordered drain on session close, so capture never slows the agent). - Drop-in auto-instrumentation for the OpenAI and Anthropic SDKs (sync and async), a
LangChain / LangGraph callback handler, Agno tool hooks, Claude Agent SDK hooks, and MCP
(Model Context Protocol)
call_toolcapture so tool use through MCP is recorded at the dispatch boundary. - Append-only sink with an independent server receipt chain, account API keys, strictly scoped per-stream tokens (no delete scope), per-stream and signup rate limits, DoS caps, idempotent ingest, WAL concurrency, and automatic anchoring on a schedule.
- Client-side pin: a signed checkpoint the agent keeps, so
pr verify --pindetects a malicious sink truncating the tail. - RFC 3161 trusted-timestamp anchoring with full verification: the standalone
pr verifychecks the CMS signature and certificate chain against a bundled TSA root, trusting neither the agent nor the sink.
Daily-use surface (the dashboard makes the integrity worth opening every day)
- Run Explorer at
/app: an authenticated dashboard over every stream, session, and event, with live integrity verdicts, token and estimated-cost analytics rolled up from captured usage, an expandable per-event timeline, and a live-session indicator. Mobile-first. - Integrity alerting: subscribe webhooks (
/v1/webhooks, or the Alerts tab) and get an HMAC-signed POST the moment a stream stops verifying (integrity.tampered), recovers, or lands its first trusted anchor.
Trust and compliance surface
- Hosted verifier at
/verify: anyone can drop a bundle and re-run verification with no install and no account. It verifies FULLY CLIENT-SIDE using a second, independent JavaScript verifier (web/verify.js, WebCrypto), so you trust not even our server; it is conformance-tested to agree byte-for-byte withpr verify. - Embeddable live badge:
<img src=".../badge/<share_token>.svg">re-verifies on every load and turns amber or red if the record ever stops verifying. - One-click evidence pack (
fr pack, or/v1/streams/{id}/evidence): a self-contained, reproducible ZIP with the bundle, a regime attestation (EU AI Act Art. 12 / HIPAA 164.312(b) / generic), a verification guide, and a SHA-256 manifest, for an auditor. - SIEM export (
/v1/streams/{id}/export.ndjson): one flattened, hash-linked record per line for Splunk / Elastic / Datadog, content-hashed by default so it does not leak prompts. - Premium, mobile-first, shareable proof page with a live verified badge.
frCLI (serve / demo / verify / report / pack), Dockerfile, CI, ruff-clean source, a marketing site inweb/, and a formalSPEC.mdso anyone can build an independent verifier.
Specification
The evidence format and the exact verification algorithm are in SPEC.md. A clean
pr verify recomputes every hash, signature, chain link, Merkle root, and timestamp from
scratch; it never trusts a derived field in the bundle.
Status
244 tests passing including hermetic real-RFC-3161 verification with tamper cases
(content edit, deletion, coherent malicious re-chain, backdating, tail truncation),
accounts/auth, token revocation and expiry, rate limiting, auto-anchoring, async transport,
Unicode NFC canonicalization, the Run Explorer dashboard and analytics, cost estimation,
integrity alerting, the hosted verifier and embeddable badge, evidence packs, SIEM NDJSON
export, MCP capture, and a JavaScript verifier conformance-tested against the Python one.
Plus committed-policy verification (the active guardrails are signed into the session and
re-checked offline), richer guardrail rules (content gates and per-session limits), and heuristic
coherence signals in both the verifier and the dashboard. The public transparency / witness
log is shipped: per-account RFC 6962 Merkle log over
the anchor receipts, inclusion and consistency proofs, C2SP checkpoint / signed-note /
cosignature wire formats, a reference witness plus a C2SP witness HTTP client with split-view
and rollback alarms, public /v1/tlog/{account}/checkpoint|inclusion|consistency endpoints,
verifier steps 7 to 8 in both Python and JavaScript (conformance-tested on witnessed,
unwitnessed, and forged-proof bundles), and a green-witnessed / amber-proofs badge axis.
Enterprise and forward-looking layers are shipped too: RBAC (org members, least-privilege
owner/admin/member/viewer roles, per-member keys), OIDC single sign-on (server/sso.py, strict
RS256/EdDSA ID-token validation with JIT provisioning), a hash-chained tamper-evident access log
(audit-of-the-audit), an agent identity / KYA registry (the verifier confirms the device key is
the one registered for the agent), an active policy / guardrail layer (declarative deny /
require-oversight rules and a per-session spend cap enforced at the dispatch boundary, with the
decision recorded in the signed chain), and run replay/diff with provable fidelity (pr diff,
diff computed over verified bundles). Dashboard and pages verified end to end in a real browser
(dark / light / mobile), including fully client-side verification.
Selective-disclosure redaction reconciles an immutable trail with the right to erasure: a
sensitive field is recorded as a salted commitment (never cleartext at the sink), the opening is
held only by the operator, and you can later disclose it to an auditor (any verifier confirms it
opens the commitment) or erase it permanently (destroy the opening; the field becomes
unrecoverable while the record stays tamper-evident and verifiable). Wrap a value with
provenrail.redactable(...); check disclosures with pr verify --openings or pr disclose.
Both the Python and JavaScript verifiers recompute the commitment identically (conformance-tested).
See SPEC section 17.
Independently-verifiable credibility, auditor artifacts, and trust robustness are deeper too:
frozen conformance vectors (tests/vectors/, with a documented manifest) let anyone validate
their own verifier against the same golden bundles; an auditor-grade verification report
(pr report --html, also folded into the evidence pack as report.html) renders the full verdict,
findings, trusted-time and witness status, committed-policy result, coherence signals, and a
chronological timeline as a printable document; and TSA trust is now operator-extensible
(pr verify --tsa-root host=cert.pem, trust.add_root(...)) with a MultiTSAAnchor that fails
over across timestampers for availability.
Guardrails are configurable without touching agent code and alert in real time: enable
prebuilt rule packs by name ({"policy": {"use": ["destructive", "secrets", "money"]}},
seven packs covering destructive tools, leaked credentials, money movement, production
changes, privilege escalation, exfiltration shapes, and per-session blast-radius caps; list
them with pr rules, check which would match your actual tool names with
pr rules --check bundle.json), or declare a
policy block in .provenrail.json (deny / require_oversight / limit rules with glob and regex
matching, plus a session spend cap) and the deployment owner sets the rules even if someone else
wrote the agent. A malformed policy is rejected loudly rather than ignored, because a typo that
silently disables a guardrail leaves you believing you are protected when you are not. When a rule
blocks an action, a policy.denied webhook fires at ingest, in the same moment the record
lands, not on the anchor schedule; delivery is HMAC-signed and runs off the request path so a slow
endpoint of yours can never slow the agent. Review afterwards with pr risk bundle.json (exits
non-zero if anything was blocked, so CI can gate on it), the dashboard's Blocked tile and red
timeline rows, or the NDJSON SIEM export. Nothing is suspicious by default: there is no built-in
threat library and no anomaly detection, so with no policy declared nothing is ever blocked.
The active policy / guardrail layer is deeper now: the policy is committed into the signed
session-start record (so a verifier proves which guardrails were in force and that no executed
action violates a re-verifiable deny / limit / spend rule, SPEC section 13), with content gates
(arg_contains regex over argument text, for secret / PII blocking) and per-session call limits
(max_per_session). The verifier and dashboard also surface heuristic coherence signals
(clock skew, time gaps, missing usage, no-governance runs, self-inconsistent seal) that point a
reviewer at structural oddities without ever changing the cryptographic verdict (SPEC section 14).
Hosted-readiness and out-of-process layers are shipped too: a persisted server identity (the
transparency-log and KYA-registry keys live in the DB so a sink reboot keeps the same public
keys an auditor pinned), usage metering with per-account monthly plan quotas and a GET /v1/usage
billing surface (server/plans.py), a standalone out-of-process witness (pr witness,
server/witness.py) that speaks the C2SP add-checkpoint protocol so a hosted sink can earn the
witnessed-green path against a witness on independent infrastructure, and the capture sidecar
(pr sidecar, sidecar.py) that records model calls from a process the agent does not control.
333 tests, ruff clean, zero em/en glyphs.
Not yet shipped: cross-account WORM storage, a Go verifier (no toolchain here yet), SAML and a
browser SSO redirect flow, multi-TSA trust roots, and the actual cloud deployment plus payment
integration (the metering and plan plumbing is in place; wiring a real provider and infra is an
ops step, not code shippable here). Roadmap in STRATEGY.md; design in
../DESIGN-agent-audit-trail.md section 11.
License
Open-core, dual-licensed (see LICENSING.md):
- MIT for the client SDK, the
pr-verifyverifier, the bundle format andSPEC.md, and the web verifier. Verify any Provenrail record with a permissively-licensed tool, no strings. - AGPL-3.0-or-later for the server / sink (
src/provenrail/server/). Self-host freely; a commercial license is available for proprietary or closed-source-SaaS use (hello@provenrail.com).
Provenrail is evidence tooling, not legal advice or a compliance guarantee; completeness is never
claimed. See DISCLAIMER.md. Business model in BUSINESS.md, moat analysis in MOAT.md,
regulatory mapping in COMPLIANCE.md.
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 provenrail-0.2.14.tar.gz.
File metadata
- Download URL: provenrail-0.2.14.tar.gz
- Upload date:
- Size: 291.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b351f42ca256008c5f5993534a18184ac86015c2981008f75def5e4988af7a90
|
|
| MD5 |
e12905c564de80d741f24d0977a60a2e
|
|
| BLAKE2b-256 |
b806988bf45130eb7d093c428d12972c45232d2798a4672ad98918adae951535
|
File details
Details for the file provenrail-0.2.14-py3-none-any.whl.
File metadata
- Download URL: provenrail-0.2.14-py3-none-any.whl
- Upload date:
- Size: 223.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a90c434e4117d1bce30ce72c8ba36dccb5a37bb245773ad24bad736be1eace0c
|
|
| MD5 |
c0f2b5f0d18387af0bf319efc2c878ba
|
|
| BLAKE2b-256 |
289994a06c1453abddce32eb952176c1d60d9b90f7148728441f9074089ab66c
|