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: use public crates.io releases by default, a full pinned revision of the official repository as fallback, and local paths only in explicit nonportable framework-development mode.
Use when
Use this playbook when a Stoffel app usesClientStore and a Rust client/server wants compile-time input/output structs generated from the exact app bytecode.
Goal
Generate Rust bindings from the exact.stflb program the app will execute, use those bindings for typed client IO, and let the manifest select/validate backend and client-slot IO shape.
Current source of truth
crates/stoffel-rust-sdk/src/codegen.rscrates/stoffel-rust-sdk/src/types.rscrates/stoffel-rust-sdk/src/program.rscrates/stoffel-rust-sdk/src/client.rscrates/stoffel-rust-sdk/tests/sdk_usage.rscrates/stoffel-rust-sdk/tests/compile_fail.rs
Cargo dependency roles
The runtime SDK belongs in[dependencies]; the dedicated generator belongs in [build-dependencies]. Use the matching versions from the current Rust SDK installation docs:
stoffel-rust-sdk in [build-dependencies] to generate bindings. Do not emit local path = "../stoffel/..." entries except in explicitly labeled, nonportable framework-development manifests.
Mode A: exact-bytecode bindings
Use this mode for deployment and whenever bindings must describe a specific.stflb artifact. Keep the artifact under the app repository (for example artifacts/program.stflb), and generate only into Cargo’s OUT_DIR:
stoffel_bindgen::generate_bindings_with_config with the same rooted input/output paths and stoffel_bindgen::BindingsConfig.
Runtime code must load the same artifact without assuming the process CWD:
Mode B: source-generated bindings
Use source mode only when the application intentionally compiles source during its Cargo build. It is convenient for development but does not prove that bindings match a separately deployed bytecode file:cargo:rerun-if-changed=... line for every imported source or other generator input. If the bytecode is built by a separate command, do not blur the modes: build it first, then use exact-bytecode mode against that output.
Generated shapes
The generator emits:ProgramManifestimpl stoffel::GeneratedProgramManifest for ProgramManifestClient{slot}Inputsfor each client slot with declared inputsClient{slot}Outputsfor each client slot with declared outputs- ordered fields such as
input_0,input_1,output_0 TypedClientInputs/TypedClientOutputsimplementations
- integer shares ->
i64 - boolean secret integers ->
bool - fixed-point shares ->
f64
ProgramManifest and a comment that no client IO was declared.
Use manifest-backed config
Typed client call
Bytecode must be the contract
Treat.stflb as the app/client contract:
- Write or update
.stflsource. - Build bytecode with the same backend/curve/topology assumptions that will be used at runtime.
- Generate Rust bindings from that bytecode.
- Compile the Rust client/server code.
- At runtime, load the same bytecode and validate manifest/client IO shape before submitting inputs.
Multi-client and ordered-input guidance
If a program has:Validation / done criteria
- Regenerate bindings after bytecode changes.
- Commit the generated app’s
Cargo.lock; runcargo check --lockedto catch type mismatches. - Run the app’s local smoke with the same bytecode.
- For network/off-chain submissions, validate the runtime’s program manifest against generated types before submitting.
stoffel-rust-sdk and stoffel-bindgen must resolve from registry+... or the same pinned official git+...#<full-sha>. A null package source is a local path/workspace leak. Final proof is cargo check --locked --manifest-path ... from a clean checkout outside the Stoffel repository, with no adjacent framework checkout.
Framework tests:
Common pitfalls
- Bindings must come from the exact
.stflbdeployed/executed. - Rebuild bytecode and regenerate bindings after any source, backend, or curve change.
- Do not hand-edit generated binding files.
- Do not bypass manifest validation when network clients submit real inputs.
- Do not assume slot order from Rust struct field order alone; it follows ordered ClientStore metadata from bytecode.
- Wrong:
stoffel = { path = "../stoffel/crates/stoffel-rust-sdk" }in a distributable app. Right: crates.io, or the official Git URL plus fullrev. - Wrong:
stoffel::generate_bindings(...)from an SDK build-dependency. Right:stoffel_bindgenin[build-dependencies]. - Wrong: input/output paths relative to process CWD or generated files written into
src/. Right: inputs underCARGO_MANIFEST_DIR, outputs underOUT_DIR, and explicit rerun directives. - Wrong: generating from source and claiming an independently built deployment artifact is identical. Use exact-bytecode mode for that claim.