Skip to main content
A Stoffel app starts with a product question, not a protocol question:
That answer defines the privacy boundary. The host app can still own routing, accounts, public metadata, UI, persistence, and ordinary business rules. Stoffel owns the privacy-sensitive computation over private inputs and opens only the result the app is allowed to reveal. Use this page while adapting the tutorial path: The progression is app-led: see the full Rust service first, then use the focused tutorials to deepen the mental models.

Core terms

Boundary design checklist

Before writing StoffelLang, write down:
  1. Which product feature needs private computation?
  2. Who owns each private input?
  3. Which facts are already public?
  4. Which intermediate facts would be harmful in logs, analytics, admin tools, support exports, or another user’s view?
  5. What exact output does the app need?
  6. Which recipient receives that output?
  7. What can the recipient infer from the output?
  8. Which facts must never be opened by this program?
Then encode that boundary in code:
  • client payload structs own private input values;
  • ClientStore.take_share(...) loads private input slots;
  • normal Rust code validates public app shape and maps outputs into domain types;
  • StoffelLang computes over shares;
  • .reveal(), Share.open(...), MpcOutput.send_to_client(...), or Share.send_to_client(...) appears only at the intended output boundary.

Example: private lottery

The product needs to announce a winner. It does not need the operator, support team, analytics job, or other participants to see the full draw board. The private computation can reveal the winner without giving the provider a board it can rerun, tune, export, or use to rank non-winners.

Example: private matchmaking

The product needs to route matches. It does not need the platform to learn every raw vector, score, ranking, and rejection path. The final match still leaks something: a user learns who they matched with. The privacy boundary prevents that necessary output from expanding into a score table or preference dossier.

Example: private state transition

In a Battleship-style game, the app needs to advance one shot. It does not need a global plaintext god view of all hidden boards. Recipient-specific rules matter. A public combat log, private result, and delayed reveal policy are different products with different leakage.

Common mistakes

  • Opening helper values because they are convenient to debug.
  • Returning a whole board, ranking table, or score vector when the product needs one decision.
  • Treating encrypted storage as enough when the backend still decrypts every input to compute.
  • Aggregating all private inputs into one synthetic client slot when the product has distinct participants.
  • Hiding product policy inside MPC when it can safely remain ordinary Rust logic.
  • Saying “the server learns less” without naming who no longer sees which fact.

Next steps