Rust SDK

The official Rust SDK for Tenzro Platform provides type-safe, async-first access to Canton Network infrastructure, TEE-secured AI time-series forecasting, cross-chain bridging via Chainlink CCIP, state-root anchoring with Merkle proofs, asset tokenization, MPC wallet governance, TEE-secured key custody, and Canton party provisioning.

  • Crate name: tenzro_platform (package: tenzro-platform-rust-sdk, version 1.0.0)
  • Edition: Rust 2021
  • Async runtime: Tokio 1.43
  • TLS: rustls (bundled with reqwest, no OpenSSL required)
  • License: Apache-2.0

Installation

Add to your Cargo.toml:

Cargo.toml
Dependency configuration
[dependencies]
# All 8 service features are enabled by default
tenzro-platform-rust-sdk = "1.0"

# Opt into the explicit full feature alias
tenzro-platform-rust-sdk = { version = "1.0", features = ["full"] }

# Select only the features you need (disables the rest)
tenzro-platform-rust-sdk = { version = "1.0", default-features = false, features = ["ai", "ledger"] }

All SDK types are imported from the tenzro_platform crate:

use tenzro_platform::{TenzroPlatform, TenzroConfig, Network, TenzroError, Result};

Authentication

Every request sent by the SDK carries two headers derived from your credentials:

  • X-API-Key — your API key (format: tnz_{tenant}_{secret})
  • X-Tenant-Id — your tenant identifier
  • X-Network — the target network (devnet, testnet, or mainnet)

Client Construction

TenzroPlatform exposes three constructors. All return Result<TenzroPlatform> because the internal HTTP client can fail to build (for example if a header value contains non-ASCII characters).

Client Construction
Three ways to construct a TenzroPlatform client
use tenzro_platform::TenzroPlatform;

// 1. Direct construction — supply an API key and tenant ID
let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// 2. From environment variables
//    TENZRO_API_KEY       — required
//    TENZRO_TENANT_ID     — optional; extracted from the key if absent
//    TENZRO_NETWORK       — optional; default "devnet"
//    TENZRO_BASE_URL      — optional; default "https://api.platform.tenzro.com"
//    TENZRO_TIMEOUT_MS    — optional; default 30000
let platform = TenzroPlatform::from_env()?;

// 3. From a TenzroConfig struct for full control
use tenzro_platform::{TenzroConfig, Network};

let config = TenzroConfig::new("tnz_mytenant_secret", "my-tenant-id")
    .with_network(Network::Mainnet)
    .with_base_url("https://api.platform.tenzro.com")
    .with_timeout(60_000);   // milliseconds

let platform = TenzroPlatform::from_config(config)?;

API keys follow the format tnz_{tenant}_{secret}. When using from_env() without setting TENZRO_TENANT_ID, the SDK parses the tenant segment from the key automatically.

Configuration Reference

TenzroConfig is a plain struct with public fields and optional builder-style mutating methods:

TenzroConfig
Configuration struct and builder methods
pub struct TenzroConfig {
    pub api_key:    String,   // format: tnz_{tenant}_{secret}
    pub tenant_id:  String,
    pub network:    Network,  // default: Network::Devnet
    pub base_url:   String,   // default: "https://api.platform.tenzro.com"
    pub timeout_ms: u64,      // default: 30_000 ms
}

// Builder-style setters (consume and return Self)
impl TenzroConfig {
    pub fn with_network(self, network: Network)   -> Self { ... }
    pub fn with_base_url(self, base_url: &str)    -> Self { ... }
    pub fn with_timeout(self, timeout_ms: u64)    -> Self { ... }
}

Network Enum

Network
Three variants; Devnet is the default
use tenzro_platform::Network;

// Three variants; Devnet is the default
Network::Devnet   // "devnet"  — GSF DevNet (default)
Network::Testnet  // "testnet" — GSF TestNet
Network::Mainnet  // "mainnet" — GSF MainNet

// Network::Devnet is the Default implementation
let config = TenzroConfig::new("tnz_mytenant_secret", "my-tenant-id");
assert_eq!(config.network, Network::Devnet);

When reading from the TENZRO_NETWORK environment variable, the following string aliases are all accepted:

  • Mainnet: mainnet, main, production, prod
  • Testnet: testnet, test, staging
  • Anything else resolves to Devnet.

