DocsReference

Limits and Non-Goals

What @galabet/fair 0.1.0 and GFS 1.0 leave out, what a claim of conformance does and does not cover, and every numeric limit in one table.

GFS 1.0 describes one thing: how a result is derived from a server seed committed in advance, a client seed and a nonce. @galabet/fair implements that derivation and the record that lets someone repeat it. If a site says it conforms, the most that can mean is that its results, recomputed from revealed seeds, match what the reference produces for the same inputs. Nobody issues that claim on the project's behalf. There is no certification, no registry and no badge to look up, so a conformance claim is worth what you can check yourself in the verifier.

Left to the Operator

The library never touches money. It has no wallet, no balance, no stake and no payout. A Dice roll of 56.12 comes back as a number, and whether that wins, what it pays and how the stake was taken are the operator's rules, which is why the game pages tell operators to publish them. The houseEdge in a Limbo record shapes the multiplier the mapper returns. It does not tell you the site pays that multiplier.

Nothing is stored and nothing is sent. There is no database layer, no session, no nonce counter and no HTTP client or server in the package. Reserving a nonce so that two requests can't share it, keeping the server seed secret while it is live, holding revealed seeds for as long as the records they explain, publishing commitments where a player can see them: all of that is the operator's code. Storing seeds and reserving nonces describes what that code has to get right.

Card games stop at the shuffle. blackjack and hilo return a deck in dealing order and the library has no dealing, no hand values and no guesses. This project also has no server-side engine that plays a concealed Blackjack or Hi-Lo round, where cards are shown one at a time while the rest of the deck stays hidden. Anyone offering those games builds that part.

Not Proved by Verification

A verified record shows that the result follows from the seeds and that the seed hashes to the commitment in the record. It does not show when the commitment was published. commit stamps publishedAt with the clock of the machine that called it, verifyRecord never reads at, and nothing in a record proves the player saw the commitment before betting. That evidence has to come from somewhere the operator can't rewrite afterwards.

The operator holds both seeds and the next nonce, so it can compute upcoming results before the player bets. It cannot change them once the commitment is out. GFS 1.0 writes that weakness down and leaves it in place. A randomness beacon as a third input is planned as GFS 1.1 and has not been written. Records may carry a beacon field today, and 0.1.0 neither uses it in derivation nor checks it; inspectRecord marks such a record incomplete.

Return to player, withdrawals and whether the site behaves honestly are outside the scheme altogether. What verification proves goes through this at length.

Gaps in 0.1.0

Errors are plain Error objects with a message. There are no error codes, no subclasses and no stable contract on the wording, so code that matches message text will break when a message is reworded. The errors page lists them all.

play checks less than you might assume. Limbo's houseEdge is not validated: 5 gives a result of 1 on every round and −1 turns the public inputs' 1.76 into 3.56. Wheel accepts any whole number of segments from 2 up, and a million is fine. A nonce has no upper bound, including values past Number.MAX_SAFE_INTEGER where neighbouring integers collide. Parameters a game doesn't read are ignored silently. The mappers don't check that a float lies between 0 and 1. inspectRecord is the strict path and verifyRecord is not: it throws on malformed fields where you might expect a reason. A bad rows or decks can surface as count must be a positive integer.

A signature made when the bet is placed stops verifying once serverSeed is added to the record, because the seed is part of the signed payload. The record hash changes at reveal for the same reason. Troubleshooting has the ways around it.

canonicalJson is the part of RFC 8785 that records need. For numbers, strings and sorted keys it produces what the RFC asks for. It does not reject a lone surrogate, which the RFC requires and which comes out here as "\ud800". It never calls toJSON, so a Date becomes {}, and it has been tested with record-shaped data only.

Signing uses Ed25519 through Web Crypto, which arrived late. The source lists Node 18.4, Chrome 113, Firefox 130 and Safari 17 as the minimums, against Node 18 for everything else. There is no fallback implementation. On an older platform the signing functions reject with the platform's own error.

Things That Do Not Exist

@galabet/fair is the only package. There is no command-line tool and no companion package for storage, for a beacon, for a web framework or for React. The repository README has a table of such packages and labels it as intended architecture. None of them has been written, and a comment in verifyRecord's source that points to a beacon package is pointing at a plan. The package itself is version 0.1.0 and has not been published to npm. The HTTP API in this repository is a demo and is not deployed anywhere; examples that call it target http://localhost:3000. "Fair 1.0", "Fair 2.0" and "Fair 3.0" are editions of the website and say nothing about the spec or the package, as Versioning explains.

Numeric Limits

Each row was read from the source and then tried on both sides of the boundary.

LimitValueEnforced by
Server seedexactly 64 lowercase hex charactersassertServerSeed, so everything that derives
Client seed1 to 64 UTF-16 code units, no :. 32 two-unit emoji fit, 33 don'tassertClientSeed
Nonce, cursorwhole number, 0 or more, no ceilingderiveDigest
Nonce, cursor, at in an inspected record0 to 9,007,199,254,740,991inspectRecord
Floats per digest8, so cursor n covers floats 8n to 8n + 7digestToFloats
Plinko rows8 to 16, default 16plinko
Mines mines1 to 24 on 25 tiles, default 3mines
Keno draws1 to 40 from a pool of 40, default 10keno
Blackjack and Hi-Lo decks1 to 8, default 1. Eight decks take 415 floats and end on cursor 51deck
Wheel segments2 or more in play, 2 to 100 when inspected, default 10wheel, validateInspectionParams
Limbo houseEdgeunchecked in play, 0 to 0.5 when inspected, default 0.01validateInspectionParams
Crash houseEdge0 up to but not including 1, default 0.01. 0 to 0.999999 and required when inspectedcrashResult, inspectRecord
Crash saltunchecked in crashResult, 1 to 1,024 characters when inspectedinspectRecord
Crash chain length1 to 10,000,000createCrashChain
Inspected record size65,536 bytes as UTF-8. 65,537 is refusedparseInspection
Inspected record nesting12 levels of objects and arrays, counting the record itselfparseInspection
Public key, signature, secret key64, 128 and 128 lowercase hex charactersverifySignature, sign, inspectRecord

verifyRecord applies none of the inspection limits. A record of any size with any nesting goes straight to the hash functions, which is one more reason to put inspectRecord in front of anything a stranger can submit.