Files
wownerogue/docs/ARCHITECTURE.md
T
jwinterm fad2a695bd Wownerogue
A browser-based roguelike synchronized with crypto block timing. A run is a race
against the next block: reach the exit before the block lands, or the house wins.
The same codebase runs the solo dungeon, the social Tavern, and operator-selected
PvP and race rulesets, with separate free and paid leaderboards.

Self-hostable under the MIT License. See README.md to run it, docs/ARCHITECTURE.md
for the design, and docs/DEPLOY.md to operate an instance.
2026-07-27 08:23:01 -04:00

7.5 KiB

Architecture

Wowngeon is a provably fair, block-timed roguelike with crypto entry and payouts. A solo run is a race against a random block deadline; multiplayer matches run the same engine under a ruleset. The game also acts as a multi-chain on-ramp for the Smirk wallet.

This page is the index for the architecture docs. Each subsystem has its own page.

System map

             ┌──────────── players (browser) ─────────────┐
             │  index.html · tavern.html · match.html     │
             │  html/js/render (render kit)               │
             └───────────────┬───────────────┬────────────┘
                    Socket.IO │               │ REST (auth, payments, status)
             ┌────────────────▼───────────────▼────────────┐
             │  src/network/*  socket handlers, chat,      │
             │                 tavern, match, payments     │
             ├─────────────────────────────────────────────┤
  game core  │  src/game/*      dungeon · rulesets · modes │  → RULESETS.md
  money      │  src/payments/*  providers · wallet-rpc     │  → PAYMENTS.md
  cosmetics  │  src/multiplayer/entitlements + catalog     │  → MONETIZATION.md
  chains     │  src/chain/chainProfile.js                  │  → chain profiles, below
  chat       │  src/network/chat/*  local + nostr          │  → CHAT_AND_NOSTR.md
             └─────────────────────────────────────────────┘
                    │                         │
              PostgreSQL              Wownero/Monero wallet-RPC, BTCPay/checkout gateways

src/index.js is the composition root: it builds the Express app and Socket.IO server, wires the database manager, wallet RPC service, payment config, game mode manager and socket handlers, and mounts the REST routes in src/routes/.

Subsystem guide

Area Where Doc
Credits, cosmetic catalog, entitlements src/multiplayer/entitlements.js, src/services/catalogService.js MONETIZATION.md
Chain constants for the five Smirk chains src/chain/chainProfile.js below
Payment providers and payouts src/payments/ PAYMENTS.md
Rulesets and match engine src/game/rulesets/, src/multiplayer/MatchEngine.js RULESETS.md, MATCH_MODE.md
Tavern lobby and spectating src/network/tavernManager.js, src/multiplayer/Room.js TAVERN_AND_MULTIPLAYER.md
Chat, local and nostr src/network/chat/ CHAT_AND_NOSTR.md
Client render kit and art packs html/js/render/ RENDER_PACKS.md
Balance and pacing src/sim/, src/game/difficultyConfig.js BALANCE_SIM.md, MULTI_LEVEL.md
Deployment and operations scripts/deploy/ DEPLOY.md, DEPLOY_INSTANCES.md, LOGS_AND_BACKUP.md
Financial reconciliation and exports src/services/ FINANCIAL_RECONCILIATION.md, FINANCIAL_EVENT_EXPORT.md

Optional subsystems are environment-gated and dormant by default: payment gateways register only when their *_URL / *_STORE_ID / *_API_KEY trio is set, global nostr chat requires NOSTR_CHAT_ENABLED=true, and match rooms require MATCH_ENABLED=true. With none of those set the server runs the native wallet-RPC payment path, local socket chat, and solo play only. The full environment template is src/.env.example.

Chain profiles

src/chain/chainProfile.js is the single source of truth for per-chain constants across BTC, LTC, XMR, WOW and GRIN: decimalsFor, meanBlockTimeMsFor, familyFor (monero / utxo / mimblewimble), uriSchemeFor and atomicDivisor (BigInt).

Chain Decimals Mean block time Family URI scheme
WOW 11 5 min monero wownero
XMR 12 2 min monero monero
BTC 8 10 min utxo bitcoin
LTC 8 2.5 min utxo litecoin
GRIN 9 1 min mimblewimble grin

An unrecognized chain id resolves to a default profile shaped like XMR: 12 decimals, 120000 ms mean block time, monero family, monero URI scheme. Callers therefore never crash on a mis-set CRYPTO_TYPE. Adding a chain is one entry in the registry plus a family adapter.

Money math is exact: src/money/atomic.js operates on BigInt atomic units parameterized by decimals, never floats.

Game balance and pacing

A solo run is bounded by a random deadline drawn from an exponential distribution with mean equal to the chain's block time, so the house edge is a convolution of the run-completion distribution with block cadence rather than a number that can be set by hand. src/sim/ drives the real engine with headless bots and integrates the deadline out analytically, reporting a measured house win rate per preset and per network.

NETWORK_TUNING in src/game/difficultyConfig.js scales dungeon depth to block time. Dungeon size and monster behaviour come from the difficulty preset; the per-network entry overrides only the level count:

Chain Levels
GRIN 1
XMR 1
LTC 2
WOW 2
BTC 3

NETWORK_TUNING_DISABLED=true bypasses the override and leaves the preset's own level count in place. Descending emits a game_event, which the client handles in html/js/network/socketHandlers.js to surface level progress. See BALANCE_SIM.md and MULTI_LEVEL.md.

Client render kit

html/js/render/ (exposed as RK) draws a shared Scene model in four techniques: Tiled, ASCII, Iso and 3D. Art packs are interchangeable data, a pure-canvas FX layer supplies torch and hazard effects, and the pack and technique available to a player come from the operator-owned cosmetic catalog. Tiled and ASCII are free; Iso and 3D are premium. See RENDER_PACKS.md.

Provable fairness

src/game/provablyFair.js derives each dungeon from a committed seed, src/game/fairnessVerifier.js exposes verification endpoints, and src/views/verifyPage.js renders the public verification page so a player can reproduce a dungeon from its published proof.

Conventions

  • Game id is a UUID stored in games.dungeon_seed; games.id is a serial integer. Match game objects with WHERE dungeon_seed = $N, never WHERE id = $N.
  • Socket ids are volatile and change on refresh. Stable identity is the database user_id or the session, never socket_id.
  • WOW has 11 decimals (atomic divisor 1e11), unlike Monero's 12.
  • Cosmetic entitlements are per-pack, resolved by tier and by lifetime credits purchased in src/multiplayer/entitlements.js. Buying credits does not by itself unlock premium packs.
  • Migrations live in src/migrations/NNN_*.sql and run in numeric order.
  • Tests: cd src && npm test (Jest, --runInBand).

Source files carry JSDoc for API-level detail; these pages cover the seams between subsystems and the operator-facing intent behind them.