Available Services

Each service is accessed via a method on TenzroPlatform. The method is only compiled when the corresponding feature flag is enabled (all eight are on by default).

ServiceAccessorClient typeFeature flagDescription
AIplatform.ai()AiClient
ai
TEE-secured time-series forecasting (Chronos models) and multi-party confidential inference
Ledgerplatform.ledger()LedgerClient
ledger
Canton Network Ledger API — parties, DAML contracts, DAR packages, domains, Canton Coin transfers
Bridgeplatform.bridge()BridgeClient
bridge
Cross-chain bridging via Chainlink CCIP
Anchorplatform.anchor()AnchorClient
anchor
Canton contract state-root anchoring to EVM chains with Merkle proofs
Tokenplatform.token()TokenClient
token
Asset tokenization — fungible, NFT, and semi-fungible collections on Canton
Walletplatform.wallet()WalletClient
wallet
MPC wallet governance — spending policies and multi-sig approvals
Custodyplatform.custody()CustodyClient
custody
TEE-secured MPC key custody and signing operations
Provisionplatform.provision()ProvisionClient
provision
Canton party allocation and Daml application lifecycle management

Quick Start Example

Quick Start
Basic usage across AI, Ledger, and Bridge services
use tenzro_platform::TenzroPlatform;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let platform = TenzroPlatform::new(
        "tnz_mytenant_secret",
        "my-tenant-id",
    )?;

    // AI time-series forecasting with TEE attestation
    let models = platform.ai().list_forecast_models().await?;
    println!("Available models: {:?}", models.len());

    // Canton Ledger operations
    let parties = platform.ledger().list_parties().await?;
    println!("Parties: {}", parties.len());

    // Cross-chain bridge routes
    let routes = platform.bridge().get_routes().await?;
    println!("Bridge routes: {}", routes.len());

    Ok(())
}

AI Service — Time-Series Forecasting

The AI service provides TEE-secured time-series forecasting using Chronos models, plus multi-party confidential inference sessions where multiple parties each contribute private encrypted data before a joint inference is executed.

AI Service
TEE-secured forecasting, multi-party inference, and TEE attestation verification
use tenzro_platform::TenzroPlatform;
use tenzro_platform::ai::{ForecastRequest, CreateSessionRequest, ContributeRequest};

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// List available forecast models
let models = platform.ai().list_forecast_models().await?;
for m in &models {
    println!("Model: {} (backend: {})", m.id, m.backend);
}

// Run a time-series forecast
let result = platform.ai().forecast(&ForecastRequest {
    context: vec![100.0, 112.0, 108.0, 125.0, 118.0, 130.0, 127.0],
    prediction_length: 5,
    model: "amazon/chronos-bolt-small".to_string(),
}).await?;

println!("Median forecast:  {:?}", result.predictions);
println!("10th percentile:  {:?}", result.low);
println!("90th percentile:  {:?}", result.high);

// Inspect TEE attestation on the result
if let Some(att) = &result.attestation {
    println!("TEE platform: {}", att.platform);
    println!("Attested:     {}", att.attested);
}

// Verify a TEE attestation quote independently
use tenzro_platform::ai::VerifyAttestationRequest;

let verification = platform.ai().verify_attestation(&VerifyAttestationRequest {
    quote: "0xdeadbeef...".to_string(),
    platform: Some("intel-tdx".to_string()),
}).await?;
println!("Attestation valid: {}", verification.valid);

// Multi-party confidential inference
let session = platform.ai().create_multiparty_session(&CreateSessionRequest {
    model_id: "amazon/chronos-bolt-small".to_string(),
    initiator: "party-A".to_string(),
    participants: vec!["party-A".to_string(), "party-B".to_string()],
}).await?;

// Each party contributes their private time-series data
platform.ai().contribute_data(&session.session_id, &ContributeRequest {
    party_id: "party-A".to_string(),
    data: vec![90.0, 95.0, 102.0, 98.0],
}).await?;

// Execute inference once all parties have contributed
use tenzro_platform::ai::ExecuteSessionRequest;

let output = platform.ai().execute_session(
    &session.session_id,
    &ExecuteSessionRequest { party_id: "party-A".to_string() },
).await?;
println!("Inference result: {:?}", output.result);

