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.

Guest and Host Programs

RISC Zero applications follow a unique architecture that separates code into two distinct execution environments: the guest (running inside the zkVM) and the host (running outside the zkVM). Understanding this separation is crucial for effective zkVM development.

The Guest/Host Model

What is the Guest?

The guest is code that executes inside the zkVM. It represents the computation you want to prove:
  • Runs in a RISC-V environment
  • All execution is proven
  • Has access to private inputs from the host
  • Can write public outputs to the journal
  • Cannot perform unproven operations (network I/O, system calls)

What is the Host?

The host is standard code that runs outside the zkVM:
  • Manages guest execution
  • Provides input data to the guest
  • Generates and verifies proofs
  • Handles I/O, networking, and other system operations
  • Can be written in any language (typically Rust)
Think of the guest as the “prover’s secret code” and the host as the “coordinator” that sets up execution and manages proofs.

Communication Model

The guest and host communicate through well-defined channels:
┌─────────────────────────────────────────────┐
│              HOST PROGRAM                   │
│                                             │
│  ┌──────────────────────────────────────┐  │
│  │  ExecutorEnv (Input Data)            │  │
│  └───────────────┬──────────────────────┘  │
│                  │ write()                  │
│                  ▼                          │
│  ┌─────────────────────────────────────┐   │
│  │       GUEST PROGRAM (zkVM)          │   │
│  │                                     │   │
│  │  env::read()  ──────► Process      │   │
│  │                         │           │   │
│  │                         ▼           │   │
│  │                   env::commit()    │   │
│  └─────────────┬───────────────────────┘   │
│                │ Journal Output             │
│                ▼                            │
│  ┌─────────────────────────────────────┐   │
│  │  Receipt (Proof + Journal)          │   │
│  └─────────────────────────────────────┘   │
└─────────────────────────────────────────────┘

Input: Host → Guest

The host sends data to the guest using the ExecutorEnv:
use risc0_zkvm::ExecutorEnv;

// Host code: prepare input
let env = ExecutorEnv::builder()
    .write(&secret_a)?
    .write(&secret_b)?
    .build()?;
The guest receives this data with env::read():
use risc0_zkvm::guest::env;

// Guest code: read input
let a: u64 = env::read();
let b: u64 = env::read();
Input data is read in FIFO order - the guest reads data in the same sequence the host wrote it.

Output: Guest → Host

The guest writes public outputs to the journal with env::commit():
// Guest code: write output
let result = a * b;
env::commit(&result);
The host extracts journal data from the receipt:
// Host code: extract output
let result: u64 = receipt.journal.decode()?;
println!("Result: {}", result);

Writing Guest Code

Guest Structure

A minimal guest program looks like this:
#![no_main]
#![no_std]

use risc0_zkvm::guest::env;

risc0_zkvm::guest::entry!(main);

fn main() {
    // Read inputs
    let a: u64 = env::read();
    let b: u64 = env::read();
    
    // Perform computation
    let product = a.checked_mul(b).expect("Integer overflow");
    
    // Commit public output
    env::commit(&product);
}
Key elements:
  • #![no_main]: Guest uses custom entry point
  • #![no_std]: No standard library by default (use std feature if needed)
  • risc0_zkvm::guest::entry!(main): Defines the guest entry point
By default, guest code runs in a no_std environment to minimize the code size and complexity. This means:
  • Only core library is available
  • No heap allocation by default
  • No file system, networking, or OS features
You can enable std support by adding the feature flag:
[dependencies]
risc0-zkvm = { version = "1.0", default-features = false, features = ["std"] }
With std enabled, you can omit #![no_main] and #![no_std], and use many standard library features.

Guest I/O Functions

The env module provides several I/O functions (from risc0/zkvm/src/guest/env/mod.rs):

Reading Input

// Read any deserializable type
let data: MyStruct = env::read();

// Read raw bytes (more efficient)
let mut buffer = [0u8; 1024];
env::read_slice(&mut buffer);

// Access stdin directly
use risc0_zkvm::guest::env::stdin;
let mut stdin = stdin();

Writing Output

// Commit to journal (public output)
env::commit(&public_data);

// Commit raw bytes
env::commit_slice(&bytes);

// Write to stdout (debugging)
env::write(&debug_info);

// Access journal directly
use risc0_zkvm::guest::env::journal;
let mut journal = journal();

File Descriptors

Guest code can access standard file descriptors:
  • stdin(): Read private input from host
  • stdout(): Write data (not proven)
  • stderr(): Write error messages (not proven)
  • journal(): Write public outputs (proven)
Only data written to the journal via env::commit() is proven and publicly verifiable. Data written to stdout/stderr is for debugging only.

Guest Control Flow

use risc0_zkvm::guest::env;

// Normal exit (success)
env::exit(0);

// Exit with error code
env::exit(1);

// Pause execution (can be resumed)
env::pause(0);

Writing Host Code

