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: application examples use public crates.io releases by default. A pinned official GitHub revision is the fallback when the required public release is unavailable. A local path is an explicit, nonportable framework-development mode only.
Use when
Use this playbook when a Rust application embeds Stoffel compilation, bytecode loading, local execution, client/server builders, typed client IO bindings, or network/off-chain integration.Current source of truth
crates/stoffel-rust-sdk/README.mdcrates/stoffel-rust-sdk/src/lib.rscrates/stoffel-rust-sdk/src/prelude.rscrates/stoffel-rust-sdk/src/runtime.rscrates/stoffel-rust-sdk/src/config.rscrates/stoffel-rust-sdk/src/types.rscrates/stoffel-rust-sdk/examples/*
Dependencies
Use the released SDK dependency from the current Rust SDK installation docs. Keep the placeholder below synchronized with that page rather than copying a release number into this skill:path = "../stoffel/..." as an application default, and do not combine path with version or git to make a locally dependent manifest appear portable.
Use use stoffel::prelude::*; for app code.
Path discipline
Rust file APIs resolve relative paths from the process current directory, which may differ under tests, services, IDEs, and CI. Root app-owned paths at the Cargo manifest instead:app_path("src/main.stfl"), app_path("artifacts/program.stflb"), and similar values with compile_file, load_file, and bytecode save/load calls. Accept deployment paths as explicit configuration when artifacts live outside the app; never depend on cd having been run first.
Clear local execution
Production-shaped client integration
For application integration, design toward deployed services and packaged artifacts: build bytecode once, deploy MPC nodes separately, and have each participant-owned client load deployment config plus typed bindings. For client-owned private input, that participant process submits directly to the MPC service; an application backend remains outside the plaintext path. Use local MPC as the development smoke path, not the production topology. Client input ownership rule: privateClientStore values originate in the SDK client. The client validates the generated input shape, encodes its own vector, and submits it through the client/coordinator protocol. Application servers and SDK server/node builders receive deployment configuration and value-blind protocol metadata; they must not receive participant values or proxy plaintext private inputs. Do not add per-client value payloads to ServerBuilder to model client slots or input ranges.
.stfl source dynamically for every request. A backend gateway that accepts participant plaintext is a separate, degraded-trust architecture and requires explicit approval.
Local MPC execution
Use local MPC to verify program semantics before deploying..execute_local().await? spawns a local MPC test network on the developer machine, and one harness process may see every fixture input. It does not prove that a production application service is outside the plaintext path.
Loading and saving bytecode
Builder options to know
Program source:Stoffel::compile(source)Stoffel::compile_file(path)Stoffel::load(bytes)Stoffel::load_file(path)
.parties(n).threshold(t).instance_id(id).honeybadger().avss(Curve::Bls12_381)/.curve(curve).backend(MpcBackend::...).manifest::<ProgramManifest>()when using generated bindings
.optimize(bool).optimization_level(0..=3).print_ir(bool).compiler_options(CompilationOptions { ... })
.with_input("a", 40_i64).with_inputs(&[("a", 40_i64), ("b", 2_i64)]).with_client_input(0, &[40_i64, 2_i64]).with_client_inputs(&[(0, vec![...])]).expected_output_clients(n)
.build().summary()/runtime.summary().to_bytecode()/runtime.to_bytecode().save_bytecode(path)/runtime.save_bytecode(path).execute_clear().execute_local().execute_local_function("entry")/ timeout variants where appropriateruntime.client(),runtime.server(party_id),runtime.offchain_client_config(slot)
SDK value model
Usestoffel::Value at the SDK boundary:
Value::I64,Value::U64,Value::Bool,Value::Float,Value::String,Value::Bytes,Value::List,Value::Object,Value::Unit.- Convenience accessors:
as_i64,as_u64,as_bool,as_f64,as_str,as_bytes,as_list,as_object,is_unit.
- integer shares ->
i64 - unsigned integer shares ->
i64/integer Rust fields at generated boundary depending on manifest mapping - boolean secret integers ->
bool - fixed-point shares ->
f64
Network config builders
For deployment-oriented code, use builders instead of hand-rolled maps:Validation / done criteria
For a generated or handed-off Rust app, commitCargo.lock and use the locked graph in verification and CI:
cargo-metadata.json: crates.io packages have a registry+... source; the fallback has a git+https://github.com/Stoffel-Labs/stoffel.git?...#<full-sha> source. A null source means a path/workspace package and fails the portable-app audit.
The final portability proof must run from a clean checkout outside the Stoffel framework repository and without an adjacent ../stoffel directory:
Common pitfalls
- Do not use path dependencies as the default after crates.io publication.
- Do not accept
git = "...", branch = "main"; use the official URL and a fullrev. - Do not omit a generated app’s
Cargo.lockor silently drop--lockedin CI. - Do not treat a successful build inside the framework checkout as portability proof; workspace inheritance and nearby paths can hide leaks.
- Do not simulate protocol behavior in app code; use SDK/runtime execution paths.
- Do not route
ClientStorevalues through an application server or add participant input payloads to SDK server/node builders. Input ownership and submission belong to the SDK client. - Do not present
.execute_local().await?as a production deployment path. - Do not compile
.stflsource dynamically inside production clients; load pinned bytecode and generated metadata. - Do not set an explicit backend that conflicts with bytecode metadata. Prefer generated manifests for ClientStore programs.
- For
ClientStoreapps, validate client input shapes before network submission. - Do not use
stoffel-rust-sdkas the binding generator build-dependency. Use the matching publicstoffel-bindgencrate under[build-dependencies]as shown in the typed-bindings playbook.