Ledger Service — Canton Network

The ledger service exposes the Canton Network Ledger API: party management, DAML contract creation and choice exercise, DAR package uploads, synchronization domain inspection, Canton Coin transfers, and resource allocation management.

Ledger Service
Parties, DAML contracts, DAR packages, domains, and Canton Coin transfers
use tenzro_platform::TenzroPlatform;
use tenzro_platform::ledger::{
    AllocatePartyRequest, CreateContractRequest, ExerciseChoiceRequest,
    TransferRequest, UploadPackageRequest, CreateAllocationRequest,
};

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// Participant identity and network status
let identity = platform.ledger().get_identity().await?;
println!("Participant: {:?}", identity.participant_id);

let status = platform.ledger().get_status().await?;
println!("Connected: {:?}", status.connected);

// List all parties
let parties = platform.ledger().list_parties().await?;
for p in &parties {
    println!("Party: {} ({})", p.party_id, p.display_name);
}

// Allocate a new party
let new_party = platform.ledger().allocate_party(&AllocatePartyRequest {
    party_id_hint: "treasury".to_string(),
    display_name:  "Treasury Party".to_string(),
    annotations:   None,
    organization_id: None,
}).await?;
println!("Allocated: {}", new_party.party_id);

// Canton Coin balance
let balance = platform.ledger().get_balance(&new_party.party_id).await?;
println!("Unlocked CC: {:?}", balance.unlocked_cc);
println!("Total CC:    {:?}", balance.total_cc);

// Canton Coin transfer
let tx = platform.ledger().transfer(&TransferRequest {
    from_party_id: "party-1::abc123".to_string(),
    to_party_id:   "party-2::def456".to_string(),
    amount: "100.0".to_string(),
    asset:  "CC".to_string(),
}).await?;
println!("Transfer tx: {:?}", tx.transaction_id);

// Create a DAML contract
let contract = platform.ledger().create_contract(&CreateContractRequest {
    template_id: "MyModule:MyTemplate".to_string(),
    payload: serde_json::json!({ "owner": "party-1::abc123", "amount": 1000 }),
    party_id: "party-1::abc123".to_string(),
}).await?;
println!("Contract ID: {}", contract.contract_id);

// Exercise a choice on a contract
let result = platform.ledger().exercise_choice(
    &contract.contract_id,
    &ExerciseChoiceRequest {
        choice:   "Transfer".to_string(),
        argument: serde_json::json!({ "newOwner": "party-2::def456" }),
        party_id: "party-1::abc123".to_string(),
    },
).await?;
println!("Exercise success: {}", result.success);

// Query active contracts for a party
let contracts = platform.ledger()
    .query_contracts("party-1::abc123", Some("MyModule:MyTemplate"))
    .await?;
println!("Active contracts: {}", contracts.len());

// Upload a DAR package (base64-encoded .dar bytes)
let pkg = platform.ledger().upload_package(&UploadPackageRequest {
    name:    "my-app".to_string(),
    version: "1.0.0".to_string(),
    content: base64_dar_bytes.to_string(),
}).await?;
println!("Package ID: {:?}", pkg.package_id);

// List synchronization domains
let domains = platform.ledger().list_domains().await?;
for d in &domains {
    println!("Domain: {} — status: {}", d.domain_id, d.status);
}

// Resource allocation
let alloc = platform.ledger().create_allocation(&CreateAllocationRequest {
    app_id:        "app-123".to_string(),
    app_name:      "My Daml App".to_string(),
    network:       "devnet".to_string(),
    max_contracts: Some(10_000),
    ..Default::default()
}).await?;
println!("Allocation: {:?}", alloc.id);

// Transaction history
let txs = platform.ledger().get_transactions("party-1::abc123", Some(50)).await?;
for tx in &txs {
    println!("Tx: {} at {:?}", tx.transaction_id, tx.effective_at);
}

// Ledger statistics
let stats = platform.ledger().get_stats().await?;
println!("Active contracts: {:?}", stats.active_contracts);
println!("Total parties:   {:?}", stats.total_parties);

Bridge Service — Chainlink CCIP

