Install
Tenbri ships one version everywhere. Pick what you need:
| You want to | Install |
|---|---|
| Record and verify from a shell, a script or CI | the tenbri CLI |
| Let agents in any language write over HTTP or gRPC | tenbrid |
| Record from a Python agent | the tenbri Python package |
| Record from a Node or TypeScript agent | the tenbri npm package |
| Record from a Rust program | the Rust crates |
| Verify a stream in the browser | @tenbri/wasm |
| Record a Claude, OpenAI or LangGraph agent with one line | an adapter |
The Python and Node packages and the CLI write the same streams with the same Rust core: a stream
written from Python verifies with tenbri verify, and the other way round.
The CLI
Section titled “The CLI”The CLI is the tenbri binary: init, append, verify, query, tail, checkpoint,
receipt, keys, erase, index and schema check.
Needs Rust 1.85 or later. The crate is tenbri-cli; the binary it installs is tenbri.
Pre-releases only install when you name the version:
cargo install tenbri-cli --version 0.1.0-beta.1Each GitHub release carries tenbri and tenbrid for Linux x86_64 and arm64 (static musl),
macOS x86_64 and arm64, and Windows x86_64, with a SHA256SUMS file, an SBOM and a Sigstore
build-provenance attestation. On macOS (Apple silicon):
VERSION=0.1.0-beta.1TARGET=aarch64-apple-darwin # or x86_64-apple-darwin, x86_64-unknown-linux-musl, aarch64-unknown-linux-muslcurl -fLO "https://github.com/tenbri/tenbri/releases/download/v$VERSION/tenbri-$VERSION-$TARGET.tar.gz"tar -xzf "tenbri-$VERSION-$TARGET.tar.gz"sudo install "tenbri-$VERSION-$TARGET/tenbri" "tenbri-$VERSION-$TARGET/tenbrid" /usr/local/bin/Windows takes tenbri-$VERSION-x86_64-pc-windows-msvc.zip. To check where an archive came from,
gh attestation verify tenbri-$VERSION-$TARGET.tar.gz --repo tenbri/tenbri.
ghcr.io/tenbri/tenbri holds the same static binaries for amd64 and arm64. Its entrypoint is
tenbri; tenbrid is next to it in /usr/local/bin. Pre-releases get their exact tag only:
docker run --rm ghcr.io/tenbri/tenbri:0.1.0-beta.1 --versiongit clone https://github.com/tenbri/tenbricd tenbricargo install --path crates/tenbri-cliCheck the install:
tenbri --versiontenbri 0.1.0-beta.1Cargo features
Section titled “Cargo features”The CLI turns on two features by default:
| Feature | What it adds |
|---|---|
keychain |
tenbri init --keychain: keep secret keys in the OS keychain instead of key files. macOS Keychain and Windows Credential Manager only; on other systems the keychain backend fails and you use key files. |
s3 |
Streams in object stores: s3://bucket/<root>/<stream> (AWS S3, MinIO) and r2://bucket/<root>/<stream> (Cloudflare R2). |
To build without the keychain:
cargo install tenbri-cli --version 0.1.0-beta.1 --no-default-features --features s3Object stores take their credentials from the environment, as the providers’ own tools read them:
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION and the other AWS_* variables;
AWS_ENDPOINT_URL for MinIO and other S3-compatible stores (with AWS_ALLOW_HTTP=true for plain
HTTP); CLOUDFLARE_ACCOUNT_ID (or AWS_ENDPOINT_URL) for R2. The Python and Node packages read the
same variables. See Streams on object storage.
The tenbrid server
Section titled “The tenbrid server”tenbrid serves the REST and gRPC API, so agents in any language can write to a stream. The
release binaries and the container above include it. With cargo, it comes from the
tenbri-server crate:
cargo install tenbri-server --version 0.1.0-beta.1From a checkout, cargo build -p tenbri-server --release builds target/release/tenbrid.
It reads a TOML configuration file: --config <FILE>, else $TENBRI_CONFIG, else tenbrid.toml
in the working directory. The file must exist. A minimal one:
keys = "keys" # default: ~/.config/tenbri/keys
[streams.support] # the stream's name in URLs (its tnb1: id works too)path = "audit/support" # or s3://bucket/prefix, r2://bucket/prefixkey = "sha256:..." # pin; default: the stream key held in `keys`
[[tokens]] # make one with `tenbrid token`sha256 = "sha256:..." # SHA-256 of the bearer token; the token is never storedprincipal = { kind = "agent", id = "support-assistant" }Relative paths are relative to the file’s directory. These settings have defaults and an environment variable that overrides the file:
| Setting | Default | Environment |
|---|---|---|
listen (REST) |
127.0.0.1:7070 |
TENBRI_LISTEN |
grpc_listen |
127.0.0.1:7071 |
TENBRI_GRPC_LISTEN |
keys |
~/.config/tenbri/keys |
TENBRI_KEYS |
schema (action schemas) |
none: nothing is checked | TENBRI_SCHEMA |
queue (appends waiting per stream) |
10000 |
TENBRI_QUEUE |
deadline_ms (how long an append waits for the queue) |
2000 |
TENBRI_DEADLINE_MS |
Make a bearer token, then start the server:
tenbrid tokentenbrid --config tenbrid.tomltenbrid token prints the token once (it is not stored) and the [[tokens]] entry that
configures it by its hash. tenbrid serves until SIGINT or SIGTERM, then stops accepting requests
and waits for the writes in flight. It exits 0 after a clean shutdown and 2 on a configuration or
startup error. Walk through it in tenbrid over REST; the routes are
in the API reference.
Python
Section titled “Python”The tenbri package is an abi3 wheel for Python 3.10 and later, on Linux (manylinux and
musllinux, x86_64 and aarch64), macOS and Windows. Nothing to compile.
pip install --pre tenbriuv add --prerelease=allow tenbripoetry add --allow-prereleases tenbriCheck it:
python -c "import tenbri; print(tenbri.__version__)"0.1.0-beta.1Tenbri blocks (it releases the GIL); AsyncTenbri has the same methods as coroutines for
asyncio. Start with the quickstart or
Write from code.
Node and TypeScript
Section titled “Node and TypeScript”The tenbri package is a native addon (napi-rs) for Node 20 and later on Linux (glibc and musl),
macOS and Windows, with TypeScript types. npm installs the prebuilt binary for your platform; nothing
to compile.
npm install tenbri@nextpnpm add tenbri@nextyarn add tenbri@nextbun add tenbri@nextCheck it:
node -e "console.log(require('tenbri').version)"0.1.0-beta.1Rust crates
Section titled “Rust crates”tenbri-writer writes and verifies streams; tenbri-core holds the entry types, tenbri-keys the
keys and tenbri-store local and object storage:
cargo add tenbri-core@0.1.0-beta.1 tenbri-keys@0.1.0-beta.1 tenbri-store@0.1.0-beta.1 tenbri-writer@0.1.0-beta.1cargo add tokio --features macros,rtAdd --features s3 to tenbri-store for s3:// and r2:// streams. The tenbri facade crate
is a placeholder that will re-export these; depend on the crates above for now. See
Write from code.
WASM verifier
Section titled “WASM verifier”@tenbri/wasm is the offline verifier compiled to WebAssembly, about 150 KB gzipped. It checks an
export of a stream against a pinned key, in the browser or in Node. It cannot write.
npm install @tenbri/wasm@nextpnpm add @tenbri/wasm@nextyarn add @tenbri/wasm@nextbun add @tenbri/wasm@nextAn export is the stream’s batch files concatenated in order. In Node:
cat audit/support/batches/*.jsonl > support.jsonlimport { readFileSync } from 'node:fs'import { createRequire } from 'node:module'import { initSync, verifyExport } from '@tenbri/wasm'
const require = createRequire(import.meta.url)initSync({ module: readFileSync(require.resolve('@tenbri/wasm/pkg/tenbri_wasm_bg.wasm')) })console.log(verifyExport(readFileSync('support.jsonl', 'utf8'), process.argv[2]))node verify.mjs sha256:13410a435d5a70cc7f96ae05209410bb37cd301124e0719229b3184530789b61{ broken: null, checkpoint: null, entries: 4, ok: true, pinnedKey: 'sha256:13410a435d5a70cc7f96ae05209410bb37cd301124e0719229b3184530789b61', stream: 'tnb1:13410a435d5a70cc7f96ae05209410bb'}In the browser, await init() fetches tenbri_wasm_bg.wasm next to the module, then
verifyExport(jsonl, key) works the same way.
Framework adapters
Section titled “Framework adapters”An adapter records an agent framework’s tool calls, approvals and handoffs as Tenbri decisions,
with one line at agent setup. Each installs the matching tenbri package with it.
| Framework | Python | npm |
|---|---|---|
| Claude Agent SDK | pip install --pre tenbri-claude-agent-sdk |
npm install @tenbri/claude-agent-sdk tenbri@next @anthropic-ai/claude-agent-sdk |
| OpenAI Agents SDK | pip install --pre tenbri-openai-agents |
npm install @tenbri/openai-agents@next tenbri@next |
| LangGraph and LangChain | pip install --pre tenbri-langgraph |
npm install tenbri@next @tenbri/langgraph@next |
With uv, poetry, pnpm, yarn or bun, use the same names with the pre-release flags shown above.
All adapters: Adapters.
Where keys live
Section titled “Where keys live”Every writer (the CLI, tenbrid, the Python and Node packages) keeps signing keys in a keys
directory, chosen in this order:
--keys <DIR>(a global CLI flag), orkeys=/keys:when you open a stream from code;$TENBRI_KEYS;$XDG_CONFIG_HOME/tenbri/keys, else~/.config/tenbri/keys(%APPDATA%\tenbri\keyson Windows).
Each stream gets its own subdirectory, named after the stream id with : written as -
(tnb1-5db31441baa2d080326d9f6c83c95319). Directories are created mode 0700 and key files
0600 on Unix. Without the stream’s keys, a stream opens read-only: verify and query work, writes
fail.
The keys directory and a local stream directory must not contain each other: tenbri init refuses
that layout. Whoever can read or copy the log must not be able to sign as it. See
Keys.
Next: the quickstart.
