- Private matchmaking gives the full app-shaped pattern.
- Private lottery isolates the authorized-output/reveal-boundary pattern.
- N-party Battleship isolates hidden state and recipient-specific reveal policies.
Core terms
Boundary design checklist
Before writing StoffelLang, write down:- Which product feature needs private computation?
- Who owns each private input?
- Which facts are already public?
- Which intermediate facts would be harmful in logs, analytics, admin tools, support exports, or another user’s view?
- What exact output does the app need?
- Which recipient receives that output?
- What can the recipient infer from the output?
- Which facts must never be opened by this program?
- 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(...), orShare.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.