Bridge Service
Cross-chain routes, limits, and operations via Chainlink CCIP
use tenzro_platform::TenzroPlatform;
use tenzro_platform::bridge::CreateOperationRequest;

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// Available cross-chain routes
let routes = platform.bridge().get_routes().await?;
for r in &routes {
    println!("Route: {:?}", r);
}

// Supported chains
let chains = platform.bridge().get_chains().await?;

// Transfer limits per route
let limits = platform.bridge().get_limits().await?;

// List bridge operations
let ops = platform.bridge().list_operations().await?;

// Bridge statistics
let stats = platform.bridge().get_stats().await?;

Anchor Service — Canton State Roots

The anchor service records Canton contract state roots on EVM chains via Merkle trees, providing settlement finality proofs that can be independently verified on-chain.

Anchor Service
State root anchoring, Merkle proof retrieval, and on-chain verification
use tenzro_platform::TenzroPlatform;
use tenzro_platform::anchor::AnchorContractRequest;

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// Anchor a Canton contract state root to an EVM chain
let anchored = platform.anchor().anchor_contract(&AnchorContractRequest {
    contract_id: "00abc123...#1".to_string(),
    template_id: "MyModule:MyTemplate".to_string(),
    hash:        "sha256:0xdeadbeef...".to_string(),
    chain:       "ethereum".to_string(),
    party_id:    "party-1::abc123".to_string(),
}).await?;

println!("Anchor status: {:?}", anchored.status);
println!("On-chain tx:   {:?}", anchored.tx_hash);

// Retrieve the Merkle proof for independent verification
let proof = platform.anchor().get_proof(&anchored.contract_id).await?;
println!("Root hash:  {}", proof.root_hash);
println!("Proof path: {:?}", proof.proof);

// Verify the proof server-side
let verification = platform.anchor().verify_proof(&anchored.contract_id).await?;
assert!(verification.valid);

// List anchor batches
let batches = platform.anchor().list_batches().await?;
for b in &batches {
    println!("Batch: {:?} — contracts: {:?}", b.batch_id, b.contract_count);
}

// Anchor service statistics
let stats = platform.anchor().get_stats().await?;
println!("Total anchored:   {:?}", stats.total_contracts);
println!("Pending:          {:?}", stats.pending_anchors);
println!("Supported chains: {:?}", stats.supported_chains);

Token Service — Canton Asset Tokenization

Token Service
Fungible, NFT, and semi-fungible collections on Canton
use tenzro_platform::TenzroPlatform;
use tenzro_platform::token::{CreateCollectionRequest, MintRequest};

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// Supported token standards (ERC-20, ERC-721, etc.)
let standards = platform.token().get_supported().await?;

// List token collections
let collections = platform.token().list_collections().await?;
for c in &collections {
    println!("Collection: {:?}", c);
}

// Token holdings for the current tenant
let holdings = platform.token().list_holdings().await?;

// Service statistics
let stats = platform.token().get_stats().await?;

Wallet Service — MPC Governance

Wallet Service
MPC wallet creation, spending policies, whitelist management, and multi-sig approvals
use tenzro_platform::TenzroPlatform;
use tenzro_platform::wallet::{
    CreateWalletRequest, CreatePolicyRequest, WhitelistRequest,
    WalletSignRequest, ApproveRequest,
};

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// Governance configuration
let settings = platform.wallet().get_settings().await?;
println!("Governance enabled:  {:?}", settings.governance_enabled);
println!("Approval threshold:  {:?}", settings.default_approval_threshold);

// Create an MPC wallet
let wallet = platform.wallet().create_wallet(&CreateWalletRequest {
    name:        "Treasury MPC Wallet".to_string(),
    wallet_type: Some("mpc".to_string()),
    chain:       Some("ethereum".to_string()),
}).await?;

// Create a spending policy
let _policy = platform.wallet().create_policy(&CreatePolicyRequest {
    name:               "High-value transfers".to_string(),
    max_amount:         Some("10000.00".to_string()),
    daily_limit:        Some("50000.00".to_string()),
    required_approvals: Some(2),
}).await?;

// Whitelist a destination address
platform.wallet().add_to_whitelist(&WhitelistRequest {
    address: "0xabc123...".to_string(),
    label:   "Partner settlement account".to_string(),
}).await?;

