Skip to main content
Most developers should use stoffel run, stoffel dev, or the Rust SDK. These surfaces compile/load .stflb bytecode and run it through clear checks, local MPC testing, or configured network execution. Use direct stoffel-vm APIs only for VM tests, bytecode tooling, custom embedding, or runtime instrumentation.

Build bytecode

For projects, prefer stoffel build:
stoffel check
stoffel build
stoffel build --release
stoffel build reads Stoffel.toml and writes .stflb artifacts under target/debug/ or target/release/. For one selected source file, use stoffel compile with an explicit output path:
stoffel compile src/main.stfl --output target/debug/main.stflb
stoffel compile src/main.stfl -O3 --output target/release/main.stflb

Inspect bytecode

stoffel compile --disassemble target/debug/main.stflb
stoffel run --program-info
stoffel check --print-ir
Use these commands to inspect function metadata, generated instructions, bytecode shape, and runtime program information before debugging low-level behavior.

Run through the CLI

stoffel run
stoffel run target/debug/main.stflb --entry main
stoffel run src/main.stfl --input a=40 --input b=2
stoffel run uses the local MPC test network unless you pass --network and --config for a deployed network configuration. For ClientStore programs, provide client-slot inputs:
stoffel run \
  --client-input 0=42 \
  --expected-output-clients 1 \
  --parties 5 \
  --threshold 1
The first number in --client-input 0=42 is the client slot. Repeating the same slot appends inputs for that client slot in order.

Watch mode

stoffel dev --client-input 0=42 --parties 5 --threshold 1
stoffel dev --once --client-input 0=42 --parties 5 --threshold 1
Use --once for CI/scripts. Omit it while editing to rebuild and rerun when files change.

Run through the Rust SDK

Clear execution is useful for fast logic checks without MPC networking:
use stoffel::prelude::*;

fn main() -> stoffel::Result<()> {
    let result = Stoffel::compile("def main(a: int64, b: int64) -> int64:\n  return a + b")?
        .with_inputs(&[("a", 40_i64), ("b", 2_i64)])
        .execute_clear()?;

    println!("Result: {}", result[0]);
    Ok(())
}
For app-shaped local MPC testing, load the bytecode that the CLI built:
use stoffel::prelude::*;

# async fn example() -> stoffel::Result<()> {
let result = Stoffel::load_file("target/debug/hello-mpc.stflb")?
    .parties(5)
    .threshold(1)
    .with_client_input(0, &[42_i64])
    .execute_local()
    .await?;
# Ok(())
# }
.execute_local().await? runs local MPC testing by spawning several MPC nodes/processes on your machine.

When to use direct VM APIs

Use direct VirtualMachine / VMFunction APIs when you are:
  • writing VM unit tests;
  • building bytecode-generation tooling;
  • testing the instruction set directly;
  • experimenting with hooks or custom output sinks;
  • integrating a custom table-memory or local-storage backend.
Do not use direct VM APIs as the first path for application docs. For applications, build bytecode with the CLI and load it through the Rust SDK.

Debugging checklist

  • Use stoffel check --print-ir before bytecode generation.
  • Use stoffel compile --disassemble ... to inspect instructions.
  • Use stoffel run --program-info to inspect loaded functions and metadata.
  • For MPC programs, confirm parties, threshold, client inputs, and --expected-output-clients match the program’s ClientStore and output behavior.
  • Use VM hooks or direct VM construction only when CLI/SDK-level inspection is not enough.

See also