jonnify·codemojex /codemojex/branded-systems/the-privacy-boundary

Codemojex · C1.3

The privacy boundary.

The privacy boundary is structural, not a filter at the edge. The secret exists in exactly one place a player can never read — the games row and its immutable cache copy — and no player-facing view selects it. A player sees their own attempt history and no one else's; the live events and the leaderboard carry a name and a score, never the code or the guess. Privacy is a property of the shape of the reads, enforced in Codemojex.View.

Pick a read below. Each returns only public fields — and the teal chip is the thing it structurally omits.

game_view/1 emojiset snapshotprize · timer · statustotals (post-reveal) omits:secret my_history/3 your emojisyour pointsat_ms omitsothers' guesses leaderboard/2 {player, max_score}highest first omitsguess content
Each read returns only public fields; the teal chip is the value it structurally omits. Source: Codemojex.View.

game_view/1 builds the public map — the emojiset snapshot, the prize, timer, and status, and (post-reveal) the totals. It never puts :secret. Story: there is no :secret key anywhere in the view — only the keyboard snapshot.

§1

Shaped reads, not edge filters

Codemojex.View

Codemojex.View's moduledoc states the invariant plainly: "nothing here returns the secret, and nothing returns another player's guesses." The three reads carry it in their shape, not as a redaction pass. game_view/1 assembles a public map and never puts :secret; my_history/3 calls Store.guesses_for(player, n) — scoped to the caller — and takes [:emojis, :points, :at_ms]; leaderboard/2 returns {player, max_score} rows through Board.top, never the emojis. There is no edge that could forget to redact, because the reads do not select what a player must not see.

the acceptance catalogue — generated from test/stories/privacy_story_test.exsstories
# Scenario: the game view never carries the secret
  Given an open game whose secret was minted server-side
  When  the client fetches the game view
  Then  there is no :secret key anywhere in the view — only the keyboard snapshot

# Scenario: a player's history shows only their own attempts
  Given both players have submitted a guess
  When  Alice reads her history
  Then  she sees her own guess and nothing of Bob's

# Scenario: the leaderboard exposes scores, never guess content
  Given a scored attempt on the board
  When  the leaderboard is read
  Then  every row is a {player, score} pair with no emojis attached
§2

The golden gate widens

revealed?/1

For a golden (blind) game the gate widens, and the rule is one function: revealed?(r) is feedback != "none" or revealed_ms not nil. Toggle the mode — the chips that cross the wire light up; the dashed chips are withheld.

commitmentgate per-guess scoregate totalsgate leaderboardgate secret + noncegate
Solid teal = on the wire; dashed = withheld. A golden game withholds every score until revealed_ms is set. Source: Codemojex.View · revealed?/1 · design §Privacy.

Classic — a guess is scored and shown live; the totals and leaderboard cross the wire. There is no commitment, and the secret never leaves the server. revealed?(r) is true because feedback != "none".

§3

The secret's one place

schemas/game.ex

Codemojex.Schemas.Game's moduledoc is explicit: "the secret (and, for a golden game, the nonce) is a server-side column and is never serialized to players." The secret and nonce fields are written server-side and never sent; the commitment is published from open — public by design, because it binds the server to the secret it fixed — and revealed_ms is the gate that opens the score. The boundary is drawn in the shape of the data: the secret has exactly one home, a player has no read that selects it, and a golden game exposes the secret and the nonce only at the sealed reveal, so a player can recompute SHA-256(secret ‖ nonce) and verify the commitment for themselves. That verifiability is the subject of the next chapter.

§4

References

sources · related

Sources

Related