Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/risc0/risc0/llms.txt

Use this file to discover all available pages before exploring further.

The Bonsai SDK provides a client library for interacting with the Bonsai proving service. It offers both blocking and async (non-blocking) APIs for uploading programs, submitting proof requests, and retrieving results.

Installation

Add the Bonsai SDK to your Cargo.toml:
[dependencies]
bonsai-sdk = "0.8"
risc0-zkvm = "1.0"

Configuration

The SDK requires environment variables for authentication and configuration:
VariableDescriptionRequired
BONSAI_API_URLAPI endpoint URLYes
BONSAI_API_KEYYour API keyYes
BONSAI_TIMEOUT_MSRequest timeout in ms (default: 30000)No
RISC0_RELEASE_CHANNELRelease channel for software versionsNo
export BONSAI_API_URL="https://api.bonsai.xyz"
export BONSAI_API_KEY="your_api_key_here"

Client Setup

From Environment Variables

The simplest way to create a client:
use bonsai_sdk::blocking::Client;

let client = Client::from_env(risc0_zkvm::VERSION)?;

From Explicit Parameters

For more control over configuration:
use bonsai_sdk::blocking::Client;

let url = "https://api.bonsai.xyz".to_string();
let api_key = "your_api_key".to_string();

let client = Client::from_parts(
    url,
    api_key,
    risc0_zkvm::VERSION
)?;

Complete Proving Workflow

Here’s a full example showing the entire proving workflow:
use std::time::Duration;
use anyhow::Result;
use bonsai_sdk::blocking::Client;
use methods::{METHOD_ELF, METHOD_ID};
use risc0_zkvm::{compute_image_id, serde::to_vec, Receipt};

fn prove_on_bonsai(input_data: Vec<u8>) -> Result<Receipt> {
    // Create client from environment
    let client = Client::from_env(risc0_zkvm::VERSION)?;
    
    // Step 1: Upload the ELF binary
    let image_id = hex::encode(compute_image_id(METHOD_ELF)?);
    client.upload_img(&image_id, METHOD_ELF.to_vec())?;
    
    // Step 2: Prepare and upload input data
    let input_data = to_vec(&input_data).unwrap();
    let input_data = bytemuck::cast_slice(&input_data).to_vec();
    let input_id = client.upload_input(input_data)?;
    
    // Step 3: Create proving session
    let assumptions = vec![]; // Add any assumption receipts here
    let execute_only = false; // Set to true for execution without proving
    
    let session = client.create_session(
        image_id,
        input_id,
        assumptions,
        execute_only
    )?;
    
    // Step 4: Poll for completion
    loop {
        let status = session.status(&client)?;
        
        match status.status.as_str() {
            "RUNNING" => {
                eprintln!(
                    "Status: {} - State: {} - Elapsed: {:?}s",
                    status.status,
                    status.state.unwrap_or_default(),
                    status.elapsed_time
                );
                std::thread::sleep(Duration::from_secs(15));
            }
            "SUCCEEDED" => {
                // Step 5: Download the receipt
                let receipt_url = status.receipt_url
                    .expect("Missing receipt URL");
                let receipt_buf = client.download(&receipt_url)?;
                let receipt: Receipt = bincode::deserialize(&receipt_buf)?;
                
                // Verify the receipt
                receipt.verify(METHOD_ID)?;
                return Ok(receipt);
            }
            _ => {
                panic!(
                    "Session failed: {} - Error: {}",
                    status.status,
                    status.error_msg.unwrap_or_default()
                );
            }
        }
    }
}

API Reference

Image Management

Upload your zkVM program (ELF binary or MemoryImage):
// Upload ELF from memory
let image_id = hex::encode(compute_image_id(METHOD_ELF)?);
let already_exists = client.upload_img(&image_id, elf_bytes)?;

Input Management

Upload input data for your zkVM program:
let input_data = to_vec(&my_input).unwrap();
let input_bytes = bytemuck::cast_slice(&input_data).to_vec();
let input_id = client.upload_input(input_bytes)?;

Session Management

Create and manage proving sessions:
// Create a session
let session = client.create_session(
    image_id,       // Image ID from upload
    input_id,       // Input ID from upload  
    assumptions,    // Vec of assumption receipt UUIDs
    execute_only    // true for execution only, false for proving
)?;

// Create session with custom cycle limit
let session = client.create_session_with_limit(
    image_id,
    input_id,
    assumptions,
    execute_only,
    Some(100_000_000) // Optional cycle limit
)?;

Session Status

Check the status of a proving session:
let status = session.status(&client)?;

