No description
  • Rust 64.7%
  • HTML 14.8%
  • Nix 8.9%
  • CSS 4.9%
  • Shell 3.8%
  • Other 2.9%
Find a file
2026-10-02 15:00:57 +00:00
docs feat: tui 2026-10-02 15:00:57 +00:00
migrations init 2026-10-01 16:11:23 +00:00
nix feat(web)!: serve the house dashboard on its own port 2026-10-01 16:31:09 +00:00
scripts feat(web)!: serve the house dashboard on its own port 2026-10-01 16:31:09 +00:00
src feat: tui 2026-10-02 15:00:57 +00:00
static feat: tui 2026-10-02 15:00:57 +00:00
tests init 2026-10-01 16:11:23 +00:00
.gitignore init 2026-10-01 16:11:23 +00:00
Cargo.lock init 2026-10-01 16:11:23 +00:00
Cargo.toml feat(web)!: serve the house dashboard on its own port 2026-10-01 16:31:09 +00:00
flake.lock init 2026-10-01 16:11:23 +00:00
flake.nix init 2026-10-01 16:11:23 +00:00
README.md docs: add technical hackathon judge Q&A 2026-10-02 10:53:13 +00:00

🎲 Bark Dice

Provably fair, Satoshi Dice-style betting settled over Ark. Players pay a stake from any Bark wallet to a one-time Ark address. The house rolls once, in the same database transaction that accepts the stake, and winnings land at the player's address within seconds. Every roll can be verified in the browser or with a standalone CLI.

Runs on signet with test coins only.

 Player's Bark wallet ──stake──▶ one-time tark1… address ┐
                                                         │  barkd 0.7.1 (house wallet, loopback, bearer auth)
 Browser ◀── /api ──▶ bark-dice (axum) ◀── history/send ─┘
                       │  settlement worker: observe → roll → pay → reconcile
                       └─ SQLite (WAL): bets · payments · obligations · append-only audit log

How the game works

The bet

The house rolls a random whole number from 0 to 9999. You pick a target, and if the roll is below it, you win a fixed multiple of your stake:

Game You win if the roll is… Chance Payout A 2,000-sat bet pays
1.97× below 5000 50% 1.97× 3,940
3.94× below 2500 25% 3.94× 7,880
9.85× below 1000 10% 9.85× 19,700
49.25× below 200 2% 49.25× stake capped at 1,015

The payout includes your stake, so winning 3,940 on 2,000 is a profit of 1,940. No single bet may pay more than 50,000 sat (DICE_MAX_PAYOUT), which is why the 2% game has a lower stake cap.

How the house makes money

A fair payout would be the inverse of the odds: 2× at 50%, 10× at 10%. The house pays 1.5% less:

payout = floor(stake × 9850 / target)

On average every bet returns 98.5% of what's staked, and that 1.5% is the house's expected profit. Any single bet is pure luck; the edge only shows across many bets.

One bet, step by step

  1. The house commits. It draws a secret 256-bit server seed and shows you only its SHA-256 hash.

  2. You choose. You pick the game, the stake, your payout address and your own client seed. The house couldn't have known your seed when it committed.

  3. The terms are locked. The house gives you a one-time Ark address and a 10-minute deadline. Every detail of the bet, including both seeds' inputs, is written into the bet's terms and hashed.

  4. You pay. You send the stake to that address from any Bark wallet.

  5. The house rolls. As soon as the payment arrives, the house computes the roll, once, in the same database transaction that records your payment:

    roll = HMAC-SHA256(key = server seed, message = hash of the terms)  →  0–9999
    
  6. You get paid and get the proof. Winnings go to your address over Ark, usually within a few seconds, and the house reveals the server seed.

Where the randomness comes from

Input Source Purpose
Server seed (256 bits) rand::rng(): a ChaCha12 cryptographic generator seeded and periodically reseeded from the operating system (getrandom) The unpredictable part of the roll. Secret until the bet settles.
Client seed Your choice. The web page fills in 96 random bits from crypto.getRandomValues, and the TUI and demo.sh use 48 random bits. You can type your own. Stops the house from picking a server seed in advance that loses against you. It only has to be unknown to the house; it doesn't need much entropy.

HMAC-SHA256 mixes the two. Without the server seed its output is indistinguishable from random, and with the seed anyone can recompute it. The output is cut into 32-bit samples. Samples of 4,294,960,000 or more are thrown away and the next one is used, so taking the rest mod 10,000 doesn't favour low numbers.

Checking the house

Once the seed is revealed, you can recompute everything yourself:

  • Was the seed fixed in advance? sha256(server seed) must equal the hash you were shown before you chose your seed.
  • Is this my bet? The hash of the terms must match. Changing any character, such as your address, your seed or the payout, changes the roll completely.
  • Is the roll right? Recomputing the HMAC must give the same number, and the win or loss and the payout must follow from it.

Three independent verifiers run these checks: the bet page in your browser (WebCrypto, sharing no code with the server), ./bin/dice-verify receipt.json, and the TUI's proof panel. They all pass the published test vectors in docs/test-vectors.json.

What fairness does not guarantee

  • The house can still refuse to pay. A receipt proves you won, but this is a house-run game, not a smart contract.
  • The house knows the outcome before you pay, because the roll is fixed once the terms are. It can't change the outcome, only decline to honour it.
  • ⚠️ Known weakness in bark-dice/v1: the house could grind the roll. The one-time payment address and the deadline are chosen by the house after it sees your client seed, and both are part of the hashed terms. A dishonest house could keep generating new addresses, or nudge the deadline by a second, recomputing privately until it found a losing roll, and every check would still pass. This server doesn't do that, but the protocol doesn't stop it. The fix (planned as bark-dice/v2) is to fix the address and the deadline together with the commitment, before you choose your seed. See docs/FAIRNESS.md.

