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.
This commit is contained in:
jwinterm
2026-07-27 08:23:01 -04:00
commit fad2a695bd
578 changed files with 102281 additions and 0 deletions
+622
View File
@@ -0,0 +1,622 @@
# =============================================================================
# Wownerogue Server Configuration
# =============================================================================
# Copy this file to .env and customize for your environment.
# The server validates these settings at startup. Production fails closed on missing secrets,
# ambiguous payout flags, simulated blocks, or missing wallet/daemon RPC endpoints.
#
# This is a generic MIT self-hosting template. The named operator is solely responsible for the
# deployment and its legal/financial obligations; Such Software does not operate or endorse it.
# Do not set OPERATED_PRODUCT_PROFILE. That opt-in identifier is reserved for the two exact
# Such Software operated templates in .env.mainnet.example and .env.stagenet.example.
#
# QUICK START CONFIGURATIONS:
# ---------------------------
# 1. FREE MODE (no payments, just play):
# PAYMENTS_ENABLED=false
#
# 2. PAID MODE (1 WOW per game, 2x payout on win):
# PAYMENTS_ENABLED=true
# PAYMENT_MODES=direct
# + Configure wallet RPC and database
#
# 3. CREDITS MODE (buy bundles, play multiple games):
# PAYMENTS_ENABLED=true
# PAYMENT_MODES=credits
# + Configure wallet RPC and database
#
# 4. BOTH MODES (let users choose):
# PAYMENTS_ENABLED=true
# PAYMENT_MODES=direct,credits
# =============================================================================
# UNDERSTANDING ATOMIC UNITS
# =============================================================================
# Wownero (like Monero) uses "atomic units" - the smallest indivisible unit.
#
# 1 WOW = 100,000,000,000 atomic units (10^11, or 100 billion)
#
# Examples:
# 1 WOW = 100000000000
# 0.5 WOW = 50000000000
# 0.1 WOW = 10000000000
# 5 WOW = 500000000000
# 10 WOW = 1000000000000
#
# Why? Floating point math is imprecise. Atomic units ensure exact calculations.
# =============================================================================
# SECTION 1: BASIC SERVER SETTINGS
# =============================================================================
# Server port (default: 3000)
PORT=3000
# Environment: production | development | debug
# - production: minimal logging, real blockchain
# - development: verbose logging, can use simulated blocks
# - debug: maximum logging, debug hotkeys enabled
NODE_ENV=production
# External renderer downloads are disabled by default. When true, pinned Pixi/Three
# bundles may be loaded from jsDelivr; vendored, self-hosted assets are preferred.
RENDERER_CDN_ENABLED=false
# Network selection (only matters for Monero - Wownero only has mainnet)
MONERO_NETWORK=mainnet
# Optional: Display "Hosted by" link in footer (leave empty to hide)
# Example: https://yoursite.com or https://github.com/yourname
# HOSTED_BY=https://example.com
# HOSTED_BY_NAME=Example Host
# =============================================================================
# SECTION 2: DATABASE (PostgreSQL) - REQUIRED
# =============================================================================
# The game stores user sessions, payment history, and credits in PostgreSQL.
# You must create the database and user first (see README).
DB_HOST=localhost
DB_PORT=5432
DB_NAME=wownerogue
DB_USER=wownerogue
DB_PASSWORD=your_secure_password_here
# =============================================================================
# SECTION 3: GAME DIFFICULTY
# =============================================================================
# Controls dungeon size, monster behavior, and effective house edge.
#
# Preset | Dungeon | Monster Speed | Player Win Rate | Best For
# ------------|----------|---------------|-----------------|------------------
# easy | 30x15 | Slow | ~70% | Free play, demos
# normal | 45x22 | Medium | ~45% | Balanced gameplay
# hard | 55x28 | Fast | ~35% | Challenge mode
# casino | 70x35 | Fast | ~30% | Paid modes (default)
#
# If unset, defaults to 'casino' for paid modes, 'normal' for free mode.
# DIFFICULTY_PRESET=casino
# Fine-tune individual difficulty parameters (override preset defaults).
# Only set these if you want to deviate from the chosen preset.
#
# Dungeon layout:
# DUNGEON_WIDTH=70 # Map width in tiles
# DUNGEON_HEIGHT=35 # Map height in tiles
# DUNGEON_DUG_PERCENTAGE=0.10 # % of map that's open space (lower = tighter)
# DUNGEON_ROOM_WIDTH_MIN=3 # Room width range (min)
# DUNGEON_ROOM_WIDTH_MAX=4 # Room width range (max)
# DUNGEON_ROOM_HEIGHT_MIN=3 # Room height range (min)
# DUNGEON_ROOM_HEIGHT_MAX=4 # Room height range (max)
# DUNGEON_CORRIDOR_MIN=6 # Corridor length range (min)
# DUNGEON_CORRIDOR_MAX=15 # Corridor length range (max)
#
# Monster behavior:
# MONSTER_SPEED=1.0 # Moves per player move (1.0 = same speed, 0.5 = half)
# MONSTER_CHASE=0.97 # Chase probability 0.0-1.0 (rest is random wander)
# MONSTER_VISION=22 # Vision range in tiles
# MONSTER_DISTANCE=0.30 # Starting distance ratio 0.0-1.0 (lower = closer)
#
# Treasure placement:
# TREASURE_ROOM_POSITION=0.20 # Room position ratio 0.0-1.0 (lower = earlier room)
# TREASURE_EXIT_DISTANCE=0.85 # Distance from exit ratio 0.0-1.0 (higher = farther)
# =============================================================================
# SECTION 4: PAYMENT SYSTEM OVERVIEW
# =============================================================================
#
# PAYMENTS_ENABLED is the master switch:
# false = FREE mode, anyone can play, no crypto involved
# true = Paid mode, requires wallet RPC configuration
#
# PAYMENT_MODES determines HOW users pay (when payments enabled):
#
# ┌─────────────────────────────────────────────────────────────────────────┐
# │ MODE: direct │
# ├─────────────────────────────────────────────────────────────────────────┤
# │ • User pays X WOW per game │
# │ • If they WIN (escape dungeon): get PAYOUT_ESCAPE × X back (e.g., 2x) │
# │ • If they WIN with treasure: get PAYOUT_TREASURE × X back (e.g., 3x) │
# │ • If they LOSE (die): house keeps the payment │
# │ │
# │ Example: Pay 1 WOW, win = get 2 WOW. Die = lose 1 WOW. │
# └─────────────────────────────────────────────────────────────────────────┘
#
# ┌─────────────────────────────────────────────────────────────────────────┐
# │ MODE: credits │
# ├─────────────────────────────────────────────────────────────────────────┤
# │ • User buys a PACKAGE of credits upfront (e.g., 10 credits for 5 WOW) │
# │ • Each game costs 1 credit (or CREDITS_PER_GAME) │
# │ • Packages can include bonus credits (bulk discount) │
# │ • Payouts are optional - credits are usually just for playing │
# │ │
# │ Example: Buy 10 credits for 5 WOW = 0.5 WOW/game (50% discount!) │
# └─────────────────────────────────────────────────────────────────────────┘
#
# ┌─────────────────────────────────────────────────────────────────────────┐
# │ MODE: direct,credits (both enabled) │
# ├─────────────────────────────────────────────────────────────────────────┤
# │ • Users can choose which mode to use │
# │ • If PREFER_CREDITS_FIRST=true, credits are auto-used when available │
# │ • Good for giving users flexibility │
# └─────────────────────────────────────────────────────────────────────────┘
# Master switch: Set to false for completely free play (no wallet needed)
PAYMENTS_ENABLED=true
# FREE_PLAY_ENABLED: offer FREE play as a CHOICE even when payments are on. Free games
# are recorded as game_mode=FREE and appear on the Pleb leaderboard (NOT the Hall of
# Champions, which is for paid games). Always effectively on when PAYMENTS_ENABLED=false.
#
# DEPLOYMENT PATTERNS
# • Mainnet prestige: free-to-play + sell service credits/entry, with NO PAYOUTS.
# No-payout design is not a legal conclusion; an independent operator needs its own review.
# PAYMENTS_ENABLED=true FREE_PLAY_ENABLED=true PAYOUTS_ENABLED=false
# DIRECT_PAYOUTS_ENABLED=false CREDITS_PAYOUTS_ENABLED=false
# DIRECT_REQUIRES_ADDRESS=false CREDITS_REQUIRES_ADDRESS=false (no payout = no address)
# • Test/stagenet: test mechanics can exercise payouts only with conspicuous no-real-value copy.
# Never accept or represent mainnet funds under a test-only profile.
# PAYMENTS_ENABLED=true PAYOUTS_ENABLED=true *_PAYOUTS_ENABLED=true
FREE_PLAY_ENABLED=true
# Which payment modes to enable (comma-separated)
PAYMENT_MODES=direct,credits
# =============================================================================
# SECTION 5: DIRECT MODE CONFIGURATION
# =============================================================================
# Only relevant if PAYMENT_MODES includes "direct"
# Enable/disable direct payments
DIRECT_PAYMENT_ENABLED=true
# Price per game in atomic units (100000000000 = 1 WOW)
DIRECT_GAME_PRICE=100000000000
# Must user provide a payout address before playing?
# true = User must set address first (ensures they can receive winnings)
# false = User can play without address (but can't receive payouts!)
DIRECT_REQUIRES_ADDRESS=false
# Allow guest play without any address? (Usually false for paid modes)
DIRECT_ALLOW_GUEST_PLAY=false
# Payout multipliers for direct mode wins:
# If user pays 1 WOW and ESCAPE multiplier is 2.0, they get 2 WOW on escape.
DIRECT_PAYOUTS_ENABLED=false
DIRECT_PAYOUT_ESCAPE=2.0 # Escape dungeon = 2x payment
DIRECT_PAYOUT_TREASURE=3.0 # Escape WITH treasure = 3x payment
# =============================================================================
# SECTION 6: CREDITS MODE CONFIGURATION
# =============================================================================
# Only relevant if PAYMENT_MODES includes "credits"
# Enable/disable credits system
CREDITS_ENABLED=true
# How many credits does one game cost?
CREDITS_PER_GAME=1
# Must user provide address for credits mode?
CREDITS_REQUIRES_ADDRESS=false
# When both modes enabled:
ALLOW_MIXED_MODE=true # Show both options to user
PREFER_CREDITS_FIRST=true # Auto-deduct credits if user has them
# Credits payouts (usually DISABLED so purchases grant service play, not prize eligibility)
CREDITS_PAYOUTS_ENABLED=false
CREDITS_PAYOUT_BASE=50000000000 # Base amount for credit wins
CREDITS_PAYOUT_ESCAPE=2.0 # Same as direct mode
CREDITS_PAYOUT_TREASURE=3.0 # Same as direct mode
# Optional credit-payout bounds. The global PAYOUT_MAX_PER_GAME below is always
# the outer safety cap, even when this mode-specific maximum is larger.
# CREDITS_PAYOUT_MIN=10000000000
# CREDITS_PAYOUT_MAX=10000000000000
# Credit packages - JSON array of available bundles
# Fields:
# id - Unique identifier (shown in UI)
# credits - Number of game credits included
# price - Cost in atomic units (as STRING to avoid JS number precision issues)
# bonus - Extra free credits (0 for no bonus)
#
# Default packages (small discounts to encourage bulk purchase):
# small: 10 credits for 9 WOW (~10% discount vs 1 WOW/game direct)
# medium: 20 credits for 17 WOW (~15% discount)
# large: 50 credits for 40 WOW (~20% discount)
#
# Tip: Format JSON with newlines for readability, then minify for .env:
# [
# {"id":"small", "credits":10, "price":"900000000000", "bonus":0},
# {"id":"medium", "credits":20, "price":"1700000000000", "bonus":0},
# {"id":"large", "credits":50, "price":"4000000000000", "bonus":0}
# ]
#
CREDITS_PACKAGES='[{"id":"small","credits":10,"price":"900000000000","bonus":0},{"id":"medium","credits":20,"price":"1700000000000","bonus":0},{"id":"large","credits":50,"price":"4000000000000","bonus":0}]'
# Credit packages can bundle cosmetic/render pack grants and a premium tier via
# a "grants" field. Example:
# CREDITS_PACKAGES='[{"id":"supporter_25","credits":20,"bonus":5,"price":"1000000000000","grants":{"packs":["generated-skins","kenney-3d-characters"],"premiumLevel":"supporter"}}]'
# Standalone products (no game entry or credits required). Each product may grant packs,
# a premium tier, and/or funded race tickets. Crypto-match tickets must declare an atomic
# value exactly equal to MATCH_ENTRY_FEE_ATOMIC, and the product price must cover every ticket.
# COSMETIC_PRODUCTS='[{"id":"pack_3d","label":"3D Character Pack","price":"250000000000","grants":{"packs":["kenney-3d-characters"]}}]'
# COSMETIC_PRODUCTS='[{"id":"race_2","label":"Two race entries","price":"10000000000","grants":{"race_entries":2,"race_entry_value_atomic":"5000000000"}}]'
# =============================================================================
# SECTION 6b: EARLY ENTRY
# =============================================================================
# Early entry lets players start a game immediately without waiting for the
# next block. Risk: if a new block arrives before they escape, the game ends.
#
# Disabled by default for direct (paid-per-game) mode since players paid for
# a full block window. Useful for free and credits modes.
# Master toggle
EARLY_ENTRY_ENABLED=true
# Allow early entry in free mode?
EARLY_ENTRY_FREE_MODE=true
# Allow early entry in credits mode?
EARLY_ENTRY_CREDITS_MODE=true
# =============================================================================
# SECTION 7: PAYOUT SETTINGS
# =============================================================================
# Controls how winnings are sent back to players.
# Payouts are batched for efficiency and to reduce transaction fees.
# Master payout switch. The Such-operated mainnet product sells credits for leaderboard prestige
# without crypto winnings. Its separate XMR stagenet test product uses 2x/3x test-coin payouts.
# This switch suppresses payout creation, block/batch dispatch, and retry workers.
PAYOUTS_ENABLED=false
# Production mainnet payouts are additionally fail-closed. Only set this true after legal review,
# bankroll/risk review, and a real wallet-RPC integration test. It is not needed on stagenet.
ALLOW_MAINNET_PAYOUTS=false
# Minimum payout amount (atomic units). Prevents dust transactions.
# 10000000000 = 0.1 WOW
PAYOUT_MIN_AMOUNT=10000000000
# Maximum payout per single game (safety limit)
# 10000000000000 = 100 WOW
PAYOUT_MAX_PER_GAME=10000000000000
# How often to process payout queue (seconds). 300 = every 5 minutes.
PAYOUT_BATCH_INTERVAL=300
# Confirmed incoming outputs must reach at least this many chain confirmations before granting
# a game, credits, cosmetics, or race tickets.
PAYMENT_CONFIRMATIONS=1
# Retry failed payouts this many times before marking as permanently failed.
# Failed payouts are logged with error details. Check logs or query:
# SELECT * FROM payouts WHERE status = 'failed';
# To retry a permanently failed payout, reset it with:
# UPDATE payouts SET status = 'pending', retry_count = 0 WHERE id = <id>;
PAYOUT_MAX_RETRIES=3
# Max payouts to send in one batch (wallet RPC can timeout on large batches)
MAX_PAYOUT_BATCH_SIZE=50
# How often to retry failed/stuck payouts (milliseconds). Default: 300000 (5 min)
# PAYOUT_RETRY_INTERVAL_MS=300000
# =============================================================================
# SECTION 8: ANTI-ABUSE LIMITS
# =============================================================================
# Protect against bots and abuse
MAX_GAMES_PER_HOUR=60 # Max games one user can play per hour
MAX_PAYOUTS_PER_DAY=100 # Max payout transactions per user per day
MAX_CREDIT_PURCHASE_PER_DAY=100000000000000 # Max credit purchase (1000 WOW)
GAME_COOLDOWN_SECONDS=5 # Minimum seconds between games
# =============================================================================
# SECTION 9: WALLET RPC - REQUIRED FOR PAID MODES
# =============================================================================
# The wallet RPC handles:
# 1. Generating unique payment addresses for each user
# 2. Checking for incoming payments
# 3. Sending payouts to winners
#
# You must run wownero-wallet-rpc (or monero-wallet-rpc) separately:
#
# wownero-wallet-rpc \
# --wallet-file /path/to/wallet \
# --password-file /path/to/root-readable-wallet-password \
# --rpc-bind-ip 127.0.0.1 \
# --rpc-bind-port 34570 \
# --rpc-login user:strong-random-rpc-password
#
# Production requires rpc-login even on loopback. The application deliberately proves that an
# unauthenticated request receives a Digest challenge; configured credentials do not make an
# unauthenticated wallet daemon safe. Never combine rpc-login with --disable-rpc-login, and never
# expose wallet RPC beyond the local host.
#
# The wallet must be synced with a daemon to see incoming transactions.
PRIMARY_WALLET_ENDPOINT=http://127.0.0.1:34570
# Required together in production:
# WALLET_RPC_USER=wownerogue
# WALLET_RPC_PASSWORD=replace-with-a-distinct-strong-rpc-password
# =============================================================================
# SECTION 10: BLOCKCHAIN DAEMON RPC
# =============================================================================
# Used to get current block height for game synchronization.
# This is the daemon (wownerod/monerod), NOT the wallet.
#
# The game syncs to block timing - each block triggers game state checks.
PRIMARY_RPC_ENDPOINT=http://127.0.0.1:34568
FALLBACK_RPC_ENDPOINT=http://127.0.0.1:34568
# OPTIONAL: additional daemons, comma or whitespace separated, in PREFERENCE ORDER.
# PRIMARY/FALLBACK above are appended to this list and the whole thing is deduplicated:
# note that the two defaults are the SAME host, so out of the box there is exactly one node
# and no redundancy at all.
#
# A fallback only helps if it can fail independently. A second daemon in the same rack (or the
# same office) shares the outage that takes the first one down, so it buys nothing. What actually
# adds availability is a node reachable over a separate path: a home node across the mesh, or a
# public/community node.
#
# Every endpoint must still pass the chain-identity check before it may answer, so listing a
# remote node cannot silently serve a different chain's data. For FREE / CREDITS instances the
# daemon is only supplying block height, so a remote node is a reasonable fallback. For instances
# taking PAYMENTS, prefer daemons you control: block height feeds match fairness and entropy, and
# an untrusted node is an untrusted input to those.
#
# RPC_ENDPOINTS=http://127.0.0.1:34568,http://192.0.2.40:34568
#
# How long to stay on a backup before re-testing the preferred node (ms, minimum 1000).
# RPC_PREFERRED_RETRY_MS=60000
#
# When NO endpoint answers, the server does not guess: entries are refused with a clear message
# and nothing is charged. There is intentionally no wallet failover: two wallet-rpc processes
# over one wallet file can spend the same outputs. So a wallet outage declines paid intake while
# credits and free play keep working.
# How often to poll for new blocks (milliseconds)
RPC_POLL_INTERVAL=2000
# =============================================================================
# SECTION 11: ADMIN API
# =============================================================================
# Secure endpoints for refunds, credit adjustments, and user management.
# Generate a strong random key and keep it secret!
# Required in production. Generate with: openssl rand -hex 32
# ADMIN_API_KEY=your-64-character-hex-key-here
# Public operator and paid-play disclosure controls. Production paid instances fail startup
# until these are explicitly set to truthful, non-placeholder values.
# Independent deployments must identify their own operator. Such Software operates only
# play.wowne.ro and monerogue.app; it does not endorse or accept responsibility for self-hosts.
# OPERATOR_NAME=Responsible operator name
# OPERATOR_CONTACT_URL=mailto:support@example.com
# OPERATOR_CONTACT_LABEL=Support
# LEGAL_POLICY_VERSION=2026-07-22-v1
# TERMS_EFFECTIVE_DATE=2026-07-22
# MINIMUM_AGE=18
# PAID_ACKNOWLEDGEMENT_REQUIRED=true
# Leave unset for third-party/self-hosted deployments. Official templates set one reviewed value:
# OPERATED_PRODUCT_PROFILE=such-play-wow-prestige | such-monerogue-stagenet
# Optional plain-language location restriction notice supplied after legal review:
# RESTRICTED_LOCATIONS_NOTICE=
# Use in requests:
# curl -H "X-Admin-Key: your-key" http://localhost:3000/api/admin/...
# Allowed origins for admin API CORS (comma-separated, only needed for cross-origin)
# Same-origin requests are always allowed. Leave unset if admin panel is on same host.
# ALLOWED_ORIGINS=https://admin.example.com
# Set to 'true' ONLY when running behind a trusted reverse proxy (Nginx/Caddy). When
# true, the real client IP is read from the X-Forwarded-For header for rate limiting and
# req.ip. Leave unset/false for direct exposure, otherwise clients could spoof their IP.
# TRUST_PROXY=true
# TRUST_PROXY_HOPS=1 # NPM directly in front of Node
# Anti-instant-death grace (ms) before a block-timeout can end a game. This is NOT a
# fairness floor: random block timing is the core mechanic. It only prevents the
# degenerate case where a block lands the instant a game starts (before the dungeon
# renders / before the first move is possible). Default 2000 (2s); set 0 to disable.
# GAME_START_GRACE_MS=2000
# =============================================================================
# SECTION 13: ADMIN ALERTS (Resend)
# =============================================================================
# Email alerts for low wallet balance and critical issues.
# Sign up at https://resend.com to get an API key.
# Resend API key
# RESEND_API_KEY=re_xxxxxxxxxxxxx
# Admin email to receive alerts
# ADMIN_EMAIL=admin@example.com
# From address for alert emails (must be verified in Resend)
# Default: alerts@monerogue.app
# ALERT_FROM_EMAIL=noreply@yourdomain.com
# -------------------------
# BALANCE THRESHOLDS
# -------------------------
# Two-tier alert system for wallet balance monitoring:
#
# BALANCE_WARN: Sends email alert when balance drops below this level
# You should top up the wallet soon.
#
# BALANCE_CRITICAL: Sends email AND HALTS NEW GAMES when balance drops below
# Users will see a modal: "Sorry, the house balance is too low..."
# Games resume automatically when balance is restored above this threshold.
#
# All values in atomic units:
# WOW: 1 WOW = 100000000000 (10^11)
# XMR: 1 XMR = 1000000000000 (10^12)
# Warning threshold - sends email alert (default: 0.1 WOW/XMR)
BALANCE_WARN=100000000000
# Critical threshold - halts new games (default: 0.01 WOW/XMR)
BALANCE_CRITICAL=10000000000
# Scan recently expired invoices for transfers that mined after the 30-minute watcher stopped.
# Valid receipts create manual-review records only; they never auto-grant stale entries/products.
LATE_PAYMENT_RECONCILE_HOURS=72
LATE_PAYMENT_RECONCILE_BATCH=100
# Legacy alias (superseded by BALANCE_WARN, kept for backwards compatibility)
# LOW_BALANCE_THRESHOLD=100000000000
# -------------------------
# OTHER ALERT SETTINGS
# -------------------------
# Alert when pending/failed payouts exceed this count
HIGH_PENDING_THRESHOLD=10
# Alert when payout processing or refund requested/processing remains nonterminal this long.
# These alerts never retry a transfer. Default: 900000 (15 minutes).
FINANCIAL_REVIEW_STALE_MS=900000
# Alert cooldown in milliseconds (avoid spam)
# Default: 3600000 (1 hour)
ALERT_COOLDOWN_MS=3600000
# =============================================================================
# SECTION 13b: OPTIONAL MAINNET FINANCIAL-EVENT EXPORT
# =============================================================================
# Confirmed payments and final refunds are snapshotted to a durable outbox. Configure both values
# only for a reviewed MAINNET accounting sink that honors Idempotency-Key. Test-network rows are
# suppressed locally and must never be exported. Keep the bearer token in the protected runtime
# environment; never commit it.
# FINANCIAL_EVENT_SINK_URL=https://ledger.operator.example/v1/events
# FINANCIAL_EVENT_SINK_TOKEN=CHANGE_ME_STRONG_ACCOUNTING_SINK_TOKEN
FINANCIAL_EVENT_ACCOUNT_REF=wowngeon:receipts
# FINANCIAL_EVENT_POLL_MS=60000
# FINANCIAL_EVENT_BATCH_SIZE=20
# FINANCIAL_EVENT_MAX_ATTEMPTS=8
# =============================================================================
# SECTION 14: SMIRK WALLET INTEGRATION
# =============================================================================
# Smirk is a browser extension wallet for Wownero/Monero.
# https://smirk.network
#
# Enable this for Wownero mainnet. Disable for Monero stagenet
# (Smirk doesn't support stagenet addresses).
# Enable Smirk wallet connection button (default: true)
# Set to false for Monero stagenet deployments
SMIRK_ENABLED=true
# =============================================================================
# SECTION 14b: BLOCK EXPLORER
# =============================================================================
# URL prefix for linking transaction hashes to a block explorer.
# Include the trailing slash. TX hash will be appended directly.
# Leave unset to disable clickable TX links in transaction history.
#
# Wownero: https://explore.wowne.ro/tx/
# Monero: https://xmrchain.net/tx/
EXPLORER_TX_URL=https://explore.wowne.ro/tx/
# =============================================================================
# SECTION 15: DEVELOPMENT/DEBUG OPTIONS
# =============================================================================
# These are for testing only. Don't use in production with real money!
# Block source: daemon (real blockchain) | simulated (fake blocks for testing)
# Paid modes always use daemon unless FORCE_SIMULATED_BLOCKS=true
BLOCK_SOURCE=daemon
# Force simulated blocks even in paid mode (DANGEROUS - for testing only!)
# FORCE_SIMULATED_BLOCKS=false
# =============================================================================
# LEGACY OPTIONS (deprecated - use PAYMENT_MODES instead)
# =============================================================================
# GAME_MODE: FREE | PAID_SINGLE | PAID_CREDITS
# Kept for backward compatibility. New setups should use PAYMENT_MODES.
# GAME_MODE=
CRYPTO_TYPE=WOW
# =============================================================================
# Tavern & Multiplayer modes (optional)
# =============================================================================
# Tavern: a walkable social room (chat, avatars, spectator camera).
TAVERN_ENABLED=true
# Server-tick / snapshot broadcast cadence in ms (default 100 = 10 Hz)
# TAVERN_TICK_MS=100
# =============================================================================
# Match mode (multiplayer races)
# =============================================================================
# Enable real-time multiplayer races. Solo/Tavern are unaffected.
MATCH_ENABLED=true
# Max players per match, also bounded by the selected ruleset (built-ins max at 8).
MATCH_MAX_PLAYERS=4
# Server tick interval in ms (default 250 = 4 Hz).
# MATCH_TICK_MS=250
# Server-selected match ruleset. Clients cannot choose this value.
# Built-ins: race, last-alive, score-attack, coop-escape. Default: race.
MATCH_RULESET_ID=race
# Paid matches freeze entrants at H, target H+2, and activate at target+1 by default.
# Production requires explicit safe integers from 2 through 100 for both settings.
MATCH_PAID_ENTROPY_DELAY_BLOCKS=2
MATCH_PAID_ENTROPY_CONFIRMATIONS=2
# Minimum race duration in ms before the next block can end it (default 20s).
# MATCH_MIN_DURATION_MS=20000
# Hard ceiling in ms after which a race is force-ended (default 4m).
# MATCH_HARD_CEILING_MS=240000
# Credits cost for prestige-only races (default 1).
# MATCH_CREDITS_COST=1
# Crypto race house fee percent. Production crypto matches require an explicit value from
# 0 (inclusive) through 100 (exclusive).
MATCH_HOUSE_FEE_PERCENT=5
# Required crypto-race per-player entry fee in atomic units when payout matches are enabled.
MATCH_ENTRY_FEE_ATOMIC=5000000000
# Crypto admission requires all three explicit gates plus a worst-case winner cap. Co-op has no
# winner-takes-pot split contract and is therefore rejected for crypto matches.
MATCH_CRYPTO_RACE_ENABLED=false
MATCH_PAYOUTS_ENABLED=false
MATCH_PAYOUT_MAX=50000000000