Skip to main content
Scope: AI-agent-agnostic playbook for building applications with the Stoffel framework. This is not a maintainer guide for compiler, VM, protocol, or release engineering work. Dependency assumption: app dependencies come from current public crates.io releases by default. Use a full pinned revision of the official GitHub repository only as fallback; reserve local paths for explicit nonportable framework development.

Use when

Use this playbook when an app needs local MPC smoke testing, ClientStore input runs, hot reload, or SDK local coordinator-backed execution.

Goal

Give developers a repeatable local loop for testing private/MPC apps before any real network deployment. Local MPC is a verification gate, not the production topology.

Current source of truth

  • crates/stoffel-cli/src/main.rs
  • crates/stoffel-rust-sdk/README.md
  • crates/stoffel-rust-sdk/src/runtime.rs
  • crates/stoffel-lang/examples/README.md
  • crates/stoffel-lang/examples/**/*.stfl

CLI local run

Local mode is the default unless --network or --config is set.

Hot reload

Use --once for CI/smoke checks and default watch mode during interactive development.

Input paths

Named function args:
ClientStore values:
The CLI also supports --input-file and --client-input-file for .json, .csv, and .txt inputs. See Stoffel CLI App Workflow.

Use example run-args headers

Many secret examples include the exact local flags in the first source line:
Run by appending those flags:
For repeated client slots, order matters: --client-input 0=50 --client-input 0=20 maps to ClientStore.take_share(0, 0) then ClientStore.take_share(0, 1).

SDK local run

For a Rust app, use the version from the current SDK installation docs and commit Cargo.lock:
If a needed change is not released, use git = "https://github.com/Stoffel-Labs/stoffel.git" with rev = "<full-40-character-commit-sha>". Do not substitute branch = "main". A path = "../stoffel/crates/stoffel-rust-sdk" dependency is nonportable and is valid only when deliberately testing framework source.
Builder shortcut:
  1. Resolve the app root from an explicit argument, manifest, or the script’s own location; do not assume the caller’s CWD.
  2. Run stoffel status --verbose "$APP_ROOT".
  3. Run stoffel check "$APP_ROOT" to catch syntax/config/type errors.
  4. Run stoffel build "$APP_ROOT" --program-info to inspect bytecode and client IO metadata.
  5. Run stoffel run "$APP_ROOT" --timeout-secs 180 with named inputs or documented # run-args: flags.
  6. If using Rust, run cargo check --locked and cargo run --locked with an explicit --manifest-path against the same bytecode/source.
  7. Inspect cargo metadata --locked and prove the app in a clean external checkout.
  8. Record the exact command/output in the app handoff.
  9. Only then move to network/off-chain config with Stoffel Deployment Runbook.
For a repository script, derive a stable root from the script path:
The Stoffel package in metadata must have a registry+... source or a pinned official git+...#<full-sha> source. source: null exposes a local path/workspace dependency.

Validation / done criteria

For app local-MPC work:
Commit Cargo.lock, then clone the app into a temporary directory outside the framework checkout (with no sibling ../stoffel) and rerun the locked check and local smoke. A pass inside the framework repository alone is insufficient portability proof. For framework example validation:

Common pitfalls

  • Compile-only success is not a local MPC smoke test.
  • A local MPC pass backed by an adjacent path dependency is not a portable app proof.
  • Do not let cargo run update the graph implicitly; commit the lockfile and use --locked.
  • Do not encode framework checkout locations or assume commands start at the repository root.
  • Local MPC success is not production deployment; it only proves the program and app boundary work on the local test network.
  • Increase --timeout-secs before assuming protocol failure.
  • Avoid port/process collisions by serializing tests that spawn local party meshes.
  • Keep ClientStore inputs separate from named function inputs.
  • Do not omit --expected-output-clients for examples/programs that send client outputs.
  • AVSS support is backend/curve/input dependent; verify the current SDK boundary.