npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tenzro-sdk

v0.7.0

Published

Tenzro Network SDK — wallets, identity, agents, inference, bridge, crypto, TEE, ZK proofs, custody, and settlement

Readme

Tenzro SDK for TypeScript

npm License Docs

The official TypeScript/JavaScript SDK for Tenzro Network -- build AI-native applications with wallets, identity, agents, inference, cross-chain bridge, crypto, TEE, ZK proofs, and settlement.

Installation

npm install tenzro-sdk

Quick Start

import { TenzroClient, TESTNET_CONFIG } from "tenzro-sdk";

const client = new TenzroClient(TESTNET_CONFIG);

// Create wallet
const wallet = await client.wallet.createWallet();
console.log("Address:", wallet.address);

// Register identity
const identity = await client.identity.registerHuman("Alice");
console.log("DID:", identity.did);

// List AI models
const models = await client.inference.listModels();
console.log(`${models.length} models available`);

// Run inference
const result = await client.inference.request("gemma3-270m", "Hello!", 100);
console.log(result.output);

Browser-extension provider

Browser dApps can route SDK calls through window.tenzro (any EIP-6963-announcing Tenzro extension) instead of opening a direct fetch to the node. The extension owns auth (DPoP-bound JWT), session management (CAIP-25), and user confirmation:

import { TenzroClient, TenzroNotInstalledError } from "tenzro-sdk";

try {
  const client = await TenzroClient.fromInjected();
  const block = await client.getLatestBlock();
} catch (err) {
  if (err instanceof TenzroNotInstalledError) {
    showInstallCta();
  } else {
    throw err;
  }
}

fromInjected() discovers the Tenzro provider via EIP-6963 (default rdns: xyz.tenzro.wallet, override with the rdns option), wraps it in an Eip1193Transport, and returns a TenzroClient whose rpc.call(...) becomes provider.request(...). No extra dependency to install — the EIP-6963 listener is bundled in the SDK. Node consumers can ignore this entrypoint entirely.

Catch-up sync

A node lagging behind the network can pull batches of historical blocks via getBlockRange. The call returns up to 256 blocks per request along with a nextHeight + moreAvailable cursor so a sync loop steps over pruning gaps:

let cur = 0;
while (true) {
  const r = await client.getBlockRange(cur, cur + 255, 256);
  for (const b of r.blocks) {
    /* import block */
  }
  if (!r.moreAvailable) break;
  cur = r.nextHeight;
}

isSyncing() reports the live gap by comparing the local tip against peer-reported network tips (gossiped on tenzro/status); pair it with getBlockRange to drive a catch-up loop only when needed.

Transaction signing

Every Tenzro transaction is hybrid post-quantum signed: a classical Ed25519 signature and an ML-DSA-65 (FIPS 204) signature, both verified synchronously by the node against the canonical Transaction::hash() preimage (which commits to the PQ public key). An invalid or missing signature on either leg returns JSON-RPC error -32003.

Two supported flows:

  1. Atomic server-side sign + send (recommended). The SDK dispatches the request via tenzro_signAndSendTransaction. The node identifies the signing wallet from the ambient DPoP-bound bearer JWT, looks up the live nonce and gas price, constructs the hash preimage, signs both legs, verifies them, and submits to the mempool — all in one call. Private keys never travel over the wire. nonce, chainId, and gasPrice are optional; value accepts the alias amount for parity with the desktop and CLI clients. Self-sends (from === to) return a cannot transfer to self validation error.

    const txHash = await client.wallet.signAndSend({
      from: "0x...",
      to: "0x...",
      value: 1_000_000_000_000_000_000n,
      // nonce, chainId, gasPrice all optional — looked up live
    });

    client.sendTransaction(...) and client.wallet.signAndSend(...) are both thin wrappers over this RPC.

  2. Offline sign, then submit. Call tenzro_signTransaction to obtain {signature, public_key, pq_signature, pq_public_key, timestamp, tx_hash}, then resubmit later via eth_sendRawTransaction with all six fields intact. Use this for batched or air-gapped submission.

Wallet model

client.wallet.create() provisions a chain-agnostic 2-of-3 Ed25519 MPC wallet. Tenzro wallets are not per-chain — a single wallet projects into EVM, SVM, and Canton via the pointer-token model, so there is no chain parameter. VM-specific operations are exposed through client.token (crossVmTransfer, wrapTnzo); transfers to external chains use client.bridge (LayerZero V2, Chainlink CCIP), client.debridge, client.wormhole, or client.lifi.

