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 current public crates.io releases by default, then the official GitHub repository at a full immutable revision when the needed change is not published. A local checkout is a separate, explicitly requested, nonportable framework-development workflow.
Use when
Use this playbook when an app moves beyond local runs and needs client/server builders, network config, off-chain coordinator integration, or typed ClientStore IO against real nodes.Goal
Guide advanced app developers from local bytecode to app-level network/off-chain integration using public SDK builders, while labeling lower-layer behavior with the current component status. For client-owned private input, the participant-owned process is the Stoffel MPC client. It submits directly to the separately deployed MPC service. The application control plane may issue public session configuration and receive non-sensitive receipts or explicitly authorized opened aggregates, but it must not receive or persist participant plaintext.Separate the application roles
Do not combine these roles into oneclient/app layer:
A backend gateway that accepts raw input is a distinct, weaker trust model. Name it and require explicit approval; do not introduce it to work around missing participant-runtime support.
Current source of truth
crates/stoffel-rust-sdk/README.mdcrates/stoffel-rust-sdk/src/runtime.rscrates/stoffel-rust-sdk/src/config.rscrates/stoffel-rust-sdk/src/client.rscrates/stoffel-rust-sdk/src/server.rscrates/stoffel-rust-sdk/src/coordinator/offchain.rscrates/stoffel-rust-sdk/examples/network_config.rscrates/stoffel-rust-sdk/examples/client_server.rs
Preconditions
Before network integration, complete the trust-boundary worksheet in Stoffel Full App Golden Path. Identify the participant runtime, the process that executes client submission, components forbidden from plaintext, the control-plane persistence allowlist, and output recipients. Then verify local program behavior:Portability and provenance preflight
Before interpreting a network result, label where every command ran. Keep these contexts distinct in notes and logs:- app checkout: the consumer application repository;
- framework checkout: a Stoffel source tree, only when intentionally used;
- deployment host: the machine that runs a coordinator, party, or client service;
- container: the image plus the mounts and network namespace visible inside it;
- CI: the runner image and job that reproduce the consumer workflow.
Do not treat a framework-checkout example as consumer proof. The portability gate is a clean external checkout of the app, outside the framework repository, using only documented public CLI and SDK dependencies. It must build and run without uncommitted files, undeclared path dependencies, workspace inheritance from the framework repository, or framework source mounted into a container. Record the external checkout path, repository URL, commit, clean status, dependency source, commands, and result. If this gate was not run, classify the result as not clean-room tested rather than portable.
For a public dependency consumer proof, create or use the smallest app that imports the publicly documented SDK/CLI source, resolves from its committed lockfile, builds bytecode and bindings, and exercises the same client-facing path. A successful framework workspace test is useful framework validation, but is not this proof.
Containers and addresses
- Mount only declared app inputs, generated deployment bundles, state, and identity files. Do not mount a framework checkout, a developer package cache containing unpublished builds, or a host
target/directory into the proof run. - Record host-to-container mount mappings and verify the bytecode/binding hashes inside the container after mounting. Use read-only mounts for release artifacts and config where practical.
- Do not copy
127.0.0.1orlocalhostacross host/container boundaries: loopback names the current network namespace. Record bind addresses separately from advertised/reachable coordinator, mesh, and RPC addresses. - Bind services deliberately, publish only required ports, and test reachability from the actual peer/client context rather than only from the host.
- Pin images by digest and include that digest in the result.
Runtime builders
The SDK runtime exposes app-level builders:Network config concepts
App-level network config must align on:- party id
- bind addresses and server addresses
- expected parties
- expected clients
- threshold
- backend and curve
- preprocessing sizes when required by the selected backend
- deployment-level mapping of party configs
- client slot and ClientStore IO shape
Off-chain ClientStore flow
- Compile/build app bytecode.
- Run local MPC with the same ClientStore inputs and expected output clients.
- Generate typed bindings from that bytecode.
- Build runtime from bytecode and generated manifest.
- Derive off-chain client config for a client slot.
- Attach coordinator address, node endpoints/RPC addresses, timestamp, and client identity material.
- Configure the separately deployed MPC service layer with the same bytecode, topology, backend, and client/output slots.
- Have each participant-owned client submit its own typed input directly to that deployment.
- Reconcile only non-sensitive submission status with the application control plane.
- Deliver typed outputs only to recipients authorized by the privacy worksheet.
- Validate typed outputs and consensus/order evidence where applicable.
Client input ownership boundary
- Each SDK client owns its private
ClientStorevalues and submits its complete typed input vector through the client protocol. - The generated manifest plus client configuration bind a client slot to that vector’s ordered input shape.
- An application server may manage public lifecycle, authorization, and bootstrap metadata, but it must not receive or proxy plaintext private inputs.
- Coordinator and party services may validate value-blind session, identity, slot, range, and topology metadata and process protocol messages. That does not make them application-level input owners.
- Do not invent server-builder APIs for participant values. If a client transport is missing, implement or fix the SDK client transport instead of moving input ownership to the server.
Control-plane bootstrap and receipts
A control plane may return public session configuration such as:Participant runtime capability gate
Resolve this before implementation:CLI network execution
The CLI can execute against a network config:--config is network/off-chain client config, not app Stoffel.toml.
Validation / done criteria
- Local smoke test passes first.
- Bytecode hash and generated bindings are recorded together.
- Coordinator address, node mesh addresses, node RPC addresses, identity material, and expected client certificates are explicitly configured or listed as operator handoff fields.
- Network config validates before starting servers/clients.
- Client IO metadata matches generated bindings.
- Real participant-client/network run returns expected output or a concrete error with logs.
- Control-plane schemas, persistence, logs, and receipts contain no participant plaintext.
- A plaintext canary test confirms private input bypasses application-service requests, storage, queues, caches, traces, analytics, and crash reports.
- The participant runtime has verified direct client-protocol support or an explicit capability blocker/participant-controlled sidecar decision.
- Every output recipient matches the privacy worksheet.
- Any coordinator/network assumptions are labeled with current component status and paired with deployment validation guidance.
- The clean external checkout/public dependency consumer proof passes, or the result is explicitly labeled not clean-room tested.
- The provenance manifest identifies every execution context, dependency source, lockfile, artifact hash, and applicable image digest.
- Services and the client consume the recorded bytecode/binding bundle unchanged; hashes are checked at build, service startup, and client execution boundaries.
- app-checkout check/build/local smoke;
- clean external public-dependency consumer build;
- bytecode/binding hash and lockfile verification;
- container build and digest capture when containers are used;
- config validation and a real service/client smoke test in the relevant network namespaces.
passed, failed, or skipped, and every skipped check must include the exact reason, affected context, and consequence (for example, not clean-room tested or network deployment unverified).
When reporting a failure, include the labeled execution context, exact command and working directory, exit status, first actionable error plus the unabridged log location, repository and dependency provenance, artifact/image hashes, and all skipped checks. Do not replace a failed service run with builder construction or a local-only success.
Common pitfalls
stoffel run --configis network/off-chain config, not projectStoffel.toml.- Do not duplicate lower-level networking/protocol logic in app code.
- Do not treat participant clients and the application control plane as one trust role.
- Do not add plaintext private fields to control-plane endpoints or persistence.
- Do not put
.with_client_input(...)or.execute_local()in a production application-service path. - Do not silently replace missing browser/client support with a plaintext backend gateway.
- Do not bypass typed IO validation for ClientStore apps.
- Do not send participant values through an application server or SDK server/node builder; private input submission belongs to each SDK client.
- Keep on-chain coordinator paths marked advanced until public docs and stable APIs exist.
- Present coordinator/network assumptions with explicit current status and deployment validation guidance.
- Do not move to network debugging until the local loop has produced a real passing or failing run.
- Do not hide missing production process startup behind local SDK examples; record the lower-layer service command or mark it as an operator handoff.
- Do not let an undeclared host mount, path dependency,
[patch], package cache, or loopback address make a container/CI run appear portable.