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, version1.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:
[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 identifierX-Network— the target network (devnet,testnet, ormainnet)
No Bearer token
Authorization: Bearer header.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).
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:
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
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).
| Service | Accessor | Client type | Feature flag | Description |
|---|---|---|---|---|
| AI | platform.ai() | AiClient | ai | TEE-secured time-series forecasting (Chronos models) and multi-party confidential inference |
| Ledger | platform.ledger() | LedgerClient | ledger | Canton Network Ledger API — parties, DAML contracts, DAR packages, domains, Canton Coin transfers |
| Bridge | platform.bridge() | BridgeClient | bridge | Cross-chain bridging via Chainlink CCIP |
| Anchor | platform.anchor() | AnchorClient | anchor | Canton contract state-root anchoring to EVM chains with Merkle proofs |
| Token | platform.token() | TokenClient | token | Asset tokenization — fungible, NFT, and semi-fungible collections on Canton |
| Wallet | platform.wallet() | WalletClient | wallet | MPC wallet governance — spending policies and multi-sig approvals |
| Custody | platform.custody() | CustodyClient | custody | TEE-secured MPC key custody and signing operations |
| Provision | platform.provision() | ProvisionClient | provision | Canton party allocation and Daml application lifecycle management |
Quick Start Example
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.
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.
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
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.
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
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
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
platform.custody() when the Feature flag
custody feature flag. It is enabled by default with the standard dependency declaration.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.
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.
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.
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.
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?;SSE streaming feature flag
streaming feature in your Cargo.toml:tenzro-platform-rust-sdk = { version = "1.0", features = ["streaming"] }
// Then in your code:
#[cfg(feature = "streaming")]
let sse_stream = platform.ledger().stream_transactions_sse(config).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:
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.
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:
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.
| Feature | Enables |
|---|---|
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 |
# 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
| Variable | Required | Default | Description |
|---|---|---|---|
TENZRO_API_KEY | Yes | — | API key in the format tnz_{tenant}_{secret} |
TENZRO_TENANT_ID | No | Extracted from key | Tenant identifier; auto-parsed from the API key if not set |
TENZRO_NETWORK | No | devnet | Target network: devnet, testnet, or mainnet |
TENZRO_BASE_URL | No | https://api.platform.tenzro.com | Override the API base URL (for staging or on-premises deployments) |
TENZRO_TIMEOUT_MS | No | 30000 | Request timeout in milliseconds |
# .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=30000Complete End-to-End Example
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:
| Crate | Version | Purpose |
|---|---|---|
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) |