client.getTransaction(hash) resolves from finalized storage first, then falls back to the consensus mempool — status is "pending" while the transaction is in-mempool and "finalized" once block-included, so callers polling immediately after broadcast can distinguish "not yet finalized" from "unknown hash" (the call returns null only when the hash is unknown to both storage and mempool).

Durable state

The node persists AI infrastructure to RocksDB and restores it on restart — SDK consumers see consistent state across node upgrades and reboots:

  • Model catalogModelRegistry writes ModelInfo records under info:<model_id> in CF_MODELS; models survive restart without re-registration.
  • Agent runtimeAgentRuntime persists RegisteredAgent, AgentLifecycleInfo, and parent→children spawn trees under agent:/lifecycle:/children: prefixes in CF_AGENTS. Terminated agents are retained for audit of state_history, registration_fee, and tenzro_did.
  • SwarmsSwarmManager persists SwarmState under swarm:<swarm_id> in CF_AGENTS with write-through on create, status transitions, and termination.
  • Wallets — FROST key shares persist across restarts when the host node is built with a KeystoreUnlocker (the source of the keystore password). This is a node-build concern, not an RPC-client one: a wallet created via createPasskeyWallet / tenzro_createWallet survives a node reboot only if the operator's node binary supplies an unlocker. Desktop hosts inject a biometric Secure-Enclave unlocker (macOS/iOS Touch ID, via the tenzro-device-key crate); headless hosts inject an env/file/KMS unlocker. Without one, the embedded node treats the wallet as ephemeral and recreates it each launch — the historical default. Cross-restart Secure Enclave persistence additionally requires the desktop app to ship a keychain-access-groups entitlement + provisioning profile.

AppClient (Developer Pattern)

You charge fiat on your own payment provider and settle the corresponding TNZO from your own app wallet — the network never holds custody of your processor keys or your funds. See Developer payments.

import { AppClient } from "tenzro-sdk";

const app = AppClient.connect("https://rpc.tenzro.xyz");

// Register the app in the on-chain registry (developer-signed DID envelope).
// The app wallet is your own TNZO treasury; margin is a pricing input, capped at 20%.
await app.registerApp(
  signer, // your EnvelopeSigner
  "my-app",
  "did:tenzro:machine:...", // developer DID that owns the app
  "0x<app-wallet-hex>",
  [{ keyId: "key-1", publicKey: enrolledEd25519Pubkey }],
  500, // margin_bps
  0n, // min_balance
  true, // active
);

// After your processor confirms the charge, sign a settlement authorization
// with one of your enrolled keys. Idempotent per (appId, externalRef).
const outcome = await app.settleAuthorized(settlementSigner, {
  appId: "my-app",
  chainId: 1337n,
  payerDid: "did:tenzro:human:...",
  amountTnzo: 10_000_000_000_000_000_000n, // 10 TNZO, base units
  externalRef: chargeId, // your PSP charge id
  nonce: crypto.getRandomValues(new Uint8Array(32)),
  expiry: BigInt(Date.now() + 60_000),
  keyId: "key-1",
});
// outcome.duplicate === true when a prior (appId, externalRef) replayed

Modules

