Skip to content
Tenzro
Documentation menu
Build and operate

SDKs

The TypeScript, Rust and Python SDKs for Tenzro Network, hardware device keys, and how signing and credentials work.

Tenzro publishes three first-party SDKs with the same shape: tenzro-sdk for TypeScript, tenzro-sdk for Rust and tenzro for Python. Each wraps the JSON-RPC surface of a node with typed clients per area (wallet, identity, inference, payments, settlement, storage, agents and more), and each includes a gateway that can call any method the node serves.

All SDKs are open source under Apache 2.0.

Install

What a registry serves today differs from the source, and this page documents the source (version 0.9.0 of the TypeScript and Rust SDKs, 0.2.0 of the Python SDK, in sdk/ of tenzro-network). A call added after the published release is only in the source build.

bash
# TypeScript (Node 18+, browsers, edge runtimes): published, latest 0.7.0
npm install tenzro-sdk

# Rust: published, latest 0.7.0
cargo add tenzro-sdk

# Python (3.10+): not published to PyPI yet; build from the repository
git clone https://github.com/tenzro/tenzro-network.git
pip install ./tenzro-network/sdk/tenzro-py
PackageWherePublishedIn the repository
tenzro-sdk (TypeScript)npm0.7.00.9.0
@tenzro/device-key (TPM / Secure Enclave keys for Node.js)npm0.1.1in the repository
@tenzro/inject (injected window.tenzro provider)npmnot publishedsdk/tenzro-inject
tenzro-sdk (Rust)crates.io0.7.00.9.0
tenzro (Python)PyPInot published0.2.0 (sdk/tenzro-py)
tenzro-device-key (Python)PyPInot publishedin the repository

Signing needs hardware: on a server or desktop the SDKs sign with the machine's TPM 2.0 or Secure Enclave through @tenzro/device-key (TypeScript) or tenzro-device-key (Python); in a browser they use a passkey. Without one they refuse to sign. Every method the SDKs bind is documented with its params, result and errors in the generated JSON-RPC reference.

Connect

The TypeScript client takes an endpoint for JSON-RPC and an apiEndpoint for the Web API (health and status).

ts
import { TenzroClient } from "tenzro-sdk";

const client = new TenzroClient({
  endpoint: "https://rpc.tenzro.com",
  apiEndpoint: "https://api.tenzro.com",
});

const height = await client.getBlockNumber();
const finalized = await client.getFinalizedBlock();
const balance = await client.getBalance("0xYourAddress"); // bigint, in wei

If you leave out apiEndpoint, the client derives it from endpoint for the public endpoints and for a local node (localhost:8545 maps to localhost:8080).

The Rust client is built from an SdkConfig:

rust
use tenzro_sdk::{TenzroClient, config::SdkConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = SdkConfig::builder()
        .endpoint("https://rpc.tenzro.com")
        .timeout(30_000)
        .max_retries(3)
        .build()?;
    let client = TenzroClient::connect(config).await?;

    println!("height: {}", client.block_number().await?);
    let models = client.inference().list_models().await?;
    println!("{} models", models.len());
    Ok(())
}

TenzroClient::new("https://rpc.tenzro.com") is the one-line form with default settings.

Run inference

List models and send a chat request through the provider client:

ts
const models = await client.inference.listModels();

const reply = await client.provider.chat("<model id>", [
  { role: "user", content: "Summarize the latest block in one sentence." },
]);
rust
use tenzro_sdk::provider::ChatMessage;

let reply = client
    .provider()
    .chat("<model id>", vec![ChatMessage {
        role: "user".into(),
        content: "Summarize the latest block in one sentence.".into(),
        ..Default::default()
    }])
    .await?;

If you do not want to pick a model yourself, client.inference.routeIntent(...) resolves a use case and budget to a model, and chatByIntent(...) runs the request in one call.

The node also serves OpenAI-compatible routes (/v1/chat/completions, /v1/responses, /v1/embeddings, /v1/audio/transcriptions, /v1/audio/speech and more), so any OpenAI client library works when you point its base URL at https://rpc.tenzro.com/v1. See OpenAI-compatible APIs.

Signing and credentials

Tenzro Network is closed by default: anything that moves money or changes state needs a signature from the account that pays. The SDKs never hold your keys. You pass in a signer backed by a passkey, a TPM 2.0 or a Secure Enclave key, and the SDK builds the canonical transaction hash, asks the signer for the composite hybrid signature (classical plus ML-DSA-65) and submits the signed transaction with eth_sendRawTransaction.

ts
// signer: a HybridSigner backed by your passkey or device key
const txHash = await client.wallet.sendSelfCustody({
  signer,
  to: "0xRecipient",
  value: 1_000_000_000_000_000_000n, // 1 TNZO in wei
});
rust
// signer: Arc<dyn HybridSigner> backed by your device key
let tx_hash = client
    .wallet()
    .send_self_custody(&signer, recipient, 1_000_000_000_000_000_000u128)
    .await?;

In the browser, a passkey enrols with client.passkeyRpc.enroll and signs transactions from its account as a HybridSigner whose P-256 leg is its WebAuthn assertion (user verification required). See Hardware-rooted keys and Console and passkey wallet.

Other credentials are read from the environment in server runtimes, so they stay out of your code:

VariableHeader sentUsed for
TENZRO_BEARER_JWT and TENZRO_DPOP_PROOFAuthorization: DPoP ... and DPoPOAuth 2.1 sessions bound to a DPoP key
TENZRO_ADMIN_TOKENX-Tenzro-Admin-TokenOperator-only methods on your own node

AuthClient (client.auth in TypeScript, client.auth() in Rust) handles onboarding for humans, delegated agents and autonomous agents and returns DPoP-bound tokens. HardwareDpopProver mints the DPoP proofs with the machine's hardware P-256 key, and jwkThumbprint computes the key's thumbprint. See RPC access.

The ML-DSA-65 leg is mandatory for account transactions. An account signs nothing until it has enrolled its ML-DSA-65 key, and a new account's first transaction is the enrollment. client.accountKeys().sendEnrolling in the TypeScript SDK, and send_enrolling in the Rust and Python SDKs, send a transaction and, if the node answers that no key is enrolled, enrol the hardware's key and send it again. The CLI equivalent is tenzro pq-hybrid enroll. See Cryptography.

Call any method

Typed clients cover the common paths. Everything else is reachable through the gateway, which reads the method list from the node itself, so a newer node simply reports more:

ts
const dir = await client.gateway.methods({ contains: "forecast" });
for (const m of dir.methods) console.log(m.method, m.gate, m.class);

const result = await client.gateway.call("tenzro_getSupplyMetrics", {});
rust
let dir = client.gateway().methods(None, Some("forecast")).await?;
let result = client.gateway().call("tenzro_getSupplyMetrics", serde_json::json!({})).await?;

The gateway runs through the same access checks as a direct JSON-RPC call. It reaches exactly what your credentials allow.

Choosing

  • TypeScript for web and desktop apps, agents, serverless and edge functions.
  • Rust for daemons, high-volume services and anything that embeds a node.
  • The CLI for scripts and operator tasks.

For every client and method, see the SDK reference.

ExploreJoin the network