// List pending approval requests
let approvals = platform.wallet().list_approvals().await?;
for approval in &approvals {
    println!("Pending: {:?} — amount: {:?}", approval.request_id, approval.amount);
}

// Approve a pending transaction
if let Some(req_id) = approvals.first().and_then(|a| a.request_id.as_deref()) {
    platform.wallet().approve(req_id, &ApproveRequest {
        comment: Some("Approved by treasury officer".to_string()),
    }).await?;
}

// MPC signing
let sig = platform.wallet().sign(&WalletSignRequest {
    wallet_id:    wallet.wallet_id.unwrap_or_default(),
    data:         "0xdeadbeef".to_string(),
    message_type: Some("transaction".to_string()),
}).await?;
println!("Signature: {}", sig.signature);

Custody Service — TEE-Secured Key Custody

Custody Service
TEE-secured MPC key custody and signing operations. Access via platform.custody() when the
custody
feature is enabled (on by default).

Provision Service — Party Allocation and App Deployment

The provision service manages the full Canton party lifecycle and Daml application deployment across devnet, testnet, and mainnet networks.

Provision Service
Canton party allocation, app registration, and network deployment
use tenzro_platform::TenzroPlatform;
use tenzro_platform::provision::{
    AllocatePartyRequest, CreateAppRequest, DeployAppRequest,
    UpdateAccessRequest, ListPartiesFilter,
};

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// List parties (paginated)
let response = platform.provision().list_parties(None).await?;
println!("Total parties: {:?}", response.total_count);
for p in &response.parties {
    println!("  {} — {:?}", p.party_id, p.network);
}

// Paginated request with filter
let filter = ListPartiesFilter {
    network:    Some("devnet".to_string()),
    page_size:  Some(20),
    page_token: None,
};
let page = platform.provision().list_parties(Some(&filter)).await?;
println!("Next page token: {:?}", page.next_page_token);

// Allocate a new party
// party_type values: 6 = Application
// network values:    1 = Devnet, 2 = Testnet, 3 = Mainnet
let allocation = platform.provision().allocate_party(&AllocatePartyRequest {
    tenant_id:         "my-tenant-id".to_string(),
    display_name:      "My App Party".to_string(),
    party_type:        Some(6),
    network:           Some(1),
    party_id_hint:     Some("myapp".to_string()),
    use_external_keys: None,
}).await?;

if allocation.success {
    println!("Allocated party: {:?}", allocation.party_id);
    println!("Participant:     {:?}", allocation.participant_id);
}

// Register a Daml application
let app = platform.provision().create_app(&CreateAppRequest {
    name:            "my-daml-app".to_string(),
    tenant_id:       "my-tenant-id".to_string(),
    description:     Some("My tokenization application".to_string()),
    deployment_type: Some("custom".to_string()),
    repository:      Some("https://github.com/my-org/my-daml-app".to_string()),
    endpoint:        None,
}).await?;
println!("App ID: {}", app.id);

// Request devnet access for the app
platform.provision().update_app_access(&app.id, &UpdateAccessRequest {
    network: "devnet".to_string(),
    status:  "requested".to_string(),
}).await?;

// Deploy to devnet once access is approved
let deploy = platform.provision().deploy_app(&app.id, &DeployAppRequest {
    network: "devnet".to_string(),
}).await?;

if deploy.success {
    if let Some(info) = &deploy.deployment {
        println!("Party ID:   {:?}", info.party_id);
        println!("Ledger API: {:?}", info.ledger_api_url);
        println!("JSON API:   {:?}", info.json_api_url);
    }
}

// Undeploy when done
use tenzro_platform::provision::UndeployAppRequest;

platform.provision().undeploy_app(&app.id, &UndeployAppRequest {
    network: "devnet".to_string(),
}).await?;

Error Handling

All SDK operations return tenzro_platform::Result<T>, which is a type alias for std::result::Result<T, TenzroError>. The TenzroError enum is derived from thiserror and integrates with the ? operator.

TenzroError variants
Match on error variants for structured error handling
use tenzro_platform::{TenzroPlatform, TenzroError, Result};

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