When a payment goes wrong

What you send What happens
The exact stake, on time The bet rolls once.
More than the stake The bet plays at the quoted stake, and the extra is refunded.
Less than the stake Refunded in full. The bet keeps waiting for a full payment.
Anything after the deadline Refunded in full, and no roll happens.
A second payment to the same bet Refunded. A bet never rolls twice.

Refunds go to your payout address. Ark payments currently cost 0 sat in fees.

How the house protects its money (and yours)

  • Bankroll reservation: before you get a payment address, the house sets aside your maximum possible win, and it refuses bets it couldn't cover.
  • Every sat explained: the house ledger is reconciled against the wallet's real balance, and it has matched to the sat throughout testing (dashboard: ledger vs wallet ✓ exact).
  • No double payouts: each payout attempt is saved before it's sent and matched to a tagged wallet movement afterwards. If a crash or timeout leaves the outcome unclear, the payout waits for an operator instead of being retried blindly.

Full specification: docs/FAIRNESS.md.

Deploying

On NixOS: the flake exports a module with two roles. services.bark-dice.house runs the house wallet and the game server. services.bark-dice.player runs a player wallet and the TUI, optionally as a full-screen booth. A minimal house:

imports = [ bark-dice.nixosModules.default ];
services.bark-dice.house = { enable = true; createWallet = true; backup.enable = true; };

This gives you sandboxed systemd units, private state in /var/lib/bark-dice, a generated admin token and hourly backups. Players are served on 127.0.0.1:8080 and the operator dashboard on 127.0.0.1:8081. Publishing the player port through your own reverse proxy or tunnel is up to you; never publish the dashboard port. Full guide: docs/DEPLOY.md covers funding, wallet restore, the operator dashboard over SSH, players, the kiosk, a single demo box, mainnet and running without NixOS. nix build .#checks.x86_64-linux.vm runs a two-machine VM test of the module.

Run locally (development)

# 1. Wallets (once): house and player on the public signet Ark
./bin/bark create --signet --ark https://ark.signet.2nd.dev --esplora https://esplora.signet.2nd.dev --datadir wallets/house
./bin/bark create --signet --ark https://ark.signet.2nd.dev --esplora https://esplora.signet.2nd.dev --datadir wallets/player
#    Fund the house from https://signet.2nd.dev (GitHub login, up to 100k sat)

# 2. Run (starts barkd, builds, serves http://127.0.0.1:8080)
scripts/run.sh

# 3. Play from the terminal, or open the web UI
scripts/demo.sh lt5000 1000
scripts/pilot.sh 20          # bounded pilot with latency and reconciliation report
scripts/tui.sh               # 🎲 terminal showcase (add --auto for autoplay)

Terminal showcase

scripts/tui.sh starts the player wallet as its own barkd (port 3536) and opens dice-tui: block-digit slot-machine rolls, confetti and a bell on wins, the protocol timeline with measured timings, the player's wallet next to the house's live books, and every receipt re-verified on screen. Keys: ←→/1-4 odds, ↑↓ stake, ⏎ roll, a autoplay, p receipt, ? help, q quit. Best at 140×42 or larger; works down to 120×34. While it runs, the player wallet is locked to barkd, so demo.sh/pilot.sh (which use the bark CLI) must wait until it exits.

The house dashboard is on its own port, at http://127.0.0.1:8081/house#token=$(cat run/admin.token). Only :8080 is meant for players: to let friends play over the internet, run nix run nixpkgs#cloudflared -- tunnel --url http://127.0.0.1:8080 and send them the trycloudflare.com URL it prints.

Toolchain: nix develop (see flake.nix), or nix shell nixpkgs#cargo nixpkgs#rustc nixpkgs#gcc. bin/ holds the official, SHA256-checked bark/barkd 0.7.1 release binaries.

Tests

cargo test     # 21 unit tests + 9 settlement tests against a fault-injecting mock barkd

The settlement tests cover duplicate observations, simultaneous payments, timed-out sends, crashes before and after a send, rejected sends, repeated and late payments, and worker restarts. None of them produces a duplicate payout or loses a liability.

Layout

Path What
src/fairness.rs Game rules, commit-and-reveal, unbiased HMAC roll, receipt verification
src/db.rs Schema, state machine, bankroll reservation, payment policy, ledger
src/worker.rs Settlement loop and payout reconciliation
src/bark.rs barkd REST adapter
src/web.rs Player API, QR codes, receipts, house API and alerts
src/bin/dice-verify.rs Standalone verifier and test-vector generator
src/bin/dice-tui/ Terminal showcase (ratatui): plays real rounds, animates, verifies
static/ Player UI, house dashboard, independent WebCrypto verifier
docs/FAIRNESS.md Versioned game and fairness spec (bark-dice/v1)
docs/INTEGRATION.md barkd API contract, measurements, reconciliation design
docs/RUNBOOK.md Operations: start/stop, alerts, manual review, backup and restore
docs/PILOT.md Signet pilot results
docs/DEMO.md Hackathon demo script
docs/JUDGE_QA.md Hackathon judge questions and answers, including technical limits and evidence
docs/DEPLOY.md NixOS deployment (house, player, kiosk)
nix/ Packages (bark-dice, pinned bark-release), NixOS module, VM test