| Module | Key Methods | | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | auth | onboardHuman(), onboardDelegatedAgent(), onboardAutonomousAgent(), revokeJwt(), revokeDid(), listPendingApprovals(), decideApproval(). When an agent's call exceeds its authority the node parks it and returns -32002 with the new record under data.approval_id. The controller reviews it with listPendingApprovals() and rules on it with decideApproval(), which takes an optional deny reason. The agent then retries the same call with approval_id in its params: the engine spends the approval against that exact action, so the retry executes instead of parking again. An approval covers only the action it was raised for — a retry carrying a different amount, counterparty, or action type parks again. A retry against a denied approval returns -32001 with the controller's reason verbatim in the message | | apiKey | create(), list(), revoke() on the operator plane; listMine(), revokeMine() on the subject plane. listMine() is the entitlement self-read — every row carries the scopes, tier ceiling, canton_networks, canton_user_id, and party-delegation arrays the node enforces, so a tenant can answer "what may I do here" without asking the operator. A row with a non-empty canton_networks but a null canton_user_id has node access without ledger access: it authenticates but is bound to no Canton party, so command submission is refused — either the operator reissues with a canton_user_id, after which the node mints the tenant JWT server-side, or the caller presents its own JWT via X-Canton-Auth. Canton authorization is per network: set canton_networks on the create params; a key naming none reaches no Canton ledger | | wallet | createWallet(), getBalance(), sendTransaction() | | passkeyRpc | Passkey-first wallet RPC mirroring the node's tenzro_*Passkey* / *Recovery* / *SessionKey* / *HardwareSigner* surface | | agentWallet | TenzroAgentWallet — the composite agent-wallet surface over passkey, bond, and Canton | | identity | registerHuman(), resolveDid(), setUsername() | | agent | register(name, creator, capabilities) (server-provisioned hybrid wallet), registerWithKeys(name, creator, capabilities, publicKey, pqPublicKey) (BYOK), sendMessage(from, to, message), sendMessageSigned({from, to, message, signature, pqSignature, messageType?, replyTo?}), spawnAgent(), createSwarm(), delegateTask() | | inference | listModels(), request(), estimateCost(), getProvenance(contentHash) (cached synthetic-content manifest, EU AI Act Art. 50(2)), intent routing routeIntent(params) / chatByIntent(params) / orchestrate(params) (resolve an intent to a model without naming one, resolve-and-run, or plan+run an ordered set of models/skills/tools/agent delegation for a goal). Accounting: getGeneration(id) reads back what one finished generation consumed and cost, keyed on the chatcmpl-… or request_id you already hold; listInferenceUsage({ modelId, provider }) returns matching records with both filters, that model's or provider's rollup with one, and the global rollup plus per-model and per-provider breakdowns with neither. Content-addressed weights: getModelHash(id) reads the canonical BLAKE3/SHA-256 record a fetcher verifies weights against before load, listModelHashes() lists every recorded hash, recordModelHash(id, files) anchors one (permissionless, first-recorder-wins) | | multimodal | Forecast, vision embed/similarity, text embedding, segmentation, detection, audio ASR, video embed as a dedicated client | | mediaGenInspection | Generative image and video, read-only: listCatalog(), quote(), listJobs(), getJob(), listWorkers(), getReceipt(), fetchOutput(), fetchLatent(), fetchInput(). Priced by the pixel-step (width × height × steps × frames) | | mediaGen | Generative image and video, write surface: postJob(), cancelJob(), enrollWorker(), claimJob(), markRunning(), failJob(), publishOutput(), recordHandoff(), submitReceipt(). A pipeline whose denoising schedule splits at a timestep boundary is served by two workers holding one expert each — the job carries a required role per half, and the high-noise worker commits to the one intermediate latent via recordHandoff() so its partner can pull it and finish | | trainingInspection | Tenzro Train read side — runs, one run, receipts, sealed manifests | | compute | Compute rental against a node started with the ai role — fixed-term CPU/GPU capacity | | storage | Decentralized storage against a node started with the storage role — erasure-coded objects over content-addressed shards | | database | Managed databases a node's operator wired up: external (Postgres, Qdrant, Valkey) and embedded engines | | files | Multi-tenant object storage: upload(filename, data, purpose), list(options), get(fileId), download(fileId), delete(fileId), usage(). Bytes are erasure-coded 4 data + 2 parity, surviving two simultaneous provider losses, and a storage deal is opened to pay for them — check deal_id on the upload result, because null means the upload succeeded but nothing is funding it. purpose is one of assistants / batch / fine_tune / vision / user_data. Listing is scoped server-side to the subject on the presented key and never returns another tenant's files | | gateway | Discover and call any method the node serves: methods(query) returns the directory with each entry's gate (admin / open), required API-key scope, and namespace, plus every namespace so a caller can narrow a second query; supports(method) tests one by name. The typed clients above cover the surfaces worth a dedicated signature — this covers the rest. It exists because a node serves ~900 JSON-RPC methods and gains more each release, so a hand-wrapped SDK is always months behind the node it is talking to and the developer hits that gap exactly when they need the method. The list comes from the node, so a newer node simply reports more. Authorization is unchanged: a gateway call runs behind the same admin-token gate, API-key scope gate, and default-deny classification as any other, reaching exactly what your credentials already allow | | iroh | Consumer surface over the shared resolver — publishBlob() and fetch tenzro://blob/<hash> content | | token | createToken(), listTokens(), crossVmTransfer() | | nft | createCollection(), mintNft(), transferNft() | | bridge | bridgeTokens(), getRoutes(), getBridgeStatus() | | wormhole | wormholeBridge(), getVaa(), redeemVaa()