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:
Variable Description Required BONSAI_API_URLAPI endpoint URL Yes BONSAI_API_KEYYour API key Yes BONSAI_TIMEOUT_MSRequest timeout in ms (default: 30000) No RISC0_RELEASE_CHANNELRelease channel for software versions No
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 from Buffer
Upload from File
Check Existence
Delete Image
// Upload ELF from memory
let image_id = hex :: encode ( compute_image_id ( METHOD_ELF ) ? );
let already_exists = client . upload_img ( & image_id , elf_bytes ) ? ;
Upload input data for your zkVM program:
Upload Input Buffer
Upload Input File
Delete Input
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
Get Logs
Stop Session
Execute Only Mode
// 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.
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