Host Structure

A complete host program:
use risc0_zkvm::{default_prover, ExecutorEnv};
use my_methods::MULTIPLY_ELF;

fn main() {
    // 1. Prepare input
    let a = 17u64;
    let b = 23u64;
    
    // 2. Build execution environment
    let env = ExecutorEnv::builder()
        .write(&a).unwrap()
        .write(&b).unwrap()
        .build()
        .unwrap();
    
    // 3. Obtain prover
    let prover = default_prover();
    
    // 4. Execute and prove
    let prove_info = prover.prove(env, MULTIPLY_ELF).unwrap();
    
    // 5. Extract receipt
    let receipt = prove_info.receipt;
    
    // 6. Decode output
    let result: u64 = receipt.journal.decode().unwrap();
    println!("Result: {}", result);
}

Execution Environment Options

The ExecutorEnvBuilder supports many configuration options:
let env = ExecutorEnv::builder()
    // Input data
    .write(&data)?
    .write_slice(&bytes)
    
    // Environment variables
    .env_var("DEBUG", "1")
    
    // Segment configuration
    .segment_limit_po2(20)  // 2^20 cycles per segment
    
    // Add trace callback for debugging
    .trace_callback(my_trace_fn)
    
    // Include assumption receipts (for proof composition)
    .add_assumption(assumption_receipt)
    
    .build()?;
Lowering segment_limit_po2 reduces memory usage. A value of 20 (2^20 = ~1M cycles) is typical. Decreasing by 1 roughly halves memory consumption.

Prover Options

The host can choose different provers and receipt types:
use risc0_zkvm::{ProverOpts, ReceiptKind};

let opts = ProverOpts {
    receipt_kind: ReceiptKind::Succinct,  // or Groth16, Composite
};

let prove_info = prover.prove_with_opts(env, ELF, &opts)?;
Receipt kinds:
  • Succinct: Recursively composed STARK (constant size)
  • Groth16: SNARK for on-chain verification
  • Composite: Multiple receipts with assumptions

Development Mode

During development, enable dev mode to skip actual proving:
export RISC0_DEV_MODE=1
This generates fake receipts instantly, useful for:
  • Testing logic without waiting for proofs
  • Debugging guest code
  • Rapid iteration
Never use dev mode in production! Fake receipts provide no security guarantees. Use the disable-dev-mode feature flag to prevent accidental misuse.

Advanced Guest Patterns

Proof Composition

Guests can verify other receipts, enabling modular proofs:
use risc0_zkvm::guest::env;

// Guest verifies another receipt
let receipt_data: Vec<u8> = env::read();
let receipt: Receipt = bincode::deserialize(&receipt_data)?;

receipt.verify(EXPECTED_IMAGE_ID)
    .expect("Receipt verification failed");

// Use the verified receipt's journal
let input: DataType = receipt.journal.decode()?;

Accessing Verified Outputs

When verifying receipts in the guest:
use risc0_zkvm::guest::env;

// Verify and extract output in one call
env::verify(receipt_bytes, IMAGE_ID)?;

// Or verify integrity only (doesn't check image ID or exit code)
env::verify_integrity(receipt_bytes)?;

Private vs Public Data

Design your guest to maintain privacy:
// Guest code
let private_input: Secret = env::read();  // Never revealed
let public_param: u64 = env::read();      // Could be committed or not

let result = compute_with_secret(private_input, public_param);

// Only commit what should be public
env::commit(&result);  // Proven output

Common Patterns

Batch Processing

// Host
let items: Vec<Item> = load_items();
let env = ExecutorEnv::builder()
    .write(&items)?
    .build()?;

// Guest
let items: Vec<Item> = env::read();
let results: Vec<Output> = items.iter().map(process).collect();
env::commit(&results);

Conditional Output

// Guest
let data: InputData = env::read();

if data.is_valid() {
    let result = process(data);
    env::commit(&result);
    env::exit(0);
} else {
    env::exit(1);  // Error exit
}

Incremental Computation

// Guest can pause and resume
for batch in batches {
    process_batch(batch);
    env::commit(&intermediate_state);
    env::pause(0);  // Can resume later
}
env::exit(0);

Performance Tips

Use Slice Methods

For large data, use _slice variants:
// Less efficient (serialization overhead)
let data: Vec<u8> = env::read();

// More efficient (direct memory copy)
let mut buffer = vec![0u8; size];
env::read_slice(&mut buffer);

Minimize Journal Writes

Journal writes are proven operations:
// Less efficient - multiple commits
for item in items {
    env::commit(&process(item));
}

// More efficient - single commit
let results: Vec<_> = items.iter().map(process).collect();
env::commit(&results);

Cycle Counting

Profile guest performance:
use risc0_zkvm::guest::env;

let start = env::cycle_count();
expensive_operation();
let end = env::cycle_count();

env::log(&format!("Operation took {} cycles", end - start));

Next Steps