v2.8 · RNG PRE-SUBMISSION PACKAGE v1.2
RAIN RNG · PRE-SUBMISSION PACKAGE v1.2 · DOCUMENT

REPORT-STATISTICAL — DIEHARDER · NIST STS · TESTU01 · SCALED OUTCOMES

Source sdk-v2/lab/REPORT-STATISTICAL.md · commit 96368332 · GLI CSR §2.2: Statistical data analysis · ← package index · raw on GitHub ↗

RAIN RNG v2.1 — Statistical evidence report (pre-certification, self-run)

Submission document per GLI Composite Submission Requirements v2.0 §2.2 "Statistical data analysis" and GLI-19 v3.0 §3.2.2–§3.2.4. This is the operator's own pre-testing; it is not a laboratory certificate. Every number below is reproduced from a log under lab/results/ by bash lab/tools/report-tables.sh; nothing is typed in by hand. Criteria used are the public ones collected in ../docs/LAB-SUBMISSION-REQUIREMENTS.md §2.2.

Run dates: 2026-09-13 21:59 UTC → 2026-09-14 (this document reflects the state at the time stamp in §11). Repository: rain-risk-markets/sdk-v2, RNG code at commit 5f3efa13 (@rain/rng-core 2.1.0, @rain/rng-session 2.1.0 — the same code the operator node runs; lab/FINGERPRINTS.json has the per-file SHA-256).

0. Headline

Suite Mechanism Result Volume Status
Dieharder 3.31.1, full -a battery, stream input (-g 200, never rewinds) chacha20 90 PASSED / 5 WEAK / 0 FAILED (95 result lines, 61 of 80 test/ntuple combinations; not run: rgb_lagged_sum ntuple 14–32, budget) ≈ 73 GB of 32-bit words valid (rewinds = 0 by construction)
Dieharder 3.31.1, full -a battery, stream hmac-drbg 83 PASSED / 3 WEAK / 0 FAILED (86 result lines, 52 of 80 test/ntuple combinations run — HMAC_DRBG produces ≈ 1 MB/s per core; not run: 28 combinations, see §9) valid, partial
NIST SP 800-22 STS 2.1.2, 1000 sequences × 10⁶ bits, all 15 tests, default parameters chacha20 188 result lines; 0 proportion outside CI; 0 uniformity P < 10⁻⁴ 125 MB from the 4 GB file
NIST STS 2.1.2, 1000 × 10⁶ bits hmac-drbg 188 result lines; 0 proportion outside CI; 0 uniformity P < 10⁻⁴ 125 MB from the 4 GB file
TestU01 1.2.3 SmallCrush (file-backed, no rewind) chacha20 15/15 statistics passed, p ∈ [0.06, 0.96] 2.27 × 10⁸ words valid
TestU01 1.2.3 SmallCrush hmac-drbg 15/15 statistics passed, p ∈ [0.09, 0.92] 2.27 × 10⁸ words valid
TestU01 Crush / BigCrush both not run (≈ 2³⁵ / 2³⁸ words = 137 GB / 1.1 TB; producer at 4–17 MB/s → 10–70 h) — stream runner shipped, §9 not run
Scaled outcomes — game results after intBelow/shuffle, collected by tools/collect-outcomes.mjs through the production ceremony (χ², blocked-χ² KS, serial correlation, runs, coupon, interplay, Fisher–Yates) chacha20 one hundred million (10⁸) dice outcomes (10⁶ rounds × 100 draws), 10⁸ roulette outcomes, 10⁸ coin outcomes, 10⁸ reel stops (10⁶ × 20 × 5 reels), two million shuffled decks (1.04 × 10⁸ card placements), 3 × 10⁸ adversarial dice outcomes — 8.04 × 10⁸ scaled outcomes in total §5 (column scaled outcomes (draws)) all total-distribution χ² pass at α = .01; isolated single-statistic exceedances at the expected rate (§5.3)
Scaled outcomes hmac-drbg twenty million (2 × 10⁷) outcomes per game (2 × 10⁵ rounds × 100 draws) for dice, roulette, coin, reel stops, 3 adversarial dice sets, plus 2 × 10⁷ legacy-dice and 2 × 10⁷ legacy-roulette (% N path, comparison only) and 4 × 10⁵ decks — 2.0 × 10⁸ in total; 10⁸-per-game sets in progress in results/outcomes/hmac-1e8/ (rows appear in §5.1 as hmac-1e8 when finished; EVIDENCE-INDEX.md records the exact count reached) §5 see §5

Invalid runs, quarantined (not evidence): results/dieharder/_smoke-invalid/chacha-13MB/ (387 rewinds, 13 MB smoke file) and results/dieharder/_invalid-rewound/chacha-4GB-file/ (19 rewinds, 4 GB file) — see §8.

1. Device under test

drbg(r_k, gameId, {mechanism}) — the draw layer of RAIN RNG v2.1 — fed by the real two-party commit-reveal ceremony (@rain/rng-session: RainRngClient.commit → RainRngHouse.reveal → settle, r_k = keccak256(abi.encode(pRev_k, hRev_k, sessionSeed, sessionId, k))). Two mechanisms are certified separately (GLI-19 §4.5.1): hmac-drbg (SP 800-90A HMAC_DRBG/SHA-256, 8 words per 256-bit block) and chacha20 (RFC 8439 block function keyed from r_k). Full description: RNG-DESCRIPTION.md; scaling functions (intBelow rejection sampling, shuffle Fisher–Yates): RNG-DESCRIPTION.md App. B and SCALING-INVENTORY.md.

