# Dicecore 3.7.1 — reproducible fairness study

Study: `dicecore-3.7.1-2026-09-28`. Publisher: Mini Kraken (internal engineering study, not independent certification).

The retained first completed collection has 14,400,000 faces in 22 datasets. Both primary tests per dataset form one Holm family of 44 tests at alpha 0.01. Zero primary hypotheses were rejected. This is evidence of compatibility with the tested models, not proof of exact PRNG uniformity or resistance to cheating.

## Contents and integrity

- `data/`: all unsigned-byte face streams, 16-byte replay seed streams, manifest, original protocol and package license.
- `analysis/`: unrounded statistics, CSV, five SVG/PNG figures.
- `collect.mjs`: public API collection and exact replay verification, Node >=22.
- `analyze.py`: deterministic statistical analysis; does not draw new outcomes.
- `self_test.py`: positive controls for bias, pair dependence, Holm, exact intervals and rejection mapping.
- `verify_package.py`: verifies the official npm tarball's SHA-512 SRI and all 89 engine/license SHA-256 fingerprints. Network required only for this command and dependency installation.
- `browser.html`, `browser-source.mjs`, `browser-runner.mjs`: actual browser collection using Web Crypto. The runner is an esbuild 0.28.1 production bundle of the audited npm library, accompanied by its MIT license.
- `protocol.json`: original frozen protocol, unchanged after inspecting outcomes.
- `evidence.json`: source references, scope, environment and completed verification records.
- `frontend-path.md`: publisher-provided integration excerpts and file fingerprints; the application repository is not publicly retrievable.
- `package-verification.json`: official tarball verification result.
- `pack.py`: rebuilds this distribution from data and analysis folders.
- `SHA256SUMS.txt`: checksums of every other file inside the ZIP. The website's outer checksum list additionally covers the ZIP itself.

Face file order is call order, then the public result's `dice` array order. Each `.u8` byte is one `rawValue` in 1..s. Each `.seeds` file concatenates the hexadecimal `seedMaterial` decoded into 16 bytes per call. Re-encode these bytes to hex; do not interpret them as native-endian integers. `manifest.json` supplies notation, algorithm/version, replay schema, plan fingerprint, calls, hashes and replay template. There are 28,000 batch calls, 350,000 fresh Node calls and 50,000 fresh browser calls (428,000 retained seeds).

The statistical analysis includes every retained face. No observed sequence is discarded or replaced. Rejection sampling is an internal raw-word mapping operation before a face exists; it is not selection of completed dice rolls.

## Reproduce the published results

Extract `study.zip` to an empty directory and open a terminal there. The tested environment was Node 24.18.0, Python 3.12.14, NumPy 2.3.3, SciPy 1.16.3, Matplotlib 3.10.7 and Chromium 154.0.8037.0 on Windows x64. Pin the library, not its current latest release.

```sh
npm init -y
npm install --ignore-scripts --save-exact @erpg/dicecore@3.7.1
python -m venv .venv
```

Activate the environment: `source .venv/bin/activate` on macOS/Linux or ` .\.venv\Scripts\Activate.ps1` in PowerShell. Alternatively call the environment's Python executable directly; activation is optional.

```sh
python -m pip install -r requirements.txt
python verify_package.py --data data --out verified-package.json
python self_test.py
node collect.mjs --verify --out data
python analyze.py --data data --out reproduced-analysis
```

`--verify` recomputes every face through the library's public replay API and checks the retained protocol, installed engine, raw-face and seed hashes. It stops on any mismatch. It does not trust the published aggregate counts. `analyze.py` independently recomputes every histogram, test statistic, adjusted p-value, interval, autocorrelation and figure. Compare `reproduced-analysis/statistics.json` with `analysis/statistics.json`: scientific fields should agree (allow floating-point tolerances across platforms); runtime metadata and rendered image bytes may differ. JSON p-values are unrounded; the article rounds for readability.

To check the ZIP contents with only the Python standard library:

```sh
python -c "import hashlib,pathlib; rows=pathlib.Path('SHA256SUMS.txt').read_text().splitlines(); assert all(hashlib.sha256(pathlib.Path(r[66:]).read_bytes()).hexdigest()==r[:64] for r in rows); print('All checksums match')"
```

Hashes establish consistency with the published bytes. An internal timestamp and hashes are not external notarization, a signature from an independent auditor, or proof that seeds were never selected by an adversary.

## Run a genuinely new experiment

```sh
node collect.mjs --out new-data
python -m http.server 8080 --bind 127.0.0.1
```

