Hidden means hidden: engineering invisible picks
“The picks are hidden until the draw” sounds like a checkbox. It’s actually a discipline that touches every layer of the system — because a secret is only as good as its leakiest endpoint.
The threat model is your own curiosity
Nobody’s nation-state is attacking movie night. The realistic adversary is a curious owner with a browser inspector, a crew member watching the network tab, or a future developer adding a convenient field to an API response. The defense is the same for all three: the data never leaves the server.
What “never leaves” means in practice
- API responses carry booleans, not bodies. Pre-draw, an activity’s roster says has_submitted: true — never what. Your own pick is the only one you can read back.
- Realtime events count; they don’t describe. The live “3 of 5 picks in” moment is powered by counts alone. A websocket frame that included pick contents would be a leak with excellent latency.
- The admin panel is on the outside too. Support tooling shows activity state, never submission contents. Operational access isn’t a peek license.
- The reveal unseals exactly one. At the draw, the winner’s payload becomes readable. The losers never make the trip — sealed rows stay sealed, forever.
Tested like an invariant, not a feature
Policy tests assert the negative space: that endpoints do not return other picks pre-draw, that events do not carry payloads, that the winner is the only unsealed row after a draw. Negative tests are the ones that catch a helpful refactor six months later — the whole point is making the leak fail CI before it ships.
The product payoff
All this machinery buys one emotional outcome: submitting feels safe. The quiet friend’s real preference enters the pool because nobody can see it, judge it, or lobby against it. Fairness is the architecture; honesty is what it purchases.