Nothing in the collection tools re-implements the generator: tools/collect-raw.mjs and tools/collect-outcomes.mjs import @rain/rng-core / @rain/rng-session from packages/*/dist and call the same functions the operator node calls.

2. Test environment and tool versions

Item Value
Host Linux 6.1.175 x86_64 (Amazon Linux 2023 kernel), 8 vCPU, 62 GB RAM, NVMe; container joni-user7
Node.js v24.14.0
Dieharder 3.31.1 (built from dieharder-3.31.1.tgz, webhome.phy.duke.edu/~rgb) against GSL 2.7.1, gcc 12, CFLAGS=-O2 -fcommon; binary labtools/prefix/bin/dieharder
NIST STS 2.1.2 (csrc.nist.gov sts-2_1_2.zip, shipped makefile, gcc 12); one private copy of the tree per mechanism (labtools/sts/sts-chacha, labtools/sts/sts-hmac) because assess writes into its own experiments/ directory
TestU01 1.2.3 (github umontreal-simul/TestU01-2009 master, gcc 12, --disable-shared); generators tools/testu01/rain_file_gen.c (file-backed, unif01_CreateExternGenBits, aborts instead of rewinding) and tools/testu01/rain_stream_gen.c (stdin)
Outcome analyzer tools/analyze-outcomes.mjs (closed-form χ² via regularised incomplete gamma, KS, z-tests; no external dependency)

3. Dieharder

3.1 Why the file runs are invalid and how the stream runs are constructed

Dieharder with file input (-g 201 -f) silently rewinds the file when it is exhausted and prints # The file file_input_raw was rewound N times. Rewound data is re-used data; tests that consume it are correlated with earlier tests and are not independent evidence. We measured the consumption of every -a test on an unbounded /dev/urandom stream (lab/results/PROGRESS.md): the light tests take 40–960 MB each, marsaglia_tsang_gcd takes 8.0 GB, and rgb_lagged_sum takes ≈ 0.44 × (ntuple + 1) GB for ntuple 0…32 (≈ 250 GB in total). No 4 GB file can carry the battery; our 4 GB chacha file run rewound 19 times and is quarantined (§8).

The valid mechanism is therefore an unbounded stream: node lab/tools/collect-raw.mjs --bits 1e15 --out - … (the production ceremony + drbg(), W independent sessions interleaved in 1 MiB chunks, bit-reproducible from SEED-STREAM) piped into dieharder -g 200 (stdin_input_raw). Because worker sessions in this environment are killed every 15–80 min, the runner (tools/run-dieharder-stream.sh) launches one dieharder -d <test> [-n <ntuple>] -g 200 process per test of the -a list on the same pipe (identical default -t/-p to -a), archives each result in tests/<id>[-n<k>].log with a .DONE marker, and resumes by skipping finished tests. Every test reads fresh, never-reused words (a finished process's unread stdio buffer is discarded, not re-read). dieharder -d 102 (sts_serial) ignores -n and runs its own 1…16 loop, so the 15 extra 102-n2…16 invocations are additional independent replicates of the whole sts_serial loop on fresh data and are reported separately.

Stream seeds (all secrets derive from them by HMAC-SHA-256, see collect-raw.mjs header): chacha: 0x5241494e4c41425f53545245414d5f4348414348415f32303236303931335f30 · hmac: 0x5241494e4c41425f53545245414d5f484d41435f32303236303931335f303101 (results/raw/<mech>/SEED-STREAM). Producer arguments: --workers 4 (chacha) / 2–5 (hmac) --words-per-draw 8 --draws 1024 --game lab.

Reading the assessment column. Dieharder marks a test WEAK when the KS p-value over its 100 p-samples is < 0.01 or > 0.99, and FAILED when < 10⁻⁶ or > 1 − 10⁻⁶. For a perfect generator every p-value is uniform, so ≈ 2 % of result lines are expected to be WEAK (both tails) — for the ≈ 95-line battery that is ~1–3 WEAK lines per run by design (author's documentation: "WEAK is not a failure … rerun with a different seed"; a reproducible FAILED is). A different WEAK set on a re-run with a different seed is the expected behaviour; the same test WEAK across seeds would warrant investigation.

3.2 Results

Tables are generated by bash lab/tools/report-tables.sh dieharder from results/dieharder/<mech>-stream/tests/*.log; the consolidated -a-style report is results/dieharder/<mech>-stream/dieharder-a.log, counts in SUMMARY.txt.

chacha (stream)

chacha: 95 result lines — 90 PASSED / 5 WEAK / 0 FAILED; rewinds: n/a (stream input, rewinding impossible). Tests not run: 19 (rgb_lagged_sum ntuple=14, rgb_lagged_sum ntuple=15, rgb_lagged_sum ntuple=16, rgb_lagged_sum ntuple=17, rgb_lagged_sum ntuple=18, rgb_lagged_sum ntuple=19, rgb_lagged_sum ntuple=20, rgb_lagged_sum ntuple=21, rgb_lagged_sum ntuple=22, rgb_lagged_sum ntuple=23, rgb_lagged_sum ntuple=24, rgb_lagged_sum ntuple=25, rgb_lagged_sum ntuple=26, rgb_lagged_sum ntuple=27, rgb_lagged_sum ntuple=28, rgb_lagged_sum ntuple=29, rgb_lagged_sum ntuple=30, rgb_lagged_sum ntuple=31, rgb_lagged_sum ntuple=32) Extra sts_serial replicates (15 further full 1..16 loops on fresh data, not part of the canonical battery): 444 PASSED / 6 WEAK / 0 FAILED.

test ntuple tsamples psamples p-value assessment
diehard_birthdays 0 100 100 0.68172231 PASSED
diehard_operm5 0 1000000 100 0.99104717 PASSED
diehard_rank_32x32 0 40000 100 0.73439545 PASSED
diehard_rank_6x8 0 100000 100 0.86757999 PASSED
diehard_bitstream 0 2097152 100 0.01362755 PASSED
diehard_opso 0 2097152 100 0.74805513 PASSED
diehard_oqso 0 2097152 100 0.77809577 PASSED
diehard_dna 0 2097152 100 0.37434899 PASSED
diehard_count_1s_str 0 256000 100 0.68908419 PASSED
diehard_count_1s_byt 0 256000 100 0.86282805 PASSED
diehard_parking_lot 0 12000 100 0.07270933 PASSED
diehard_2dsphere 2 8000 100 0.96460550 PASSED
diehard_3dsphere 3 4000 100 0.76780037 PASSED
diehard_squeeze 0 100000 100 0.07700051 PASSED
diehard_sums 0 100 100 0.00278315 WEAK
diehard_runs 0 100000 100 0.46449654 PASSED
diehard_runs 0 100000 100 0.03931625 PASSED
diehard_craps 0 200000 100 0.47911000 PASSED
diehard_craps 0 200000 100 0.17153488 PASSED
marsaglia_tsang_gcd 0 10000000 100 0.34083256 PASSED
marsaglia_tsang_gcd 0 10000000 100 0.40081702 PASSED
sts_monobit 1 100000 100 0.53640600 PASSED
sts_runs 2 100000 100 0.55013762 PASSED
sts_serial 1 100000 100 0.80156226 PASSED
sts_serial 2 100000 100 0.13191400 PASSED
sts_serial 3 100000 100 0.97590322 PASSED
sts_serial 3 100000 100 0.45922445 PASSED
sts_serial 4 100000 100 0.67837598 PASSED
sts_serial 4 100000 100 0.08183992 PASSED
sts_serial 5 100000 100 0.71835238 PASSED
sts_serial 5 100000 100 0.46047979 PASSED
sts_serial 6 100000 100 0.50070812 PASSED
sts_serial 6 100000 100 0.05184866 PASSED
sts_serial 7 100000 100 0.93819755 PASSED
sts_serial 7 100000 100 0.50215455 PASSED
sts_serial 8 100000 100 0.64877059 PASSED
sts_serial 8 100000 100 0.70360416 PASSED
sts_serial 9 100000 100 0.99967289 WEAK
sts_serial 9 100000 100 0.97998454 PASSED
sts_serial 10 100000 100 0.47522522 PASSED
sts_serial 10 100000 100 0.96202724 PASSED
sts_serial 11 100000 100 0.40735352 PASSED
sts_serial 11 100000 100 0.80650883 PASSED
sts_serial 12 100000 100 0.10313302 PASSED
sts_serial 12 100000 100 0.00007842 WEAK
sts_serial 13 100000 100 0.14887157 PASSED
sts_serial 13 100000 100 0.88717272 PASSED
sts_serial 14 100000 100 0.88728948 PASSED
sts_serial 14 100000 100 0.92398252 PASSED
sts_serial 15 100000 100 0.89541942 PASSED
sts_serial 15 100000 100 0.88176400 PASSED
sts_serial 16 100000 100 0.37332922 PASSED
sts_serial 16 100000 100 0.20115634 PASSED
rgb_bitdist 1 100000 100 0.86888183 PASSED
rgb_bitdist 2 100000 100 0.73992059 PASSED
rgb_bitdist 3 100000 100 0.95754103 PASSED
rgb_bitdist 4 100000 100 0.82153013 PASSED
rgb_bitdist 5 100000 100 0.32074393 PASSED
rgb_bitdist 6 100000 100 0.20932820 PASSED
rgb_bitdist 7 100000 100 0.38894727 PASSED
rgb_bitdist 8 100000 100 0.10285610 PASSED
rgb_bitdist 9 100000 100 0.06122998 PASSED
rgb_bitdist 10 100000 100 0.26549130 PASSED
rgb_bitdist 11 100000 100 0.99801205 WEAK
rgb_bitdist 12 100000 100 0.07465212 PASSED
rgb_minimum_distance 2 10000 1000 0.98412344 PASSED
rgb_minimum_distance 3 10000 1000 0.82328522 PASSED
rgb_minimum_distance 4 10000 1000 0.60919914 PASSED
rgb_minimum_distance 5 10000 1000 0.12584648 PASSED
rgb_permutations 2 100000 100 0.40971888 PASSED
rgb_permutations 3 100000 100 0.09328447 PASSED
rgb_permutations 4 100000 100 0.61062939 PASSED
rgb_permutations 5 100000 100 0.05554972 PASSED
rgb_kstest_test 0 10000 1000 0.17051720 PASSED
dab_bytedistrib 0 51200000 1 0.23962347 PASSED
dab_dct 256 50000 1 0.49778464 PASSED
dab_filltree 32 15000000 1 0.99352260 PASSED
dab_filltree 32 15000000 1 0.84527318 PASSED
dab_filltree2 0 5000000 1 0.42968644 PASSED
dab_filltree2 1 5000000 1 0.88334883 PASSED
dab_monobit2 12 65000000 1 0.57177002 PASSED
rgb_lagged_sum 0 1000000 100 0.42967500 PASSED
rgb_lagged_sum 1 1000000 100 0.99997451 WEAK
rgb_lagged_sum 2 1000000 100 0.95132654 PASSED
rgb_lagged_sum 3 1000000 100 0.40574003 PASSED
rgb_lagged_sum 4 1000000 100 0.79398211 PASSED
rgb_lagged_sum 5 1000000 100 0.78468441 PASSED
rgb_lagged_sum 6 1000000 100 0.53376787 PASSED
rgb_lagged_sum 7 1000000 100 0.69311757 PASSED
rgb_lagged_sum 8 1000000 100 0.68748782 PASSED
rgb_lagged_sum 9 1000000 100 0.31392601 PASSED
rgb_lagged_sum 10 1000000 100 0.97981797 PASSED
rgb_lagged_sum 11 1000000 100 0.50062405 PASSED
rgb_lagged_sum 12 1000000 100 0.05637778 PASSED
rgb_lagged_sum 13 1000000 100 0.51171494 PASSED

hmac (stream)

hmac: 86 result lines — 83 PASSED / 3 WEAK / 0 FAILED; rewinds: n/a (stream input, rewinding impossible). Tests not run: 28 (rgb_lagged_sum ntuple=5, rgb_lagged_sum ntuple=6, rgb_lagged_sum ntuple=7, rgb_lagged_sum ntuple=8, rgb_lagged_sum ntuple=9, rgb_lagged_sum ntuple=10, rgb_lagged_sum ntuple=11, rgb_lagged_sum ntuple=12, rgb_lagged_sum ntuple=13, rgb_lagged_sum ntuple=14, rgb_lagged_sum ntuple=15, rgb_lagged_sum ntuple=16, rgb_lagged_sum ntuple=17, rgb_lagged_sum ntuple=18, rgb_lagged_sum ntuple=19, rgb_lagged_sum ntuple=20, rgb_lagged_sum ntuple=21, rgb_lagged_sum ntuple=22, rgb_lagged_sum ntuple=23, rgb_lagged_sum ntuple=24, rgb_lagged_sum ntuple=25, rgb_lagged_sum ntuple=26, rgb_lagged_sum ntuple=27, rgb_lagged_sum ntuple=28, rgb_lagged_sum ntuple=29, rgb_lagged_sum ntuple=30, rgb_lagged_sum ntuple=31, rgb_lagged_sum ntuple=32)

test ntuple tsamples psamples p-value assessment
diehard_birthdays 0 100 100 0.68609744 PASSED
diehard_operm5 0 1000000 100 0.13040900 PASSED
diehard_rank_32x32 0 40000 100 0.99987653 WEAK
diehard_rank_6x8 0 100000 100 0.70322967 PASSED
diehard_bitstream 0 2097152 100 0.95456790 PASSED
diehard_opso 0 2097152 100 0.09616516 PASSED
diehard_oqso 0 2097152 100 0.97193538 PASSED
diehard_dna 0 2097152 100 0.20047125 PASSED
diehard_count_1s_str 0 256000 100 0.99939055 WEAK
diehard_count_1s_byt 0 256000 100 0.39800464 PASSED
diehard_parking_lot 0 12000 100 0.86323545 PASSED
diehard_2dsphere 2 8000 100 0.29461438 PASSED
diehard_3dsphere 3 4000 100 0.73616533 PASSED
diehard_squeeze 0 100000 100 0.52762935 PASSED
diehard_sums 0 100 100 0.16892557 PASSED
diehard_runs 0 100000 100 0.79047806 PASSED
diehard_runs 0 100000 100 0.01934008 PASSED
diehard_craps 0 200000 100 0.79736718 PASSED
diehard_craps 0 200000 100 0.55204569 PASSED
marsaglia_tsang_gcd 0 10000000 100 0.97522402 PASSED
marsaglia_tsang_gcd 0 10000000 100 0.62578183 PASSED
sts_monobit 1 100000 100 0.50378653 PASSED
sts_runs 2 100000 100 0.97219601 PASSED
sts_serial 1 100000 100 0.95862996 PASSED
sts_serial 2 100000 100 0.12622394 PASSED
sts_serial 3 100000 100 0.00278798 WEAK
sts_serial 3 100000 100 0.15186858 PASSED
sts_serial 4 100000 100 0.06668808 PASSED
sts_serial 4 100000 100 0.93333167 PASSED
sts_serial 5 100000 100 0.12137431 PASSED
sts_serial 5 100000 100 0.33668558 PASSED
sts_serial 6 100000 100 0.32797158 PASSED
sts_serial 6 100000 100 0.96284935 PASSED
sts_serial 7 100000 100 0.89962442 PASSED
sts_serial 7 100000 100 0.15793288 PASSED
sts_serial 8 100000 100 0.47590090 PASSED
sts_serial 8 100000 100 0.40394877 PASSED
sts_serial 9 100000 100 0.24733679 PASSED
sts_serial 9 100000 100 0.59811425 PASSED
sts_serial 10 100000 100 0.72761033 PASSED
sts_serial 10 100000 100 0.05620910 PASSED
sts_serial 11 100000 100 0.51755502 PASSED
sts_serial 11 100000 100 0.97427738 PASSED
sts_serial 12 100000 100 0.91961157 PASSED
sts_serial 12 100000 100 0.68638058 PASSED
sts_serial 13 100000 100 0.84772785 PASSED
sts_serial 13 100000 100 0.33343097 PASSED
sts_serial 14 100000 100 0.81418042 PASSED
sts_serial 14 100000 100 0.43682339 PASSED
sts_serial 15 100000 100 0.93862995 PASSED
sts_serial 15 100000 100 0.80390179 PASSED
sts_serial 16 100000 100 0.34168999 PASSED
sts_serial 16 100000 100 0.54049114 PASSED
rgb_bitdist 1 100000 100 0.68671624 PASSED
rgb_bitdist 2 100000 100 0.85362674 PASSED
rgb_bitdist 3 100000 100 0.30782277 PASSED
rgb_bitdist 4 100000 100 0.99107009 PASSED
rgb_bitdist 5 100000 100 0.70782590 PASSED
rgb_bitdist 6 100000 100 0.87895469 PASSED
rgb_bitdist 7 100000 100 0.03360376 PASSED
rgb_bitdist 8 100000 100 0.65199856 PASSED
rgb_bitdist 9 100000 100 0.83565353 PASSED
rgb_bitdist 10 100000 100 0.13197023 PASSED
rgb_bitdist 11 100000 100 0.17450761 PASSED
rgb_bitdist 12 100000 100 0.93710513 PASSED
rgb_minimum_distance 2 10000 1000 0.91623918 PASSED
rgb_minimum_distance 3 10000 1000 0.16813567 PASSED
rgb_minimum_distance 4 10000 1000 0.47526150 PASSED
rgb_minimum_distance 5 10000 1000 0.76912236 PASSED
rgb_permutations 2 100000 100 0.30773472 PASSED
rgb_permutations 3 100000 100 0.11241957 PASSED
rgb_permutations 4 100000 100 0.86752510 PASSED
rgb_permutations 5 100000 100 0.57090317 PASSED
rgb_kstest_test 0 10000 1000 0.70900008 PASSED
dab_bytedistrib 0 51200000 1 0.88806521 PASSED
dab_dct 256 50000 1 0.70678794 PASSED
dab_filltree 32 15000000 1 0.26850768 PASSED
dab_filltree 32 15000000 1 0.90557405 PASSED
dab_filltree2 0 5000000 1 0.02064555 PASSED
dab_filltree2 1 5000000 1 0.53994751 PASSED
dab_monobit2 12 65000000 1 0.34368168 PASSED
rgb_lagged_sum 0 1000000 100 0.60588286 PASSED
rgb_lagged_sum 1 1000000 100 0.50413098 PASSED
rgb_lagged_sum 2 1000000 100 0.34668317 PASSED
rgb_lagged_sum 3 1000000 100 0.52724432 PASSED
rgb_lagged_sum 4 1000000 100 0.32958104 PASSED

4. NIST SP 800-22 STS 2.1.2

Data: the first 125,000,000 bytes (1000 sequences × 10⁶ bits) of each 4 GB raw file (results/raw/<mech>/raw-32gbit.bin; sha256 d8b314f6…07dfda chacha, 68a170a0…62e43d3 hmac; regenerable from SEED-BIG, --workers 4 --draws 64 --words-per-draw 1 (chacha) / 8 (hmac)). Command: printf '0\n<file>\n1\n0\n1000\n1\n' | ./assess 1000000 — generator 0 = input file, all 15 tests, default block parameters (BlockFrequency M = 128, NonOverlapping/Overlapping m = 9, ApproximateEntropy m = 10, Serial m = 16, LinearComplexity M = 500), binary input mode. Runner: tools/run-sts.sh.

Criteria (SP 800-22 §4.2): for each test line, (i) the proportion of sequences with p ≥ 0.01 must lie within 0.99 ± 3·√(0.99·0.01/1000) = [0.9806, 0.9994] (STS prints * and "minimum pass rate ≈ 0.980" — RandomExcursions lines use their own, smaller m); (ii) the P-value of P-values (χ² over 10 bins of the 1000 p-values) must be ≥ 0.0001. With 188 template lines under NonOverlappingTemplate, 8 + 18 excursion lines, 2 Serial, 2 CumulativeSums, one expects a few lines near the CI edge by chance; a systematic failure is many lines below 0.9806 or any uniformity P < 10⁻⁴.

Full reports: results/sts/<mech>/finalAnalysisReport.txt (+ experiments/<Test>/results.txt|stats.txt per test, freq.txt).

chacha

chacha: 188 result lines over 15 tests; proportion outside the confidence interval: 0 line(s); P-value-of-P-values < 0.0001: 0 line(s). STS-printed minimum pass rate = 980 of 1000 sequences (Random Excursions lines use their own, smaller m).

test lines (templates/states) sequences passed / total min proportion proportion lines outside CI min P-value of uniformity uniformity lines < 1e-4
Frequency 1 991 / 1000 0.9910 0 0.1342 0
BlockFrequency 1 992 / 1000 0.9920 0 0.9737 0
CumulativeSums 2 1982 / 2000 0.9890 0 0.4373 0
Runs 1 983 / 1000 0.9830 0 0.2493 0
LongestRun 1 990 / 1000 0.9900 0 0.1107 0
Rank 1 993 / 1000 0.9930 0 0.0247 0
FFT 1 988 / 1000 0.9880 0 0.0661 0
NonOverlappingTemplate 148 146538 / 148000 0.9820 0 0.0175 0
OverlappingTemplate 1 983 / 1000 0.9830 0 0.0083 0
Universal 1 988 / 1000 0.9880 0 0.8129 0
ApproximateEntropy 1 993 / 1000 0.9930 0 0.0396 0
RandomExcursions 8 4688 / 4744 0.9831 0 0.1507 0
RandomExcursionsVariant 18 10586 / 10674 0.9882 0 0.0125 0
Serial 2 1974 / 2000 0.9850 0 0.1296 0
LinearComplexity 1 993 / 1000 0.9930 0 0.3669 0

hmac

hmac: 188 result lines over 15 tests; proportion outside the confidence interval: 0 line(s); P-value-of-P-values < 0.0001: 0 line(s). STS-printed minimum pass rate = 980 of 1000 sequences (Random Excursions lines use their own, smaller m).

test lines (templates/states) sequences passed / total min proportion proportion lines outside CI min P-value of uniformity uniformity lines < 1e-4
Frequency 1 990 / 1000 0.9900 0 0.4522 0
BlockFrequency 1 994 / 1000 0.9940 0 0.7096 0
CumulativeSums 2 1982 / 2000 0.9900 0 0.2757 0
Runs 1 989 / 1000 0.9890 0 0.5503 0
LongestRun 1 992 / 1000 0.9920 0 0.5062 0
Rank 1 990 / 1000 0.9900 0 0.0266 0
FFT 1 989 / 1000 0.9890 0 0.5022 0
NonOverlappingTemplate 148 146468 / 148000 0.9800 0 0.0034 0
OverlappingTemplate 1 986 / 1000 0.9860 0 0.4827 0
Universal 1 984 / 1000 0.9840 0 0.1990 0
ApproximateEntropy 1 993 / 1000 0.9930 0 0.2583 0
RandomExcursions 8 4925 / 4960 0.9887 0 0.0278 0
RandomExcursionsVariant 18 11060 / 11160 0.9823 0 0.0983 0
Serial 2 1975 / 2000 0.9870 0 0.4118 0
LinearComplexity 1 989 / 1000 0.9890 0 0.8613 0

5. Scaled outcomes (GLI-19 §3.2.2 "final outcome", §3.2.3(b), §3.2.4(b))

Collection: tools/collect-outcomes.mjs — the ceremony above, then per round k the game's certified scaling of drbg(r_k, gameId): dice = intBelow(100), roulette = intBelow(37), coin = intBelow(2), slot-reel = 5 reels × intBelow(20) from one cursor, card-shuffle = Fisher–Yates shuffle(52). 100 draws per round for the N ≤ 100 games (20 for slot-reel) — exactly what a game engine consumes through stream.cursor(). Seed 0x5241494e4c41425f4f5554434f4d45535f32303236303931335f303030303001; per-run secrets, sessions, sha256 in results/outcomes/<mech>/<set>.csv.meta.json (CSV regenerable by seed). Runner: tools/run-outcomes.sh.

Adversarial player modes (RNG-DESCRIPTION App. A; GLI-19 §3.3.2(b), AGCO 4.26 "impervious to the player"): zero — player reveals are all-zero bytes; fixed — the player uses one fixed chain secret for every session; grind — for every session the player tries 16 candidate secrets and keeps the one whose first-round dice would be highest if the house reveal were the previous session's (a grinding strategy with no information about the unrevealed house chain). If the construction is sound, all three must be indistinguishable from the honest run.

Analysis (tools/analyze-outcomes.mjs, 99 % level): (1) total-distribution χ² (N − 1 d.f.) at α = .01 and .05; (2) the stream is cut into 100 blocks of 10⁶ draws, a χ² p-value per block, KS test of the 100 p-values against U(0,1) (a single lucky p-value cannot pass this); (3) serial correlation at lags 1…8, z = ρ√n on 5 × 10⁷ consecutive pairs, |z| < 2.576; (4) Wald–Wolfowitz runs above/below the median; (5) coupon-collector mean; (6) slot-reel interplay: for each of the 10 reel pairs the χ² test of independence on the 20 × 20 joint table (expected = row·column/n, 361 d.f.), plus the joint-uniformity χ² against n/400 per cell (399 d.f.); tool note: analyzer v1.0 evaluated the joint-uniformity statistic with 361 instead of 399 d.f., which produced three spurious α = .01 exceedances (chacha 3×4, hmac 0×1 and 2×4) in the first analysis pass — fixed 2026-09-14 (analyze-outcomes.mjs), all slot-reel sets re-analysed; the split-sample re-tests that prompted the fix are in results/outcomes/hmac/slot-reel.interplay-*-check.txt; (7) card-shuffle: 52 × 52 card × position table χ² (2601 d.f.), per-position χ² (51 d.f.) KS-uniformity, adjacency table (card j follows card i, 52 × 51), first-card χ².

5.1 Summary table (bash lab/tools/report-tables.sh outcomes)

| mechanism | data set | N | rounds × draws/round | scaled outcomes (draws) | adversary | χ² total (p) | blocked χ² KS (p) | serial corr. lags 1–8 (min p / any |z|>2.576) | runs (p) | interplay / shuffle | verdict @ α=.01 | |---|---|---|---|---|---|---|---|---|---|---|---| | chacha | card-shuffle | 52 | 2,000,000 decks × 52 = 104,000,000 placements | 104,000,000 | none | — | — | — | — | 52×52 table p=0.1000; per-position KS p=0.1369; adjacency p=0.2136; first card p=0.2173 | pass | | chacha | coin | 2 | 1,000,000 × 100 | 100,000,000 | none | 0.5279 | 0.2201 | 0.1183 / 0 | 0.9225 | — | pass | | chacha | dice-adv-fixed | 100 | 1,000,000 × 100 | 100,000,000 | fixed | 0.3725 | 0.1124 | 0.0707 / 0 | 0.6810 | — | pass | | chacha | dice-adv-grind | 100 | 1,000,000 × 100 | 100,000,000 | grind | 0.7094 | 0.0054 | 0.0021 / 1 | 0.2062 | — | see note: blockedChiSquareKS, serial | | chacha | dice-adv-zero | 100 | 1,000,000 × 100 | 100,000,000 | zero | 0.2496 | 0.5761 | 0.0010 / 1 | 0.3649 | — | see note: serial | | chacha | dice | 100 | 1,000,000 × 100 | 100,000,000 | none | 0.9423 | 0.0960 | 0.0290 / 0 | 0.7735 | — | pass | | chacha | roulette | 37 | 1,000,000 × 100 | 100,000,000 | none | 0.9733 | 0.1694 | 0.0460 / 0 | 0.3495 | — | pass | | chacha | slot-reel | 20 | 1,000,000 × 20 × 5 reels | 100,000,000 | none | 0.6622 | 0.4724 | 0.0690 / 0 | 0.9127 | 10 reel pairs, min p 0.1192, 0 < .01 | pass | | hmac | card-shuffle | 52 | 400,000 decks × 52 = 20,800,000 placements | 20,800,000 | none | — | — | — | — | 52×52 table p=0.2708; per-position KS p=0.2010; adjacency p=0.5977; first card p=0.8365 | pass | | hmac | coin | 2 | 200,000 × 100 | 20,000,000 | none | 0.6467 | 0.3853 | 0.0275 / 0 | 0.9637 | — | pass | | hmac | dice-adv-fixed | 100 | 200,000 × 100 | 20,000,000 | fixed | 0.4204 | 0.2889 | 0.1027 / 0 | 0.1801 | — | pass | | hmac | dice-adv-grind | 100 | 200,000 × 100 | 20,000,000 | grind | 0.6844 | 0.9153 | 0.0333 / 0 | 0.1669 | — | pass | | hmac | dice-adv-zero | 100 | 200,000 × 100 | 20,000,000 | zero | 0.6192 | 0.7359 | 0.2120 / 0 | 0.0709 | — | pass | | hmac | dice | 100 | 200,000 × 100 | 20,000,000 | none | 0.6154 | 0.3611 | 0.1494 / 0 | 0.9334 | — | pass | | hmac | legacy-dice | 100 | 20,000,000 × 1 | 20,000,000 | none | 0.6013 | 0.4211 | 0.1479 / 0 | 0.6976 | — | pass | | hmac | legacy-roulette | 37 | 20,000,000 × 1 | 20,000,000 | none | 0.6663 | 0.0452 | 0.0096 / 1 | 0.6164 | — | see note: serial | | hmac | roulette | 37 | 200,000 × 100 | 20,000,000 | none | 0.0607 | 0.6584 | 0.0717 / 0 | 0.1988 | — | pass | | hmac | slot-reel | 20 | 200,000 × 20 × 5 reels | 20,000,000 | none | 0.4192 | 0.4219 | 0.0751 / 0 | 0.8716 | 10 reel pairs, min p 0.0400, 0 < .01 | pass |

Total scaled outcomes analysed: chacha = 804,000,000 (8.04e+08); hmac = 200,800,000 (2.01e+08) — every one of them is a game result after intBelow/shuffle (not a raw bit), produced by the same code path a game engine uses.

Reading the volume columns: rounds × draws/round is what the collector was asked for; scaled outcomes (draws) is the number of game results in the CSV (one line per round holds draws/round comma-separated outcomes; slot-reel lines hold 5 reel stops per draw; card-shuffle lines hold one 52-card permutation = 52 placements). One-screen artifact list with sizes, counts and sha256: EVIDENCE-INDEX.md.

Per-data-set detail (every lag, every reel pair, the 52 × 52 counts): results/outcomes/<mech>/<set>.analysis.md and .csv.analysis.json.

5.2 Fisher–Yates position uniformity (card-shuffle)

mechanism decks 52×52 position table χ² (2601 d.f.) p per-position χ² (51 d.f.): positions with p < .01 / min p / KS p adjacency χ² (2600 d.f.) p first-card χ² (51 d.f.) p
chacha 2,000,000 2693.87 0.1000 0 / 0.0343 / 0.1369 0.2136 0.2173
hmac 400,000 2644.60 0.2708 0 / 0.0221 / 0.2010 0.5977 0.8365

5.3 Interpretation

Every total-distribution χ² — the GLI-19 §3.2.3(b) test proper — passes at α = .01 for every game, both mechanisms and all three adversarial modes; the observed cell extremes are within ±0.3 % of expectation at 10⁶ per cell. The blocked-χ² KS test passes for every honest data set. The 100-block × 8-lag × per-pair design evaluates ≈ 15 independent statistics per data set at the 1 % level, i.e. ≈ 0.15 expected exceedances per data set and ≈ 2–3 over the whole table; the exceedances observed (listed in the "verdict" column: lag-4 |z| = 3.07 / 3.29 in the chacha grind / zero adversarial sets, blocked-KS p = .0054 in grind) are of that count and magnitude, are not shared between data sets (a real lag-4 dependence would appear in the honest dice set drawn from the same generator, where lag 4 has z = −1.27, and in the hmac adversarial sets, where every lag passes), and are of the kind GLI-19's "collectively, at a 99 % confidence level" anticipates. All 20 reel-pair independence tests pass at α = .01 (two at .04 for hmac, ≈ the expected 0.5–1 of 20 below .05). The adversarial sets are statistically indistinguishable from the honest set on every aggregate statistic, which is the property App. A claims.

6. TestU01

mechanism battery result statistics cost
chacha smallcrush ALL PASSED 15 statistics, p-values in [0.06, 0.96] CPU 00:00:10.99, 226999458 words
chacha crush not run — —
chacha bigcrush not run — —
hmac smallcrush ALL PASSED 15 statistics, p-values in [0.09, 0.92] CPU 00:00:11.67, 226953194 words
hmac crush not run — —
hmac bigcrush not run — —

SmallCrush was run file-backed on the 4 GB files (rain_file_gen: sequential, exits 3 instead of rewinding; consumed 2.27 × 10⁸ words = 0.9 GB each). Criteria: TestU01 flags p-values outside [10⁻³, 1 − 10⁻³] as suspicious and outside [10⁻¹⁰, 1 − 10⁻¹⁰] as failures; both runs print "All tests were passed" with no flagged statistic. Logs: results/testu01/<mech>/smallcrush.log, command lines in RUN.txt.

Crush and BigCrush were not run (see §9). The stream generator (rain_stream_gen) and runner (tools/run-testu01-crush-stream.sh <mech> crush|bigcrush [workers]) are shipped; Crush consumes ≈ 2³⁵ words ≈ 137 GB.

6a. Latency on a real network (engineering evidence, not a §2.2 statistical item)

An unmodified @rain/rng-node was deployed on a remote host (Render web service, origin GCP us-west1 Oregon behind Cloudflare) and driven from this lab host (AWS us-east-1 Virginia) by an unmodified RainRngClient — 1,000 sequential spins per run × {HTTPS, WebSocket} × {pipelining OFF, ON}; every round verified client-side. Measured application RTT (GET /healthz, keep-alive) p50 ≈ 101 ms.

gap (animation) transport pipelining OFF — critical path p50 / p95 / p99 pipelining ON — p50 / p95 / p99 settled / errors
60 ms HTTPS 88.5 / 101.8 / 119.3 ms 36.0 / 49.0 / 67.5 ms 2,000 / 0
60 ms WebSocket 91.1 / 100.2 / 101.5 ms 23.3 / 39.7 / 57.9 ms 2,000 / 0
0 ms (nothing to hide behind) HTTPS / WS ≈ 87 / 100 ms ≈ 87 / 100 ms — ON ≈ OFF, as the model predicts 4,000 / 0
1,000 ms (realistic reel) HTTPS / WS see results/latency-real-network/loadtest-gap1000.txt critical path = local settle only

Critical path with pipelining ≈ max(0, RTT − animation). Full report, raw JSON, network probes and the exact server bundle: results/latency-real-network/{REPORT.md,results*.json,network-baseline.txt,RUN.txt,deploy-manifest.txt}.

7. Verdict against the §2.2 criteria

Criterion (docs/LAB-SUBMISSION-REQUIREMENTS.md §2.2) chacha20 hmac-drbg
Dieharder: no reproducible FAILED; WEAK at the ≈ 2 % rate met — 0 FAILED in 95 lines (+ 0 FAILED in 450 sts_serial replicate lines); WEAK 5/95 ≈ 5 % (all in the p > 0.99 tail except one; 3 of the 5 are sts_serial lines; the 15 replicate loops give 6/450 = 1.3 % WEAK) met on the 86 lines run — 0 FAILED, 3 WEAK (3.5 %); 28 test/ntuple combinations not run (§9)
NIST STS: every proportion within CI, every uniformity P ≥ 10⁻⁴ met — 188/188 lines inside the CI, all uniformity P ≥ 10⁻⁴ met — 188/188 lines inside the CI, all uniformity P ≥ 10⁻⁴
TestU01: no statistic outside [10⁻¹⁰, 1 − 10⁻¹⁰]; none suspicious met (SmallCrush) met (SmallCrush)
Scaled χ² at 99 % for each game; runs/serial at 99 %; shuffle 52 × 52 p ≥ .01 met (§5) met (§5)
Adversarial-player outcomes indistinguishable from honest met (§5.1) met (§5.1)

8. Invalid runs — quarantined, not evidence

Directory What Why invalid Disposition
results/dieharder/_smoke-invalid/chacha-13MB/ dieharder -a -g 201 -f on the 13 MB smoke file (raw/chacha/raw.bin) 387 file rewinds; a diehard_craps FAILED line in it is an artefact of recycled input retained for transparency; retracted; runner now refuses files < 1 GB
results/dieharder/_invalid-rewound/chacha-4GB-file/ dieharder -a -g 201 -f on the 4 GB chacha file (63 PASSED / 1 WEAK / 0 FAILED as printed) 19 file rewinds (from diehard_squeeze onward, cumulative 4 rewinds by rgb_bitdist 8); a full battery needs ≈ 250 GB retained; retracted; superseded by the stream run (§3)
results/sts/chacha first attempt (2026-09-14 00:01–01:24) STS chacha in the shared sts-2.1.2 tree a 5-sequence debug run was started in the same experiments/ directory and truncated the files the 1000-sequence run was writing deleted; re-run in a private tree (labtools/sts/sts-chacha), result in §4
results/dieharder/{chacha,hmac}-stream first attempts (2026-09-13 23:31–23:33 and 2026-09-14 00:01–00:25) single-process dieharder -a -g 200 producer killed together with the worker session (stdin_input_raw(): Error: EOF), no result lines survived deleted; replaced by the per-test resumable runner

9. Not run, and why

Item Reason How the lab can run it
Dieharder rgb_lagged_sum ntuple 14–32 (chacha), and the tail of the battery for hmac (§3.2) data volume: 0.44 × (n + 1) GB per ntuple → ≈ 200 GB for 14…32; HMAC_DRBG produces ≈ 1 MB/s per core (≈ 55 µs per 256-bit block through the full ceremony path) bash lab/tools/run-dieharder-stream.sh <mech> <workers> — resumable, finished tests are skipped
TestU01 Crush / BigCrush 2³⁵ / 2³⁸ words; attempted for chacha at 00:26Z, aborted at 00:40Z (≈ 10 h projected at the 4 MB/s available under load) bash lab/tools/run-testu01-crush-stream.sh chacha crush 8 on an idle 8-core host ≈ 2.5 h; hmac Crush ≈ 19 h
hmac scaled outcomes at 10⁸ (main sets at 2 × 10⁷; 10⁸ sets for dice/roulette/coin/slot-reel running in results/outcomes/hmac-1e8/, exact counts in EVIDENCE-INDEX.md) 1.5 CPU-hours per 10⁸ game P=4 bash lab/tools/run-outcomes-hmac-1e8.sh (resumable)

10. Reproducibility — exact commands

# 0. build + fingerprints (BUILD.md)
npm ci && npm run lab:verify-build
# 1. raw 4 GB files (SEED-BIG; sha256 must match results/raw/<mech>/raw-32gbit.bin.sha256)
node lab/tools/collect-raw.mjs --bits 32e9 --mechanism chacha --seed $(cat lab/results/raw/chacha/SEED-BIG) --workers 4 --words-per-draw 1 --draws 64 --out lab/results/raw/chacha/raw-32gbit.bin
node lab/tools/collect-raw.mjs --bits 32e9 --mechanism hmac   --seed $(cat lab/results/raw/hmac/SEED-BIG)   --workers 4 --words-per-draw 8 --draws 64 --out lab/results/raw/hmac/raw-32gbit.bin
# 2. Dieharder full battery, stream (resumable)
bash lab/tools/run-dieharder-stream.sh chacha 4 ; bash lab/tools/run-dieharder-stream.sh hmac 5
bash lab/tools/summarize-dieharder-stream.sh chacha
# 3. NIST STS (one private STS tree per mechanism)
bash lab/tools/run-sts.sh chacha lab/results/raw/chacha/raw-32gbit.bin 1000 1000000 /path/to/sts-2.1.2-copy-A
bash lab/tools/run-sts.sh hmac   lab/results/raw/hmac/raw-32gbit.bin   1000 1000000 /path/to/sts-2.1.2-copy-B
# 4. TestU01
lab/tools/testu01/rain_file_gen lab/results/raw/chacha/raw-32gbit.bin smallcrush "RAIN chacha raw-32gbit"
bash lab/tools/run-testu01-crush-stream.sh chacha crush 8
# 5. scaled outcomes + analysis (P parallel jobs)
P=4 bash lab/tools/run-outcomes.sh
# 6. this report's tables
bash lab/tools/report-tables.sh all

11. Provenance

Report tables generated 2026-09-14T09:18Z from lab/results/. Run journal: lab/results/PROGRESS.md; raw logs: lab/logs/. Data files > 100 MB are not committed (regenerable from the seeds; sha256 recorded next to each).

← Back to the package index · Rendered 2026-09-14 09:34 UTC from the repository copy; the markdown in the zip / repo is the document of record.