Open `http://127.0.0.1:8080/browser.html` in Chromium. Wait for completion and download all three files (`browser-fresh-xoshiro128ss-d20.u8`, `.seeds`, `browser-dataset.json`) into one folder, for example `browser-new/`. In a second terminal with the same environment:

```sh
node collect.mjs --import-browser browser-new/browser-dataset.json --out new-data
python analyze.py --data new-data --out new-analysis
```

The importer checks the protocol hash and replays every browser face before merging it. Do not analyze just the Node profiles: the fixed experiment expects all 22 datasets. A fresh experiment uses fresh OS entropy, so its seeds, counts and p-values will differ. Report the first complete dataset even if it rejects a hypothesis; retrying until a favorable result invalidates the procedure. `collect.mjs` refuses to overwrite an existing manifest. `--smoke` generates smaller development fixtures, explicitly excluded from publication analysis.

The provided browser runner is the archived, integrity-fingerprinted build. To build your own runner from the source, use the same package and esbuild version:

```sh
npm exec --yes --package=esbuild@0.28.1 -- esbuild browser-source.mjs --bundle --format=esm --platform=browser --minify --define:process.env.NODE_ENV=\"production\" --outfile=browser-runner-local.mjs
```

Shell quoting of `--define` differs across platforms. An equivalent Node API build uses `define: { 'process.env.NODE_ENV': JSON.stringify('production') }`. Change the HTML import to your local runner only for your new experiment. Retain the bundled package license when redistributing it. The archived runner additionally has a license banner; byte-for-byte build identity requires the same banner and build settings, and is distinct from replay identity.

## Models and limits

Uniformity: Pearson chi-square against `N/s`, degrees of freedom `s-1`. Independence: Pearson contingency test on disjoint adjacent pairs with empirical marginal expectations, no Yates correction; `b=min(s,20)` buckets, `(b-1)^2` degrees of freedom. d100 buckets contain five consecutive faces. Expected contingency cell counts are checked to be >=5. Holm adjusts all 44 primary p-values together at alpha 0.01.

Five hundred face probabilities receive a separate simultaneous 99% null reference envelope via binomial quantiles and Bonferroni. Individual 95% Clopper-Pearson intervals are not simultaneous. The 20-lag autocorrelations per dataset are exploratory, with a normal approximation Bonferroni guide over 440 lags; they are not 440 extra primary tests. The plotted d20 guide uses N=50,000 and is wider than the N=1,000,000 reference.

The advantage, disadvantage and 2d6 plots reuse disjoint pairs of the original raw streams (500,000 derived observations per plot); they illustrate transformations and are not independent new experiments or tests of every modifier implementation.

The mapping proof assumes uniform independent 32-bit source words. xoshiro128** and MT19937 are deterministic non-cryptographic PRNGs. Fresh Web Crypto seeds do not turn them into cryptographic generators. This study covers their small-sided face streams through the exact npm 3.7.1 public API, with the frontend's xoshiro option and the library's default MT alternative. It does not establish exact IID output over all possible states, every browser/OS, long-range independence, BigCrush/TestU01 success, unpredictability, or tamper resistance. The all-zero xoshiro state is repaired deterministically by the implementation.

No production-user histories, Go backend/Fortuna runtime measurements, browser extension tampering, server authentication or multiplayer seed commitments were audited. The source revision referenced in the article was inspected separately; npm metadata lacks `gitHead`, so no claim is made that that repository revision produced the npm tarball.

## License and sources

The archived npm 3.7.1 package declares MIT and includes `package-license.txt`; preserve its notices for redistribution. The current fork repository has separate Arkanus license terms. Do not assume a repository's current license equals an older npm artifact's license.

- Package: https://www.npmjs.com/package/@erpg/dicecore/v/3.7.1
- Fork: https://github.com/arkanus-app/rpg-dice-roller
- Inspected source: https://github.com/arkanus-app/rpg-dice-roller/tree/1940044c34c47a3c55faa723ccb0d524eef79b03
- Original upstream: https://github.com/dice-roller/rpg-dice-roller
- PRNG authors: https://prng.di.unimi.it/
- Mersenne Twister authors: https://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/emt.html
- Web Crypto: https://www.w3.org/TR/webcrypto/#Crypto-method-getRandomValues
- Pearson uniformity: https://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm
- Analysis APIs: https://docs.scipy.org/doc/scipy/reference/stats.html
- Holm: https://stat.ethz.ch/R-manual/R-devel/library/stats/html/p.adjust.html
