Commit before play.
Publish SHA-256 of the server seed text. Keep evidence that the commitment existed before the round. A matching hash alone cannot establish when it was published.
GALABET FAIR STANDARD DRAFT
The rules behind
a Galabet result.
From the seed commitment to the final record. These are the calculations another implementation needs to reproduce.
Draft reference · GFS/1.0 calculation profile
Fair product editions have separate version numbers.

01 / INPUT CONTRACT
Keep the exact strings. A hex-looking seed is used as UTF-8 text, not decoded into 32 bytes before hashing.
5c1f7d3e8a2b4c6d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2dgalabet420Publish SHA-256 of the server seed text. Keep evidence that the commitment existed before the round. A matching hash alone cannot establish when it was published.
Use a new nonce for each new round under a seed pair. Store that counter durably. The calculation library does not manage sessions or prevent reuse.
Retire the seed before disclosing it. A live server seed exposes future results when the other inputs are known. This page uses an intentionally public fixture.
The server seed is 64 lowercase hexadecimal characters. The client seed is a nonempty string of at most 64 JavaScript UTF-16 code units and cannot contain a colon. Nonce and cursor use nonnegative integer values; portable records must keep them within JavaScript’s safe integer range.
SHA-256(serverSeed)ab37723062965715c5e6eeb54816f909a8e787bd3bfaee67a3cc0a3b90707de7
02 / FOLLOW FOUR BYTES
HMAC-SHA256 uses the server seed as its key and clientSeed:nonce:cursor as its message. Read each digest from left to right, in groups of four bytes.
FIRST FOUR DIGEST BYTES
143/256 + 168/256² + 234/256³ + 224/256⁴FRACTION / [0, 1)
0.5611712262034416floor(f × 10001) / 100DICE RESULT
56.12cursor 0Cursor 0 supplies floats 0–7. Cursor 1 supplies 8–15. Continue until the mapper has enough; the record stores the highest cursor consumed.
cursor = floor((floatCount − 1) / 8)8fa8eae01ccd43120b5028acd55241e63f01343d81935389da84578897a5fe39Consecutive four-byte groups are interpreted in big-endian order, divided by 2³². Preserve binary64 arithmetic and the mapper’s rounding order when reproducing the reference implementation.
03 / GAME MAPPINGS
Nine games consume the float stream through play(). Crash has a separate calculation. Open a rule to see the mapping and the boundary between outcome generation and application logic.
| Game | Input consumed | Returned result / rule |
|---|---|---|
| Dice | 1floats | A number, 0.00–100.00Both endpoints are possible. The target, comparison operator and payout are application rules; they do not change the generated roll. |
| Limbo | 1floats | A multiplier, at least 1.00The default houseEdge is 0.01. Preserve the operation order and round down to hundredths. The declared edge belongs with the record. |
| Plinko | rowsfloats | A path and bucket indexRows range from 8 to 16; the default is 16. Consume one new float per row. A payout table is separate from the path and bucket. |
| Mines | 24floats | Sorted tile indices, 0–24Shuffle all 25 tiles, then select 1–24 mines (default 3). Always consume 24 floats, even when selecting only one mine. Rows and columns are a rendering choice. |
| Roulette | 1floats | A pocket, 0–36This is a 37-pocket mapping. Wheel order, colour, bet types and payouts belong to the application. |
| Blackjack | 52d − 1floats | The full dealing orderUse 1–8 decks (default 1). Base suit order is clubs, diamonds, hearts, spades; ranks run A, 2…9, T, J, Q, K. The mapper produces a deck, not a hand score or a settlement. |
| Hi-Lo | 52d − 1floats | The full dealing orderThe default single deck consumes 51 floats. Reveals read the deck in order; rank comparisons and tie rules are application rules. Unrevealed future cards must stay on the server. |
| Keno | 39floats | Sorted numbers, 1–40Draw 1–40 numbers (default 10). Always shuffle the full pool with 39 floats. Selection order is not preserved in the sorted result. |
| Wheel | 1floats | A zero-based segment indexThe default is 10 segments. Store the ordered payout table and its version separately: an index does not carry a multiplier. |
| Crash | 52 bitsof the HMAC digest | A shared crash multiplierA separate hash-chain profile, not a play() mapper. Calculation uses a declared edge and the exact integer-to-number steps in the Crash section below. |
04 / MULTIPLAYER PROFILE
A Crash round uses a game hash and salt. It does not use a player’s client seed or nonce. Every participant follows the same endpoint.
Hash the revealed game hash as UTF-8 text. It must equal the previous published value. The terminating hash anchors the first round.
A calculation can reproduce a result without proving that its salt was unpredictable. Record the chain commitment before the chosen external salt becomes known, and retain that evidence separately.
Crash implementation guide ↗const e = 1n << 52n;
const h = BigInt('0x' + digestHex.slice(0, 13));
const scaled = (100n * e * 1_000_000n) / (h + 1n);
const cents = Number(scaled) / 1_000_000;
const result = Math.max(1, Math.floor(cents * (1 - houseEdge)) / 100);Here digestHex is HMAC-SHA256 with gameHash as the text key and salt as the text message. The first 13 hex digits supply 52 bits. Preserve the integer truncation to six decimal places before applying the edge.
Flight uses this endpoint calculation. Betting windows, cash-out acceptance, receipts and balance settlement are server rules beyond this calculation profile.
05 / THE VERIFICATION RECORD
A completed record gives an independent verifier the inputs and parameters used to produce the claimed result.
{
"spec": "GFS/1.0",
"profile": "single-player",
"game": "dice",
"params": {},
"serverSeed": "5c1f7d3e8a2b4c6d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
"commitment": "ab37723062965715c5e6eeb54816f909a8e787bd3bfaee67a3cc0a3b90707de7",
"clientSeed": "galabet",
"nonce": 42,
"cursor": 0,
"result": 56.12,
"at": 0
}at: 0; it is not a live round.OPTIONAL SIGNATURE / EXACT PAYLOAD
Remove only signature and signer, canonicalize the remaining object, and sign its UTF-8 bytes. Object keys sort in JavaScript UTF-16 order; arrays retain order and numbers use JavaScript JSON serialization. Adding a revealed seed changes the signed payload.
A valid signature proves agreement with the supplied key. Establishing who owns that key is a separate check.
Signing and key handling ↗06 / IMPLEMENTATION CHECK
A matching example is a useful first check. A port must also agree on digests, cursor progression, every game result and canonical record bytes.
import { play, verifyCommitment } from '@galabet/fair';
const serverSeed = '5c1f7d3e8a2b4c6d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d';
const commitment = 'ab37723062965715c5e6eeb54816f909a8e787bd3bfaee67a3cc0a3b90707de7';
const { result, cursor } = await play({
game: 'dice', serverSeed, clientSeed: 'galabet', nonce: 42,
});
console.log(await verifyCommitment(serverSeed, commitment));
console.log(result, cursor);true
56.12 0