match status.status.as_str() {
    "RUNNING" => {
        println!("State: {:?}", status.state);
        println!("Elapsed: {:?}s", status.elapsed_time);
    }
    "SUCCEEDED" => {
        let receipt_url = status.receipt_url.unwrap();
        let receipt_buf = client.download(&receipt_url)?;
    }
    "FAILED" | "TIMED_OUT" | "ABORTED" => {
        eprintln!("Error: {:?}", status.error_msg);
    }
    _ => {}
}
Status Values:
  • RUNNING - Session is currently proving
  • SUCCEEDED - Proof completed successfully
  • FAILED - Session failed with an error
  • TIMED_OUT - Session exceeded time limit
  • ABORTED - Session was manually stopped
Proving States (when status is RUNNING):
  • Setup - Initializing the session
  • Executor - Running the zkVM execution
  • ProveSegments: N/M - Proving individual segments
  • Planner - Planning recursion strategy
  • Recursion - Performing recursive proving
  • RecursionJoin: N/M - Joining recursive proofs
  • Resolve - Resolving assumptions
  • Finalize - Finalizing the receipt

Session Operations

// Retrieve zkVM guest stdout/stderr
let logs = session.logs(&client)?;
println!("Guest output: {}", logs);

Receipt Management

// Download receipt from a previous session
let receipt_bytes = client.receipt_download(&session_id)?;
let receipt: Receipt = bincode::deserialize(&receipt_bytes)?;

// Upload assumption receipts
let receipt_bytes = bincode::serialize(&receipt)?;
let receipt_id = client.upload_receipt(receipt_bytes)?;

STARK to SNARK Conversion

Convert a STARK proof to a Groth16 SNARK:
use std::time::Duration;

// After getting a successful STARK session
let snark_session = client.create_snark(session.uuid)?;

loop {
    let status = snark_session.status(&client)?;
    
    match status.status.as_str() {
        "RUNNING" => {
            eprintln!("SNARK proving in progress...");
            std::thread::sleep(Duration::from_secs(15));
        }
        "SUCCEEDED" => {
            let snark_url = status.output.unwrap();
            let snark_buf = client.download(&snark_url)?;
            let snark_receipt: Receipt = bincode::deserialize(&snark_buf)?;
            break;
        }
        _ => {
            panic!("SNARK failed: {:?}", status.error_msg);
        }
    }
}
See Groth16 SNARK Generation for more details.

Account Information

let quotas = client.quotas()?;
println!("Cycle budget: {}", quotas.cycle_budget);
println!("Concurrent proofs: {}", quotas.concurrent_proofs);
println!("Executor limit: {} million cycles", quotas.exec_cycle_limit);

Error Handling

The SDK uses the SdkErr enum for error handling:
use bonsai_sdk::SdkErr;

match client.upload_img(&image_id, elf_bytes) {
    Ok(_) => println!("Upload successful"),
    Err(SdkErr::MissingApiKey) => {
        eprintln!("BONSAI_API_KEY not set");
    }
    Err(SdkErr::MissingApiUrl) => {
        eprintln!("BONSAI_API_URL not set");
    }
    Err(SdkErr::InternalServerErr(msg)) => {
        eprintln!("Server error: {}", msg);
    }
    Err(e) => eprintln!("Error: {:?}", e),
}

Async API

The SDK also provides an async API via the non_blocking module:
use bonsai_sdk::non_blocking::Client;

#[tokio::main]
async fn main() -> Result<()> {
    let client = Client::from_env(risc0_zkvm::VERSION)?;
    
    let image_id = hex::encode(compute_image_id(METHOD_ELF)?);
    client.upload_img(&image_id, METHOD_ELF.to_vec()).await?;
    
    let input_id = client.upload_input(input_bytes).await?;
    
    let session = client.create_session(
        image_id,
        input_id,
        vec![],
        false
    ).await?;
    
    let status = session.status(&client).await?;
    // ... rest of proving workflow
    
    Ok(())
}

Best Practices

Image Caching: The SDK automatically checks if an image already exists before uploading. Reuse the same image_id across sessions to avoid redundant uploads.
Polling Frequency: Poll for session status every 10-15 seconds to balance responsiveness with API rate limits.
Timeout Configuration: For long-running proofs, increase BONSAI_TIMEOUT_MS or set it to "none" to disable timeouts on the HTTP client.

Next Steps

Remote Proving

Learn about Boundless remote proving

Groth16 Proofs

Generate blockchain-ready SNARK proofs

Local Proving

Set up local proving for development

Proof Composition

Compose multiple proofs together