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.
bonsai-sdk
The bonsai-sdk crate provides a Rust SDK for interacting with Bonsai, RISC Zero’s remote proving service. It supports both blocking and async (non-blocking) APIs.
Installation
[dependencies]
bonsai-sdk = "1.4.3"
Overview
Bonsai is a remote proving service that generates RISC Zero proofs in the cloud. This SDK provides a simple interface to upload programs, submit proving requests, and retrieve results.
You need a Bonsai API key to use this service. Sign up at bonsai.xyz
Feature Flags
Enables async/await API using Tokio.
Modules
blocking
Synchronous/blocking API (default).use bonsai_sdk::blocking::{Client, SessionId};
non_blocking
Asynchronous/non-blocking API (requires non_blocking feature).use bonsai_sdk::non_blocking::{Client, SessionId};
Client
Main client for interacting with Bonsai.pub struct Client {
url: String,
client: HttpClient,
}
Construction
Create a client from environment variables.pub fn from_env(risc0_version: &str) -> Result<Self, SdkErr>
Environment Variables:
BONSAI_API_URL: Bonsai API endpoint
BONSAI_API_KEY: Your API key
BONSAI_TIMEOUT_MS: Request timeout (optional, default: 30000)
RISC0_RELEASE_CHANNEL: Release channel (optional)
Example:let client = Client::from_env(risc0_zkvm::VERSION)?;
Create a client with explicit parameters.pub fn from_parts(
url: String,
key: String,
risc0_version: &str
) -> Result<Self, SdkErr>
Example:let client = Client::from_parts(
"https://api.bonsai.xyz".to_string(),
api_key.to_string(),
risc0_zkvm::VERSION,
)?;
Image Management
Upload a guest program image.pub async fn upload_img(
&self,
image_id: &str,
buf: Vec<u8>
) -> Result<bool, SdkErr>
Parameters:
image_id: Hex-encoded image ID
buf: ELF binary or encoded MemoryImage
Returns: true if image already existed, false if newly uploadedExample:let image_id = hex::encode(compute_image_id(ELF)?).to_string();
client.upload_img(&image_id, ELF.to_vec())?;
Upload a guest program from a file.pub async fn upload_img_file(
&self,
image_id: &str,
path: &Path
) -> Result<bool, SdkErr>
Check if an image exists.pub async fn has_img(&self, image_id: &str) -> Result<bool, SdkErr>
Delete an image.pub async fn image_delete(&self, image_id: &str) -> Result<(), SdkErr>
Upload input data for a proving session.pub async fn upload_input(&self, buf: Vec<u8>) -> Result<String, SdkErr>
Returns: UUID for the uploaded inputExample:let input_data = to_vec(&input)?;
let input_data = bytemuck::cast_slice(&input_data).to_vec();
let input_id = client.upload_input(input_data)?;
Upload input from a file.pub async fn upload_input_file(&self, path: &Path) -> Result<String, SdkErr>
Delete uploaded input.pub async fn input_delete(&self, input_uuid: &str) -> Result<(), SdkErr>
Session Management
Creating Sessions
Create a new proving session.pub async fn create_session(
&self,
img_id: String,
input_id: String,
assumptions: Vec<String>,
execute_only: bool,
) -> Result<SessionId, SdkErr>
Parameters:
img_id: Image ID from upload_img
input_id: Input ID from upload_input
assumptions: List of assumption receipt UUIDs
execute_only: If true, only execute without proving
Example:let session = client.create_session(
image_id,
input_id,
vec![],
false,
)?;
create_session_with_limit
Create a session with custom cycle limit.pub async fn create_session_with_limit(
&self,
img_id: String,
input_id: String,
assumptions: Vec<String>,
execute_only: bool,
exec_cycle_limit: Option<u64>,
) -> Result<SessionId, SdkErr>
SessionId
Represents a proving session.pub struct SessionId {
pub uuid: String,
}
Check session status.pub async fn status(
&self,
client: &Client
) -> Result<SessionStatusRes, SdkErr>
Example:let status = session.status(&client)?;
match status.status.as_str() {
"RUNNING" => println!("Still running..."),
"SUCCEEDED" => println!("Proof complete!"),
"FAILED" => println!("Failed: {}", status.error_msg.unwrap()),
_ => {}
}
Fetch execution logs.pub async fn logs(&self, client: &Client) -> Result<String, SdkErr>
Stop a running session.pub async fn stop(&self, client: &Client) -> Result<(), SdkErr>
SessionId::exec_only_journal
Get journal from execute-only session.pub async fn exec_only_journal(
&self,
client: &Client
) -> Result<Vec<u8>, SdkErr>
SessionStatusRes
Session status response.pub struct SessionStatusRes {
pub status: String, // RUNNING | SUCCEEDED | FAILED | TIMED_OUT | ABORTED
pub receipt_url: Option<String>,
pub error_msg: Option<String>,
pub state: Option<String>, // Current proving stage
pub elapsed_time: Option<f64>,
pub stats: Option<SessionStats>,
}
Receipt Management
Download a completed receipt.pub async fn receipt_download(
&self,
session_id: &SessionId
) -> Result<Vec<u8>, SdkErr>
Example:let receipt_buf = client.receipt_download(&session)?;
let receipt: Receipt = bincode::deserialize(&receipt_buf)?;
Upload a receipt (for assumptions).pub async fn upload_receipt(&self, buf: Vec<u8>) -> Result<String, SdkErr>
Download data from a URL.pub async fn download(&self, url: &str) -> Result<Vec<u8>, SdkErr>
SNARK Support
Create a Groth16 SNARK from a STARK session.pub async fn create_snark(
&self,
session_id: String
) -> Result<SnarkId, SdkErr>
Example:let snark_session = client.create_snark(session.uuid)?;
Represents a SNARK proving session.pub struct SnarkId {
pub uuid: String,
}
Check SNARK session status.pub async fn status(
&self,
client: &Client
) -> Result<SnarkStatusRes, SdkErr>
Quota Management
Get current user quotas and cycle budget.pub async fn quotas(&self) -> Result<Quotas, SdkErr>
Example:let quotas = client.quotas()?;
println!("Cycle budget: {}", quotas.cycle_budget);
println!("Concurrent proofs: {}", quotas.concurrent_proofs);
Get supported Bonsai component versions.pub async fn version(&self) -> Result<VersionInfo, SdkErr>
Error Handling
SDK error types.pub enum SdkErr {
InternalServerErr(String),
HttpErr(reqwest::Error),
HttpHeaderErr(header::InvalidHeaderValue),
MissingApiKey,
MissingApiUrl,
FileNotFound(std::io::Error),
ReceiptNotFound,
}
Examples
Basic Proving Workflow
use bonsai_sdk::blocking::Client;
use risc0_zkvm::{compute_image_id, serde::to_vec, Receipt};
fn prove_on_bonsai() -> Result<()> {
// Create client
let client = Client::from_env(risc0_zkvm::VERSION)?;
// Upload image
let image_id = hex::encode(compute_image_id(METHOD_ELF)?);
client.upload_img(&image_id, METHOD_ELF.to_vec())?;
// Upload input
let input_data = to_vec(&input)?;
let input_data = bytemuck::cast_slice(&input_data).to_vec();
let input_id = client.upload_input(input_data)?;
// Start session
let session = client.create_session(
image_id,
input_id,
vec![],
false,
)?;
// Poll for completion
loop {
let status = session.status(&client)?;
match status.status.as_str() {
"RUNNING" => {
println!("Status: {}", status.state.unwrap_or_default());
std::thread::sleep(Duration::from_secs(15));
}
"SUCCEEDED" => {
let receipt_buf = client.download(&status.receipt_url.unwrap())?;
let receipt: Receipt = bincode::deserialize(&receipt_buf)?;
receipt.verify(METHOD_ID)?;
break;
}
_ => {
eprintln!("Error: {}", status.error_msg.unwrap_or_default());
return Err(anyhow!("Proving failed"));
}
}
}
Ok(())
}
Async/Await (Non-Blocking)
use bonsai_sdk::non_blocking::Client;
#[tokio::main]
async fn prove_async() -> Result<()> {
let client = Client::from_env(risc0_zkvm::VERSION)?;
let image_id = hex::encode(compute_image_id(ELF)?);
client.upload_img(&image_id, ELF.to_vec()).await?;
let input_id = client.upload_input(input_data).await?;
let session = client.create_session(
image_id,
input_id,
vec![],
false,
).await?;
// Poll asynchronously
loop {
let status = session.status(&client).await?;
if status.status == "SUCCEEDED" {
let receipt_buf = client.download(
&status.receipt_url.unwrap()
).await?;
break;
}
tokio::time::sleep(Duration::from_secs(15)).await;
}
Ok(())
}
STARK to SNARK
use bonsai_sdk::blocking::Client;
use std::time::Duration;
fn stark_to_snark(stark_session_uuid: String) -> Result<()> {
let client = Client::from_env(risc0_zkvm::VERSION)?;
// Request SNARK conversion
let snark_session = client.create_snark(stark_session_uuid)?;
// Poll for completion
loop {
let status = snark_session.status(&client)?;
match status.status.as_str() {
"RUNNING" => {
println!("Converting to SNARK...");
std::thread::sleep(Duration::from_secs(15));
}
"SUCCEEDED" => {
let snark_buf = client.download(&status.output.unwrap())?;
let snark_receipt: Receipt = bincode::deserialize(&snark_buf)?;
println!("SNARK proof complete!");
break;
}
_ => {
return Err(anyhow!("SNARK conversion failed: {}",
status.error_msg.unwrap_or_default()));
}
}
}
Ok(())
}
Environment Setup
export BONSAI_API_URL="https://api.bonsai.xyz"
export BONSAI_API_KEY="your_api_key_here"
# Optional settings
export BONSAI_TIMEOUT_MS="60000" # 60 seconds
export RISC0_RELEASE_CHANNEL="stable"
Links