match platform.ledger().list_parties().await {
    Ok(parties) => {
        println!("Got {} parties", parties.len());
    }

    // Structured API error — includes HTTP status, machine-readable code, and message
    Err(TenzroError::Api { status, code, message }) => {
        eprintln!("API error {}: [{}] {}", status, code, message);
    }

    // HTTP 401 — invalid or expired API key
    Err(TenzroError::Unauthorized(msg)) => {
        eprintln!("Authentication failed: {}", msg);
    }

    // HTTP 404 — resource does not exist
    Err(TenzroError::NotFound(msg)) => {
        eprintln!("Not found: {}", msg);
    }

    // HTTP 429 — too many requests
    Err(TenzroError::RateLimited(msg)) => {
        eprintln!("Rate limited: {}", msg);
    }

    // Network/TLS/DNS failure from reqwest
    Err(TenzroError::Http(e)) => {
        eprintln!("HTTP transport error: {}", e);
    }

    // Invalid SDK configuration (e.g. bad header value)
    Err(TenzroError::Config(msg)) => {
        eprintln!("Configuration error: {}", msg);
    }

    // serde_json parse failure
    Err(TenzroError::Serialization(e)) => {
        eprintln!("Deserialisation error: {}", e);
    }

    // Request exceeded the configured timeout_ms
    Err(TenzroError::Timeout(msg)) => {
        eprintln!("Timeout: {}", msg);
    }

    // Caller-supplied argument rejected before any network call
    Err(TenzroError::InvalidArgument(msg)) => {
        eprintln!("Invalid argument: {}", msg);
    }
}

Pagination

The SDK provides built-in cursor-based pagination for all list endpoints. Use PageRequest to control page size and cursor, and PageResponseto iterate through results. An auto-paginating PageIterator is also available for collecting all pages.

Pagination
Cursor-based pagination and advanced contract filtering
use tenzro_platform::{TenzroPlatform, PageRequest};
use tenzro_platform::pagination::{ContractFilter, ContractStatus, SortOrder};

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// Paginated party listing
let page = platform.ledger().list_parties_paged(PageRequest {
    page_size: Some(20),
    page_token: None,
    offset: None,
    sort_by: None,
    sort_order: Some(SortOrder::Desc),
}).await?;

println!("Got {} parties, has_more: {}", page.items.len(), page.has_more());

// Iterate through all pages
if let Some(next_token) = page.next_page_token {
    let next_page = platform.ledger().list_parties_paged(PageRequest {
        page_token: Some(next_token),
        ..Default::default()
    }).await?;
}

// Advanced contract filtering
let filter = ContractFilter {
    party_id: Some("party-1::abc123".to_string()),
    template_ids: Some(vec!["MyModule:MyTemplate".to_string()]),
    status: Some(ContractStatus::Active),
    domain_id: None,
    created_after: None,
    created_before: None,
    payload_search: Some("treasury".to_string()),
    payload_filter: None,
    include_archived: Some(false),
};
let contracts = platform.ledger().query_contracts_filtered(&filter).await?;

Real-Time Streaming

The SDK supports two streaming modes for real-time transaction monitoring: polling-based TransactionStream (always available) and SSE-basedSseTransactionStream (requires the streaming feature flag). A ContractWatcher utility is also provided for watching specific contracts.

Streaming
Polling-based and SSE-based transaction streams
use tenzro_platform::TenzroPlatform;
use tenzro_platform::streaming::StreamConfig;

let platform = TenzroPlatform::new("tnz_mytenant_secret", "my-tenant-id")?;

// Polling-based transaction stream
let config = StreamConfig {
    party_id: "party-1::abc123".to_string(),
    poll_interval_ms: Some(2000),
    begin_offset: None,
    template_filter: Some(vec!["MyModule:MyTemplate".to_string()]),
};
let mut stream = platform.ledger().stream_transactions(config).await?;

// Poll for new transactions
while let Some(event) = stream.next().await? {
    println!("Tx: {} at offset {}", event.transaction.transaction_id, event.offset);
}

// Watch a specific contract for changes
let watcher = platform.ledger().watch_contract("contract-id-123", "party-1::abc123").await?;

Built-in Retry with Exponential Backoff

The SDK includes built-in retry middleware for transient errors. The internal HTTP client automatically retries requests that fail with retryable errors (HTTP transport failures, timeouts, rate limits, and 502/503/504 responses) using configurable exponential backoff.

