12 KiB
Two-instance deployment runbook
Two independent instances of the same secured codebase, behind Nginx Proxy Manager (NPM):
| Instance | Domain | Port | Network | Money | Payouts | Leaderboards |
|---|---|---|---|---|---|---|
| wownerogue (existing) | play.wowne.ro | 3000 | Wownero mainnet | purchased service credits | OFF, always | Pleb + paid prestige |
| monerogue (new) | monerogue.app | 3001 | Monero stagenet only | test coins — NO REAL VALUE | ON for 2×/3× solo only (target; wallet-gated) | Pleb + Hall of Champions |
Such Software (apps@such.software) operates only these two domains and these exact scopes.
play.wowne.ro is pay-for-credits prestige with no prize/payout/cash-out and is not marketed as
gambling; legal classification depends on applicable law. monerogue.app is a conspicuous
no-real-value test gambling surface; never send mainnet XMR and never enable crypto-match payouts.
Stagenet payouts are a technical test surface, not authorization to offer real-money wagering.
ALLOW_MAINNET_PAYOUTS=true is only an engineering interlock; obtain jurisdiction-specific legal,
age/access-control, responsible-play, and operational review before attaching redeemable value.
NPM already forwards both domains to these ports + handles TLS — no reverse-proxy work here.
⚠️ This is an operator runbook, not a record of actions already executed. Deploy only a reviewed, tagged release after the full test suite and both preflights pass.
Step 0 — Stage an immutable reviewed release
Build and verify one runtime artifact using RELEASE_ARTIFACT.md, then transfer only that blob into a new root-owned mode-0700 candidate inbox. Compare the host's SHA-256 with the independently recorded literal digest; the transferred sidecar is not an independent provenance source.
Such Software production uses only the separate, default-closed fleet playbooks for inert blob
staging, two-dump clone validation, wallet candidate preparation/promotion, and one-instance release
activation. Each operation is bound to literal hashes and exact receipts; none infers a release,
dump, wallet, predecessor, or instance. Do not substitute manual extraction under
/var/www/<instance>/releases, dependency installation, service commands, or a hand-written
current switch. Never make a release directory writable by either application service identity.
Step 1 — Reconfigure MAINNET: free + purchased-credit prestige, NO payouts
Install the reviewed source profile src/.env.mainnet.example as /etc/wownerogue/app.env (mode
0640, root:wownerogue), replace every placeholder, and review these money gates. Environment
examples are intentionally not present in the runtime artifact; provision this file through the
hash-pinned fleet/source change, never from the application release directory.
OPERATED_PRODUCT_PROFILE=such-play-wow-prestige
OPERATOR_NAME=Such Software
OPERATOR_CONTACT_URL=mailto:apps@such.software
OPERATOR_CONTACT_LABEL=apps@such.software
FREE_PLAY_ENABLED=true # offer free play as a choice (Pleb board)
PAYMENT_MODES=credits # only purchased-credit prestige is operated here
DIRECT_PAYMENT_ENABLED=false # per-run payment is outside the operated scope
CREDITS_ENABLED=true
PAYOUTS_ENABLED=false # no crypto leaves the house wallet
DIRECT_PAYOUTS_ENABLED=false
CREDITS_PAYOUTS_ENABLED=false
DIRECT_REQUIRES_ADDRESS=false # no payout = no payout address needed
CREDITS_REQUIRES_ADDRESS=false
TRUST_PROXY=true
TRUST_PROXY_HOPS=1 # exactly one NPM hop
Players buy non-redeemable credits for leaderboard/prestige play; free players use the Pleb board. This operated product offers no winnings, payouts, or cash-out and is not offered or marketed as gambling. Classification depends on applicable law; this documentation is not legal advice or a jurisdiction-specific compliance approval.
The following are post-activation verification commands, not authorization to restart the live service while the wallet/preflight and fleet activation gates remain open:
sudo systemctl restart wownerogue.service
journalctl -u wownerogue.service -n 50 --no-pager # verify: "Payment system: ENABLED", payouts disabled
curl -s localhost:3000/api/game-modes | jq '{freePlayEnabled, PAID_SINGLE, PAID_CREDITS}'
MAINNET WALLET NO-GO: this application release does not authorize changing, copying, stopping, or replacing the Wownero wallet, its key file, password, unit, or RPC authentication. Keep payouts off. A later wallet change requires independently recoverable keyfile/password and mnemonic custody, an off-host recovery proof, an isolated rehearsal, exact address/balance verification, and a separately reviewed maintenance/rollback plan. Root-level wallet templates are operations source, not files in the runtime artifact and not approval to install them.
Step 2 — Prove and promote the MONERO STAGENET wallet boundary
Wallet work is a separate custody operation from application release work. Use only the reviewed
wowngeon_wallet_candidate and wowngeon_wallet_promote roles from a clean, hash-pinned fleet
revision, select exactly monerogue, and preserve every generic receipt. Candidate preparation
must prove the fixed encrypted stagenet snapshot reopens the exact live address, has a spend key,
and enforces the application's strong Digest credentials before it publishes inert dedicated-user
material. Promotion must then stop only the selected legacy wallet, install only the reviewed
loopback-only unit, require unauthenticated 401 plus authenticated address/network/balance probes,
and automatically roll back to the exact legacy unit on a failed gate.
The earlier non-mutating design record is
README-monero-stagenet-parallel-candidate.md.
It is not an installation procedure and its sample unit is not a promotion artifact.
Neither wallet role reads, exports, or proves a mnemonic. A passing keyfile/password proof is not independent seed custody or a full off-host recovery drill. Record those separately as unproven until a human-controlled custody inventory and isolated off-host restore establish them. This gap does not authorize weakening a proof, printing a secret, or applying either role to the funded Wownero mainnet wallet. Wownero wallet promotion and the corresponding application activation remain blocked pending that separate review.
Step 3 — Create the monerogue identity, immutable layout, and DB
sudo useradd --system --home /var/www/monerogue --no-create-home --shell /usr/sbin/nologin monerogue
sudo install -d -m 0750 -o root -g monerogue /var/www/monerogue
sudo install -d -m 0750 -o root -g monerogue /var/www/monerogue/releases
sudo install -d -m 0750 -o root -g monerogue /etc/monerogue
sudo -u postgres createuser --pwprompt monerogue
sudo -u postgres createdb --owner=monerogue --template=template0 monerogue
The service identity owns its database role, not its application files. Do not clone source onto
the production host or recursively chown /var/www/monerogue to the service user.
Step 4 — The monerogue .env
This is the exact no-real-value stagenet test profile. Apply its non-secret policy keys through the
fleet runtime-policy role; do not overwrite or log the protected environment. Keep
/etc/monerogue/app.env as root:monerogue mode 0640. Amounts are XMR atomic units
(1 XMR = 1e12):
NODE_ENV=production
PORT=3001
TRUST_PROXY=true
TRUST_PROXY_HOPS=1
OPERATED_PRODUCT_PROFILE=such-monerogue-stagenet
OPERATOR_NAME=Such Software
OPERATOR_CONTACT_URL=mailto:apps@such.software
OPERATOR_CONTACT_LABEL=apps@such.software
CRYPTO_TYPE=XMR
MONERO_NETWORK=stagenet
HOSTED_BY=https://monerogue.app
DIFFICULTY_PRESET=casino
# NO REAL VALUE: test coins only. Never send mainnet XMR.
# --- payments + free play ---
PAYMENTS_ENABLED=true
PAYMENT_MODES=direct,credits
FREE_PLAY_ENABLED=true # both boards available
# --- direct (pay per game) WITH payouts (fake stagenet money) ---
DIRECT_PAYMENT_ENABLED=true
DIRECT_GAME_PRICE=10000000000 # 0.01 XMR
DIRECT_REQUIRES_ADDRESS=true
DIRECT_PAYOUTS_ENABLED=true
DIRECT_PAYOUT_ESCAPE=2.0
DIRECT_PAYOUT_TREASURE=3.0
# --- credits WITH payouts ---
CREDITS_ENABLED=true
CREDITS_PER_GAME=1
CREDITS_REQUIRES_ADDRESS=true
CREDITS_PAYOUTS_ENABLED=true
CREDITS_PAYOUT_BASE=10000000000
CREDITS_PAYOUT_ESCAPE=2.0
CREDITS_PAYOUT_TREASURE=3.0
CREDITS_PACKAGES=[{"id":"small","credits":10,"price":"90000000000","bonus":0},{"id":"medium","credits":20,"price":"170000000000","bonus":2}]
ALLOW_MIXED_MODE=true
PREFER_CREDITS_FIRST=true
# --- payouts --- (verify the unlocked stagenet bankroll before every rollout)
PAYOUTS_ENABLED=true
ALLOW_MAINNET_PAYOUTS=false
PAYOUT_MIN_AMOUNT=1000000000 # 0.001 XMR
PAYOUT_MAX_PER_GAME=50000000000 # 0.05 XMR safety cap (real max is 3x*0.01 = 0.03)
PAYOUT_BATCH_INTERVAL=300
PAYOUT_MAX_RETRIES=3
BALANCE_WARN=100000000000 # warn below 0.10 XMR unlocked
BALANCE_CRITICAL=50000000000 # stop accepting new payout liability below 0.05 XMR
# Tip: lower DIRECT_GAME_PRICE/CREDITS_PAYOUT_BASE to 0.001 XMR (1000000000) for many more
# demo plays per the small bankroll; refill from a stagenet faucet as needed.
# --- wallet RPC (monero stagenet) ---
PRIMARY_WALLET_ENDPOINT=http://127.0.0.1:38083
# --- daemon RPC (monerod stagenet) ---
PRIMARY_RPC_ENDPOINT=http://127.0.0.1:38081
FALLBACK_RPC_ENDPOINT=http://127.0.0.1:38081
RPC_POLL_INTERVAL=2000
# --- database ---
DB_HOST=localhost
DB_PORT=5432
DB_NAME=monerogue
DB_USER=monerogue
DB_PASSWORD=CHANGE_ME
# --- admin ---
ADMIN_API_KEY=GENERATE_WITH_openssl_rand_hex_32
# --- smirk: disable for monero stagenet (Smirk doesn't support stagenet) ---
SMIRK_ENABLED=false
# --- PvP financial boundary: free/prestige rulesets may evolve, crypto payouts may not ---
MATCH_CRYPTO_RACE_ENABLED=false
MATCH_PAYOUTS_ENABLED=false
The application unit separately requires /etc/monerogue/wallet-rpc.env, owned
root:monerogue mode 0640, containing only WALLET_RPC_USER and WALLET_RPC_PASSWORD. Provision
it only through the reviewed wallet-candidate workflow. Never place either
password in a command argument, unit ExecStart, shell history, or this runbook.
Production startup proves that wallet RPC actually enforces Digest authentication: an intentional
unauthenticated, read-only probe must receive a usable 401 Digest challenge before the
authenticated identity probe is accepted. Merely setting application credentials is insufficient;
a wallet RPC that still accepts the unauthenticated request makes preflight/readiness fail closed.
sudo chmod 0640 /etc/monerogue/app.env
sudo chown root:monerogue /etc/monerogue/app.env
cd /var/www/monerogue/releases/<reviewed-release-id>/src
sudo -u monerogue bash -c 'set -a; . /etc/monerogue/app.env; . /etc/monerogue/wallet-rpc.env; set +a; npm run preflight'
Step 5 — Install + start the monerogue instance
The runtime artifact contains no service units. Promote the wallet and activate monerogue only
through the separately reviewed, hash-pinned ~/src/such-fleet roles after the recovery,
clone-validation, protected-environment, wallet, drain, and financial-audit gates pass. Do not copy
a unit from the application release.
Install the hash-pinned fleet copies of wowngeon-firewall.sh and
wowngeon-firewall.service, then set the fixed proxy source as NPM_SOURCE_IPV4=<address> in
/etc/wowngeon/firewall.env. Verify direct access to ports 3000/3001 is rejected while both
domains, websocket upgrades, and local readiness probes still work.
Step 6 — Verify end to end
monerogue.apploads;play.wowne.rostill healthy.- After wallet promotion and the separately approved bounded canary: buy credits / pay entry with a stagenet wallet → game starts → qualifying solo outcome → 2×/3× test-coin payout arrives.
- After the corresponding gates clear, both boards populate: free game → Pleb, paid game → Hall of Champions, on each instance.
- Mainnet, only after its separate recovery gate and release: confirm purchased-credit play never creates a payout or cash-out path.
Rollback
- Stop only the affected instance, atomically repoint
/var/www/<instance>/currentto the previous immutable release, and restart it. Keep the pre-deploy database backup for an operator-reviewed restore if a data rollback is required. - Never overwrite either legacy dirty checkout; preserve it until the new release has passed its soak.