Fairness
How Save Me That keeps it fair
When two or more people want the same thing, the app can pick who gets it. It's like pulling a name out of a hat, and here is how the app makes sure that pick is fair.
How it works
- Everyone in the draw goes in the hat. Each person once. Usually that means everyone who asked, but the person deciding can keep a draw to one group, like the grandchildren.
- The computer pulls one name out. It makes up a fresh random number for every pick. The person deciding can't see it beforehand or choose it.
- The pick lands on that person. For one thing, everyone in the draw had the same chance, to within a rounding far too small to ever notice.
If there are several of something, like 6 chairs, the app puts everyone in the draw in a random order and goes down the list. Each person gets what they asked for while enough are left. Someone who asked for more than is left is passed over unless they said fewer is fine, so some chairs can be left over.
Who is in the draw is read at the moment of the pick, and the pick uses exactly those people.
We tested it a million times
The line is a perfectly even split. Everyone landed close to it.
One thing to know
The app only makes a pick when the person deciding asks it to. They can also just choose who gets something themselves, choose not to use the pick, draw again before sharing it, or take a thing back to decide again. The pick is fair; what's done with it is up to them.
The technical detail
How the draw works, cryptographically
For readers who want the mechanism. Everything here describes the code the app runs today.
1. Who is in the draw is read once, before any randomness exists
The draw reads the candidates once, then makes the seed, then draws from exactly that list. In a house that shares each decision as it's made, the thing's status first moves to closed in its own write, and a new Save me that answer can only be written while a thing is open. Answers aren't otherwise frozen: someone can still take their own answer back, and the person deciding or a helper can still add an ask for someone. The draw uses the set as it read it.
In a house that reviews decisions before sharing them (share_when_ready), the draw is a draft and the thing isn't closed first, so the family can still answer. Asks drafted on someone's behalf are candidates too.
Candidates are everyone with a Save me that answer (stored as love), still in the house, and not already holding a share, sorted by their id in byte order. The order carries no information about names, roles or answer times.
2. Entropy
Each draw takes a fresh 256-bit seed s from Go's crypto/rand, freshly drawn for every pick. On the Linux servers that run the app, that reads the operating system's CSPRNG through getrandom(2): the kernel's ChaCha20 generator, which the kernel continually reseeds from hardware entropy sources. Its output is computationally indistinguishable from true randomness; it isn't a physical random process per draw.
For a thing with several, every position in the order comes from that one seed through SHA-256, not from separate random numbers per position. The seed is made after the candidates are read and isn't shown until the result is. Nobody using the app, including the person deciding, can supply or influence it.
3. One thing, everyone asked for one
w = BE_uint64(s[0..8]) mod n
winner = candidates[w]With s uniform, w is uniform up to modulo bias: each outcome has probability ⌊2⁶⁴/n⌋/2⁶⁴ or ⌈2⁶⁴/n⌉/2⁶⁴, a difference of at most 1/2⁶⁴. For 100 candidates the total deviation from exactly 1/n is under 5.4 × 10⁻¹⁸.
4. Several of a thing: a seeded Fisher-Yates shuffle
for i = n−1 down to 1:
k = n−1−i
j = BE_uint64( SHA-256(s ‖ BE_uint32(k))[0..8] ) mod (i+1)
swap(order[i], order[j])SHA-256 in counter mode expands the seed into a stream of words. A 256-bit seed can reach at most 2²⁵⁶ orders, so for large n most of the n! orders can't occur at all. What holds instead: under the standard assumption that SHA-256 behaves as a pseudorandom function, the order is computationally indistinguishable from a uniformly random one, so nobody can tell the difference or exploit it. Each step's own modulo bias, choosing among m, is below m/2⁶⁴ in total and negligible.
People are then served in that order: what they asked for if enough is left, otherwise what is left if they said fewer is fine, otherwise passed over.
5. A draw can be worked out again
The pick is a pure function of the seed, the people in the draw at that moment (sorted by id) with what each asked for, the quantity and what was already given: f(s, sorted candidates, quantity, already given). When a draw gives anyone a share, its seed is kept with those shares as hex (draw_seed) and shown to the person deciding under the result. The app keeps the seed, not a permanent record of who was in the draw, so working a pick out again needs that list as it stood at the moment of the draw.
6. Measured
10⁶ draws of 1 thing among 4: χ² = 2.99 (3 d.f., p = 0.39). 10⁶ shuffles of 5, first place: χ² = 1.77 (4 d.f., p = 0.78). These tests found no statistically significant evidence of bias; they don't prove fairness on their own.
Trust boundary
The seed is made on our server, so the guarantee assumes the server runs this code unaltered; it isn't a public commit-and-reveal scheme. Choosing to draw at all, adding an ask on someone's behalf, limiting a draw to a group, drawing again before a decision is shared, or taking a decided thing Back to Decide and drawing again are the decider's choices, outside what the draw itself guarantees.