@sizls/minglingo-verify
v0.1.0
Published
Verify any Minglingo game happened as claimed. Deterministic-replay CLI + library over the public verify-artifact endpoint — engine-agnostic (bingo, trivia, prediction, competition, plus future plugin games). `npx @minglingo/verify <gameId>` → provably-fa
Downloads
165
Maintainers
Readme
@sizls/minglingo-verify — prove a Minglingo game happened
Deterministic-replay CLI + library for any game hosted on Minglingo. One command, no build step, no server trust, prints a provably-fair receipt in your terminal:
npx @sizls/minglingo-verify <gameId>✓ provably fair stream-trivia · trivia
────────────────────────────────────────
game game-abc-123
status completed
completed 2026-06-01T00:05:00.000Z
sessions 1
events 47 (2 retractions)
final prompts 45
server hash 0f8a1b3c9d4e…
local hash 0f8a1b3c9d4e… ✓Exit code 0 means the local replay hash matched the server's canonical hash. Exit code 1 means it didn't — the log was tampered with or the CLI is out of date.
Engine-agnostic by design
Minglingo hosts many game types on one runtime — traditional bingo (75-ball, 90-ball), social bingo variants (custom, speed, battle-royale, team-wars, tournament), stream-trivia, quiz-show, crowd-predictor, and any future third-party plugin game shipped through Phase E. This CLI verifies all of them from the same command.
Every game records its state changes as engine-agnostic Kite events (call.issued + call.retracted), and the retraction-aware projection is shared across engines. Bingo tracks callValue, trivia tracks questionId, prediction tracks predictionId — the CLI reads the engineKind off the artifact and replays through the same code path either way. New engines added by plugins verify with the same CLI on day one.
Why it exists
Every social-gaming app on the market makes a "provably fair" claim ("we don't rig outcomes, wins are legitimate"). None of them let you check. You take their word or you don't.
Minglingo's game state is a pure function of an append-only event log. Given the same events + the same projection, any consumer computes byte-identical final state — there's nothing hidden in a database that changes the outcome. This CLI runs that math on your machine so you don't have to trust the server for the fair-play claim.
Install
You don't. Use it with npx:
npx @sizls/minglingo-verify <gameId>Or drop it in your CI so every closed game auto-verifies:
- name: Prove last night's game
run: npx @sizls/minglingo-verify ${{ vars.LAST_NIGHT_GAME_ID }}Flags
--host <url> Verify against a custom host (default: https://minglingo.pages.dev)
--json Emit the full receipt as JSON (colour-free, machine-readable)
--quiet Suppress output; exit 0 on pass, 1 on fail
--help Show usageExit codes
| Code | Meaning |
|-----:|---------|
| 0 | provably fair (byte-identical replay + server hash match) |
| 1 | verification failed (see --json .failures for the list) |
| 2 | fetch failed (network, 4xx/5xx) |
| 64 | usage error |
Library usage
The CLI is a thin wrapper over the exported library. Same primitives are available programmatically for any consumer — a leaderboard aggregator, a support-desk auto-responder, an EU AI Act audit pipeline:
import { fetchAndVerify, verifyArtifact } from '@sizls/minglingo-verify';
const { artifact, receipt } = await fetchAndVerify('game-abc-123');
if (!receipt.provablyFair) {
throw new Error(`verification failed: ${JSON.stringify(receipt.failures)}`);
}
console.log(
`✓ ${artifact.gameId} — ${artifact.gameTypeId} (${artifact.engineKind}), ` +
`${receipt.reconstructedPromptCount} prompts, ${receipt.retractionCount} retractions`,
);verifyArtifact(artifact) takes an artifact you already have in hand (from a signed export, a local file, a paste from a Slack thread) and returns the same receipt shape — useful for offline audits.
Wire format
GET https://minglingo.pages.dev/api/games/<id>/verify-artifact returns:
{
"schemaVersion": 1,
"gameId": "game-abc-123",
"gameTypeId": "stream-trivia", // any Minglingo game type
"engineKind": "trivia", // engine that projects the events
"status": "completed",
"completedAt": "2026-06-01T00:05:00Z",
"sessionCount": 1,
"events": [
{ "seq": 1, "sessionNumber": 1, "kind": "call.issued", "key": "q_47", "at": "..." },
{ "seq": 2, "sessionNumber": 1, "kind": "call.issued", "key": "q_48", "at": "..." },
...
],
"hash": "sha256:0f8a1b3c9d4e..."
}The endpoint is:
- Public for completed non-private games across every engine. No auth.
- Rate limited at 60 req/min per IP (Cloudflare Durable-Object-backed).
- Edge-cached for 5-10 minutes — a completed game's artifact never changes, so a press-driven verify spike hits the edge cache and pays for itself.
- Never available for private or in-flight games (returns 404 — deliberate, so endpoint existence doesn't leak game state).
What "provably fair" means today
The CLI verifies:
- Deterministic replay — every recorded call/retraction, run through the same LIFO-per-session projection the server ran, reconstructs the authoritative prompt list. Same output for the same input, no engine-specific hidden branches.
- Content hash match — the SHA-256 of the canonical wire form matches the server's published hash. The server + the CLI observed the same event stream.
The CLI does NOT verify:
- Origin authenticity (yet). A dishonest server could serve a manufactured event sequence that hashes cleanly. Closing this needs Ed25519 signed exports + a public JWKS endpoint — that lands with the Kite plugin runtime (Phase E). Once available,
@sizls/minglingo-verifygains a--verify-signatureflag that checks the signature against the published key. TheschemaVersionfield on the wire gates the compat — older CLI releases will keep working against pre-signature artifacts.
Today's claim: any two observers running this CLI on the same game id, of any engine, see the same result byte-for-byte. That's the strongest determinism guarantee the current surface exposes.
Contributing
Lives in the sizls/minglingo monorepo under packages/minglingo-verify. PRs welcome — the interesting tests are in src/verifier.test.ts (unit, engine-agnostic).
License
MIT.