The TenzroError::is_retryable() method determines which errors are safe to retry:

Retry behaviour
Default retry configuration and retryable error classification
use tenzro_platform::TenzroError;

// Retryable errors (automatically retried by the SDK):
// - TenzroError::Http(_)         — transport failures
// - TenzroError::Timeout(_)      — request timeouts
// - TenzroError::RateLimited(_)  — HTTP 429
// - TenzroError::Api { status: 502|503|504, .. } — gateway errors

// Default retry configuration:
// - max_retries: 3
// - initial_backoff: 500ms
// - backoff_multiplier: 2.0x (exponential)
// - max_backoff: 10,000ms

// Check if an error is retryable:
match platform.ledger().list_parties().await {
    Err(e) if e.is_retryable() => {
        println!("Transient error, SDK will have already retried: {}", e);
    }
    Err(e) => {
        println!("Non-retryable error: {}", e);
    }
    Ok(parties) => {
        println!("Got {} parties", parties.len());
    }
}

DAR File Upload Helper

The SDK provides a convenience method for uploading DAR files directly from the filesystem. It reads the file, base64-encodes the contents, and uploads to the ledger service.

DAR Upload
Upload DAR files from disk or raw base64 bytes
use std::path::Path;

// Upload a DAR file from disk
let pkg = platform.ledger().upload_dar_file(
    Path::new("/path/to/my-app.dar"),
    Some("my-app"),      // optional name
    Some("1.0.0"),       // optional version
).await?;
println!("Uploaded package: {:?}", pkg.package_id);

// Or upload raw base64 bytes manually
use tenzro_platform::ledger::UploadPackageRequest;

let pkg = platform.ledger().upload_package(&UploadPackageRequest {
    name:    "my-app".to_string(),
    version: "1.0.0".to_string(),
    content: base64_dar_string,
}).await?;

Party Management

Beyond party allocation, the SDK supports updating party metadata and inspecting package templates:

Party Management
Update party metadata and inspect package templates
use tenzro_platform::ledger::UpdatePartyRequest;

// Update party display name and annotations
platform.ledger().update_party("party-1::abc123", &UpdatePartyRequest {
    display_name: Some("Updated Name".to_string()),
    annotations: Some(serde_json::json!({ "role": "treasury", "tier": "premium" })),
}).await?;

// Inspect templates in an uploaded package
let templates = platform.ledger().get_package_templates("package-id-123").await?;
for t in &templates {
    println!("Template: {}:{} — choices: {:?}", t.module_name, t.template_name, t.choices);
}

Feature Flags

Every service module is gated behind a Cargo feature. All eight are included in the default feature set and in the full alias. The streaming feature enables SSE-based real-time streams and requires tokio-stream and futures-util.

FeatureEnables
ai
platform.ai() — TEE time-series forecasting and multi-party inference
ledger
platform.ledger() — Canton Network Ledger API
bridge
platform.bridge() — Chainlink CCIP cross-chain bridging
anchor
platform.anchor() — Canton state-root anchoring with Merkle proofs
token
platform.token() — asset tokenization on Canton
wallet
platform.wallet() — MPC wallet governance
custody
platform.custody() — TEE-secured MPC key custody
provision
platform.provision() — Canton party allocation and app deployment
streaming
SSE-based real-time transaction streams (adds tokio-stream and futures-util)
full
Alias that enables all 8 service features plus streaming
Feature flag examples
Minimal and full build configurations
# Minimal build: only ledger and provision, no AI/bridge/anchor/token/wallet/custody
tenzro-platform-rust-sdk = {
    version = "1.0",
    default-features = false,
    features = ["ledger", "provision"]
}

# Full build with SSE streaming
tenzro-platform-rust-sdk = { version = "1.0", features = ["full"] }

Environment Variable Configuration

