Quickstart
Install the SDK, wrap your agent, and verify the result yourself. No account is required to use the open-source software. Python and TypeScript are byte-for-byte compatible.
The CLI (pr quickstart, verifier, and sink). Recommended: install with
uv, which brings its own pinned Python, so a later
brew upgrade or system Python change cannot orphan it:
uv tool install provenrail
No uv yet? One line installs it: curl -LsSf https://astral.sh/uv/install.sh | sh.
Prefer pip? It works, but install it inside a virtualenv, not a system or Homebrew Python.
A global pip install breaks the day Homebrew upgrades Python (the package lands in the
old interpreter's site-packages). If that happens, recover with
pipx reinstall provenrail or switch to the uv command above:
python3 -m venv .venv && source .venv/bin/activate
pip install provenrail
Using the SDK from your own project? Add it to that project's environment, again not system Python:
uv add provenrail # or: pip install provenrail (venv active)
TypeScript / Node 20+ (recording SDK):
npm install provenrail
One command sets up a local sink and writes .provenrail.json, so your code carries no URLs or tokens:
pr quickstart # starts a local sink + writes config
Then two lines in your code:
import provenrail as fr
with fr.record("my-agent"):
... # your agent runs; model and tool calls are captured
fr.record(...) provisions a stream, opens a signed session, and seals and drains it
off-box when the block exits. A decorator form exists too: @fr.recorded("nightly-job").
Stop the local sink with pr quickstart --stop; point at your own sink with
pr quickstart --url <URL>.
from provenrail.integrations import instrument_openai, instrument_anthropic, instrument_mcp
instrument_openai(openai_client, fr) # every model call captured
instrument_anthropic(anthropic_client, fr)
instrument_mcp(mcp_session, fr) # every MCP call_tool captured
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 prove it. Node 20+ is required (WebCrypto Ed25519).
Verification trusts neither the agent nor the sink. Anyone can run it, with no account:
pr verify bundle.json --pin pin.json
To verify your own recorded run after pr quickstart, export it from the
local sink first (this uses the read token quickstart saves), then verify the bundle:
pr export my-run.json # pulls your sealed run out of the sink
pr verify my-run.json # recompute everything; trust nobody
Or verify in your browser, with the bundle never leaving your device: provenrail.com/verify. Try the live verified demo or watch it catch a tampered run.
pr quickstart # local sink + config, zero tokens
pr demo # records a session, anchors it, writes bundle.json + pin.json
pr export my-run.json # export your own recorded run from the local sink
pr verify bundle.json --pin pin.json # verify, trusting nobody
pr report --regime eu-ai-act bundle.json --md # regulatory attestation
pr pack bundle.json # self-contained evidence pack (zip) for auditors
pr diff run-a.json run-b.json # diff two runs with provable fidelity
pr ots-verify proof.ots --data-sha256 H # verify a Bitcoin (OpenTimestamps) proof
pr serve --anchor rfc3161 # run the sink yourself (real trusted time)
pr sidecar --upstream https://api.openai.com # out-of-process capture proxy
pr witness --log <origin>=<pubkey> # independent witness on separate infra
The sink is the append-only server that receives records. You run it; your records never reach us. For real third-party trusted time, anchor with RFC 3161:
pr serve --anchor rfc3161 --tsa https://freetsa.org/tsr
Or with Docker:
docker compose up
pr sidecar as an outbound proxy and
lock model egress to it, so capture is mandatory rather than a default. Add --fail-closed
to refuse any call that cannot be recorded.On the Team plan you can invite up to 10 teammates with role-based access and connect your identity provider, so staff sign in with the IdP you already use. Everything is account-authenticated against your own sink; the integrity guarantee is unchanged on every plan.
Invite a teammate and they get their own key (shown once). Four least-privilege roles: owner (billing and everything), admin (manage streams, members, webhooks), member (run agents, export their own streams), and viewer (read only). An actor can only grant roles at or below its own.
curl -X POST $URL/v1/members -H "Authorization: Bearer $ACCOUNT_KEY" \
-d '{"role":"admin","email":"dana@acme.com"}'
{
"member_id": "mbr_9cf5...",
"role": "admin",
"api_key": "pr_mk_rPk8...", # shown once, store it now
"note": "store this key now; it is shown only once"
}
SSO is API-first: you configure your IdP once, then your staff present an ID token from that IdP to receive a session key. Provenrail makes no network call (the JWKS is pinned out of band), and validation is strict, only RS256 and EdDSA are accepted, with issuer, audience and expiry all checked. New users are provisioned just-in-time at the default role.
# 1. Owner configures the org IdP once (issuer, audience, pinned JWKS, default role)
curl -X PUT $URL/v1/sso/config -H "Authorization: Bearer $ACCOUNT_KEY" \
-d '{"issuer":"https://acme.okta.com","audience":"provenrail",
"jwks":{ ... },"default_role":"member","email_domain":"acme.com"}'
# 2. A teammate authenticates with your IdP, then exchanges the ID token for a member key
curl -X POST $URL/v1/sso/login -d '{"id_token":"eyJhbGciOiJSUzI1Ni..."}'
{
"member_id": "mbr_9ca5...",
"role": "member", # JIT-provisioned at the default role
"api_key": "pr_mk_lAFz..."
}
kid; alg: none and HMAC algorithms are rejected, so
there is no algorithm-confusion bypass. An optional email_domain restricts which
addresses may provision. Every login is written to the tamper-evident audit log.Provenrail is open-core and dual-licensed:
pr verify verifier, the
in-browser verifier, and the spec. Use them anywhere, including commercially.Get a commercial license key from your account, then activate it on your server:
pr activate prl_live_... # verifies offline, stores the key
pr serve # now runs at your licensed tier
Verification is fully offline: the package ships the Ed25519 public key and checks the signed key locally, so nothing phones home and a licensed build works air-gapped. The license is a commercial control, not DRM; the open-source integrity guarantee is identical on every tier.
The wire format and verification steps are a frozen, public specification, so a third party can write an independent verifier and check the same bundles. The in-browser verifier at /verify is a second, independent implementation of that spec, kept in lockstep with the Python one. Source code is open under the licenses above.