VariableRequiredDefaultDescription
TENZRO_API_KEY
Yes
API key in the format tnz_{tenant}_{secret}
TENZRO_TENANT_ID
No
Extracted from keyTenant identifier; auto-parsed from the API key if not set
TENZRO_NETWORK
No
devnetTarget network: devnet, testnet, or mainnet
TENZRO_BASE_URL
No
https://api.platform.tenzro.comOverride the API base URL (for staging or on-premises deployments)
TENZRO_TIMEOUT_MS
No
30000Request timeout in milliseconds
.env example
Sample environment variable configuration
# .env example
TENZRO_API_KEY=tnz_mytenant_s3cr3t
TENZRO_TENANT_ID=my-tenant-id    # optional if key contains the tenant segment
TENZRO_NETWORK=mainnet
TENZRO_BASE_URL=https://api.platform.tenzro.com
TENZRO_TIMEOUT_MS=30000

Complete End-to-End Example

End-to-End Example
Full example combining Ledger, Anchor, AI, and Provision services
use tenzro_platform::TenzroPlatform;
use tenzro_platform::ai::ForecastRequest;
use tenzro_platform::anchor::AnchorContractRequest;
use tenzro_platform::ledger::{AllocatePartyRequest, CreateContractRequest};
use tenzro_platform::provision::{CreateAppRequest, DeployAppRequest};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialise from environment variables
    let platform = TenzroPlatform::from_env()?;

    // Allocate a Canton party
    let party = platform.ledger().allocate_party(&AllocatePartyRequest {
        party_id_hint:   "operator".to_string(),
        display_name:    "My App Operator".to_string(),
        annotations:     None,
        organization_id: None,
    }).await?;
    println!("Party: {}", party.party_id);

    // Check Canton Coin balance
    let balance = platform.ledger().get_balance(&party.party_id).await?;
    println!("CC balance: {:?}", balance.total_cc);

    // Create a DAML contract on the ledger
    let contract = platform.ledger().create_contract(&CreateContractRequest {
        template_id: "Finance:Invoice".to_string(),
        payload: serde_json::json!({
            "issuer":   party.party_id,
            "amount":   5000,
            "currency": "USD"
        }),
        party_id: party.party_id.clone(),
    }).await?;
    println!("Contract: {}", contract.contract_id);

    // Anchor the contract state root to Ethereum
    let anchored = platform.anchor().anchor_contract(&AnchorContractRequest {
        contract_id: contract.contract_id.clone(),
        template_id: "Finance:Invoice".to_string(),
        hash:        format!("sha256:{}", contract.contract_id),
        chain:       "ethereum".to_string(),
        party_id:    party.party_id.clone(),
    }).await?;
    println!("Anchored tx: {:?}", anchored.tx_hash);

    // Run a TEE-secured time-series forecast
    let forecast = platform.ai().forecast(&ForecastRequest {
        context:           vec![5000.0, 5200.0, 4900.0, 5300.0, 5100.0],
        prediction_length: 3,
        model:             "amazon/chronos-bolt-small".to_string(),
    }).await?;
    println!("Next 3 periods (median): {:?}", forecast.predictions);

    // Register and deploy a Daml application
    let app = platform.provision().create_app(&CreateAppRequest {
        name:        "finance-app".to_string(),
        tenant_id:   std::env::var("TENZRO_TENANT_ID").unwrap_or_default(),
        description: Some("Finance module".to_string()),
        ..Default::default()
    }).await?;

    let deploy = platform.provision().deploy_app(&app.id, &DeployAppRequest {
        network: "devnet".to_string(),
    }).await?;

    if deploy.success {
        if let Some(info) = deploy.deployment {
            println!("Ledger API: {:?}", info.ledger_api_url);
            println!("JSON API:   {:?}", info.json_api_url);
        }
    }

    Ok(())
}

Dependencies

The SDK pulls in the following direct dependencies:

CrateVersionPurpose
reqwest
0.12
Async HTTP client with json and rustls-tls features
serde / serde_json
1.0
JSON serialization and deserialization
thiserror
2.0
Ergonomic error type derivation for TenzroError
tokio
1.43
Async runtime (requires the rt feature in your binary)
tracing
0.1
Structured diagnostic logging
uuid
1.11
UUID v4 generation and serde support
chrono
0.4
Date/time types with serde support
base64
0.22
Base64 encoding for DAR file uploads
pin-project-lite
0.2
Pin projection for Stream trait implementations
tokio-stream
0.1
Stream utilities (optional, requires
streaming
feature)
futures-util
0.3
Future/Stream combinators (optional, requires
streaming
feature)