mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-23 00:58:33 +02:00
feat: harden operated production release
This commit is contained in:
@@ -25,13 +25,13 @@ the same `DatabaseManager.runMigrations()` used by application startup: lexical
|
||||
the `schema_migrations` ledger, and one transaction per newly applied file. Finally, it requires the
|
||||
ledger to equal the complete repository migration manifest, in order, with no missing or extra
|
||||
entry. The manifest is discovered from the candidate release and must be contiguous from 001; the
|
||||
current floor is migration 040. A successful current release reports:
|
||||
current floor is migration 042. A successful current release reports:
|
||||
|
||||
```text
|
||||
clone_migration_status=ok
|
||||
database=monerogue_restore_20260721t220048z_28d513b29b36
|
||||
migration_count=40
|
||||
latest_migration=040_paid_match_entropy_precommit.sql
|
||||
migration_count=42
|
||||
latest_migration=042_immutable_financial_event_snapshots.sql
|
||||
```
|
||||
|
||||
## Invocation
|
||||
|
||||
+30
-21
@@ -2,6 +2,21 @@
|
||||
|
||||
This guide covers deploying Wownerogue to a production environment.
|
||||
|
||||
## Operated profiles versus independent deployments
|
||||
|
||||
Such Software (`apps@such.software`) operates only `play.wowne.ro` (Wownero mainnet
|
||||
pay-for-credits leaderboard/prestige, no prize/payout/cash-out and not marketed as gambling) and `monerogue.app` (Monero
|
||||
stagenet **NO REAL VALUE** 2×/3× solo test gambling mechanics, with crypto-match payouts off).
|
||||
Classification of any product under applicable law requires jurisdiction-specific advice.
|
||||
Their reviewed environment templates set `OPERATED_PRODUCT_PROFILE`, so preflight/startup rejects
|
||||
scope drift. Independent MIT deployments must leave that variable unset and identify their actual
|
||||
operator.
|
||||
|
||||
MIT rights are subject to retaining the copyright and permission notice. The software is provided
|
||||
“AS IS”, without warranty, and the code/docs are not legal advice or compliance approval. Each
|
||||
third-party operator is solely responsible for its deployment, legal obligations, funds, players,
|
||||
claims, and support; Such Software neither endorses nor accepts responsibility for it.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
@@ -129,6 +144,10 @@ the read-only historical audit and the rollback-only native validation proof des
|
||||
[`FINANCIAL_CONSTRAINT_VALIDATION.md`](FINANCIAL_CONSTRAINT_VALIDATION.md) for each restored
|
||||
database. The native `VALIDATE CONSTRAINT` gate deliberately refuses live database names.
|
||||
|
||||
If the Wownero mainnet service will export accounting events, review and configure the durable
|
||||
[financial-event outbox](FINANCIAL_EVENT_EXPORT.md) before cutover. Monero stagenet export must
|
||||
remain unset: those no-value test events are marked ignored locally.
|
||||
|
||||
Install the reviewed `wowngeon-db-backup.sh`, `.service`, and `.timer` through the hash-pinned fleet
|
||||
operations change—not from the runtime artifact—and create
|
||||
`/var/backups/wowngeon/daily` as `postgres:postgres` mode `0700`, then enable the timer. Each run
|
||||
@@ -147,28 +166,18 @@ npm run db:create # Recreate schema
|
||||
|
||||
## systemd Service
|
||||
|
||||
Install reviewed application/firewall templates through the hash-pinned fleet operations change;
|
||||
the runtime artifact intentionally has no service units. Wallet-RPC replacement remains subject to
|
||||
the explicit NO-GO gates in [DEPLOY_INSTANCES.md](DEPLOY_INSTANCES.md). Application units read secrets from
|
||||
`/etc/wownerogue/app.env` or `/etc/monerogue/app.env`, not from an immutable release directory.
|
||||
They run `npm run preflight` before every start and use `/var/www/<instance>/current/src` so rollback
|
||||
is an atomic symlink switch.
|
||||
Install reviewed application, wallet, backup, and firewall artifacts only through the hash-pinned
|
||||
fleet operations change; the runtime artifact intentionally has no service units. Application
|
||||
units read secrets from `/etc/wownerogue/app.env` or `/etc/monerogue/app.env` plus the narrow
|
||||
wallet-RPC environment, never from an immutable release directory. They run `npm run preflight`
|
||||
before every start and use `/var/www/<instance>/current/src` so rollback is an atomic symlink
|
||||
switch.
|
||||
|
||||
Enable and start:
|
||||
|
||||
```bash
|
||||
# Install the correct profile outside the release and replace every placeholder.
|
||||
sudo install -d -m 0750 -o root -g wownerogue /etc/wownerogue
|
||||
# Use the separately reviewed source/fleet copy; environment examples are not in the runtime archive.
|
||||
sudo install -m 0640 -o root -g wownerogue <reviewed-source>/src/.env.mainnet.example /etc/wownerogue/app.env
|
||||
sudoedit /etc/wownerogue/app.env
|
||||
|
||||
# Enable and start service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable wownerogue
|
||||
sudo systemctl start wownerogue
|
||||
sudo systemctl status wownerogue
|
||||
```
|
||||
For the two Such Software services, do not replace this boundary with manual `install`, `systemctl`,
|
||||
or symlink commands. Follow the default-closed wallet candidate/promotion, candidate validation,
|
||||
and one-instance activation procedures in [DEPLOY_INSTANCES.md](DEPLOY_INSTANCES.md) and the
|
||||
hash-pinned `~/src/such-fleet/RUNBOOK-wowngeon.md`. Independent MIT operators must design and review
|
||||
their own equivalent control plane.
|
||||
|
||||
The systemd hardening options:
|
||||
- `NoNewPrivileges` - prevents privilege escalation
|
||||
|
||||
+65
-36
@@ -4,9 +4,13 @@ Two independent instances of the same secured codebase, behind Nginx Proxy Manag
|
||||
|
||||
| Instance | Domain | Port | Network | Money | Payouts | Leaderboards |
|
||||
|----------|--------|------|---------|-------|---------|--------------|
|
||||
| **wownerogue** (existing) | play.wowne.ro | 3000 | Wownero **mainnet** | real WOW | **OFF** | Pleb + Hall of Champions |
|
||||
| **monerogue** (new) | monerogue.app | 3001 | Monero **stagenet** | fake XMR | **ON (target; wallet-gated)** | Pleb + Hall of Champions |
|
||||
| **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.
|
||||
@@ -25,15 +29,16 @@ transfer only that blob into a new root-owned mode-0700 candidate inbox. Compare
|
||||
with the independently recorded literal digest; the transferred sidecar is not an independent
|
||||
provenance source.
|
||||
|
||||
There is currently no reviewed fleet extraction/activation helper. Blob-only staging is allowed;
|
||||
manual extraction under `/var/www/<instance>/releases`, dependency installation, clone migration,
|
||||
preflight, service restart, and `current` changes remain blocked until that helper implements the
|
||||
complete ownership/path/hash/rollback contract. Never make a release directory writable by either
|
||||
application service identity.
|
||||
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 the MAINNET instance: free-to-play, NO payouts
|
||||
## 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
|
||||
@@ -41,7 +46,14 @@ examples are intentionally not present in the runtime artifact; provision this f
|
||||
hash-pinned fleet/source change, never from the application release directory.
|
||||
|
||||
```ini
|
||||
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
|
||||
@@ -51,9 +63,10 @@ TRUST_PROXY=true
|
||||
TRUST_PROXY_HOPS=1 # exactly one NPM hop
|
||||
```
|
||||
|
||||
Players still buy credits / pay entry (→ Hall of Champions prestige); free players → Pleb
|
||||
board. This profile sends no winnings, but legal classification still depends on the product,
|
||||
jurisdiction, and access controls; obtain counsel before treating that as a legal conclusion.
|
||||
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:
|
||||
@@ -61,7 +74,7 @@ service while the wallet/preflight and fleet activation gates remain open:
|
||||
```bash
|
||||
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_SINGLE.payoutMultiplier}'
|
||||
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,
|
||||
@@ -73,24 +86,27 @@ not files in the runtime artifact and not approval to install them.
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — MONERO STAGENET wallet boundary: NO-GO
|
||||
## Step 2 — Prove and promote the MONERO STAGENET wallet boundary
|
||||
|
||||
Do **not** install a replacement unit, create wallet identities/directories, copy wallet or key
|
||||
files, capture a password, provision RPC credentials, stop/restart the wallet, or change the live
|
||||
application endpoint as part of this release. The current wallet, unit, and application dependency
|
||||
remain unchanged.
|
||||
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 only retained design is
|
||||
The earlier non-mutating design record is
|
||||
[`README-monero-stagenet-parallel-candidate.md`](../scripts/deploy/README-monero-stagenet-parallel-candidate.md).
|
||||
It currently approves only a disposable synthetic fixture and, after independent review, a fixed
|
||||
read-only inventory preflight. Even its manual candidate service is not approved for installation.
|
||||
Before any host mutation, its complete crash/failure-injection gates, stopped-copy restart proof,
|
||||
offline address match, keyfile/password restore, mnemonic/off-host custody, and isolated unfunded
|
||||
rehearsal must all pass against an immutable reviewed hash.
|
||||
It is not an installation procedure and its sample unit is not a promotion artifact.
|
||||
|
||||
Until a separate wallet promotion is approved, a stagenet application candidate must use an
|
||||
isolated database with payments, payouts, and match money disabled. Paid intake and the payout
|
||||
canary remain blocked; do not bypass application preflight to make them start.
|
||||
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
|
||||
|
||||
@@ -109,8 +125,9 @@ the production host or recursively `chown` `/var/www/monerogue` to the service u
|
||||
|
||||
## Step 4 — The monerogue `.env`
|
||||
|
||||
This is the eventual paid stagenet profile, not permission to activate it while Step 2 is NO-GO.
|
||||
Write `/etc/monerogue/app.env` as `root:monerogue` mode `0640`. Amounts are XMR atomic units
|
||||
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):
|
||||
|
||||
```ini
|
||||
@@ -118,11 +135,17 @@ 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
|
||||
@@ -150,6 +173,7 @@ 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
|
||||
@@ -179,11 +203,15 @@ 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 through the approved secrets workflow only after the wallet gate clears. Never place either
|
||||
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
|
||||
@@ -200,10 +228,10 @@ sudo -u monerogue bash -c 'set -a; . /etc/monerogue/app.env; . /etc/monerogue/wa
|
||||
|
||||
## Step 5 — Install + start the monerogue instance
|
||||
|
||||
The runtime artifact contains no service units. Install or update `monerogue.service` only through
|
||||
the separately reviewed, hash-pinned `~/src/such-fleet` change after the wallet and application
|
||||
gates pass. Do not copy a unit from the application release or start this paid profile while Step 2
|
||||
is NO-GO.
|
||||
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
|
||||
@@ -213,11 +241,12 @@ domains, websocket upgrades, and local readiness probes still work.
|
||||
## Step 6 — Verify end to end
|
||||
|
||||
- `monerogue.app` loads; `play.wowne.ro` still healthy.
|
||||
- After a separate wallet GO and isolated canary: buy credits / pay entry with a stagenet wallet →
|
||||
game starts → win → payout arrives. This check is blocked while Step 2 is NO-GO.
|
||||
- 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: confirm a winning paid game does NOT send a payout (no crypto out).
|
||||
- Mainnet, only after its separate recovery gate and release: confirm purchased-credit play never
|
||||
creates a payout or cash-out path.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Financial-event export
|
||||
|
||||
Migrations 041 and 042 create a durable outbox for confirmed customer payments and final refund
|
||||
states. Migration 042 stores an immutable source snapshot before delivery; the exporter never
|
||||
reconstructs an event from later-mutated payment rows. Payloads contain transaction classification,
|
||||
atomic amount, asset/network, catalog/provider identifiers, and receipt count, but no player ID,
|
||||
wallet address, session token, IP address, email address, or authentication material.
|
||||
|
||||
Delivery is **at least once**. Every request sets `Idempotency-Key` to the immutable financial event
|
||||
ID. The receiving system must durably deduplicate that key before acknowledging with a 2xx response.
|
||||
A timeout or lost acknowledgement can otherwise produce a repeat request. The application marks a
|
||||
row delivered only after a 2xx response; bounded retries use a durable lease and exponential delay,
|
||||
then move an exhausted row to `dead_letter` for operator review.
|
||||
|
||||
The public health response exposes only aggregate exporter state: enabled/running flags, backlog,
|
||||
dead-letter and ignored counts, and cumulative delivery counters. It exposes no sink URL,
|
||||
token, event ID, error text, payload, or source identity. Accounting export is informational and does
|
||||
not independently make gameplay readiness fail, so production monitoring must alert on backlog age,
|
||||
dead letters, and the exporter process logs/metrics needed to detect stale delivery.
|
||||
|
||||
## Configuration contract
|
||||
|
||||
Configure both of these only in the protected Wownero mainnet runtime environment:
|
||||
|
||||
```dotenv
|
||||
FINANCIAL_EVENT_SINK_URL=https://ledger.operator.example/v1/events
|
||||
FINANCIAL_EVENT_SINK_TOKEN=<strong protected bearer token>
|
||||
FINANCIAL_EVENT_ACCOUNT_REF=wowngeon:wow-receipts
|
||||
FINANCIAL_EVENT_POLL_MS=60000
|
||||
FINANCIAL_EVENT_BATCH_SIZE=20
|
||||
FINANCIAL_EVENT_MAX_ATTEMPTS=8
|
||||
```
|
||||
|
||||
Production requires HTTPS, forbids URL-embedded credentials, and requires a strong token. The account
|
||||
reference is a non-PII identifier. URL and token must be set together. Never place the token in Git,
|
||||
Ansible inventory, an invocation, a receipt, a log, or a public health check.
|
||||
|
||||
Monero stagenet deliberately sends nothing to an accounting sink. Its pending, leased, or dead-letter
|
||||
test rows are moved to `ignored` with reason `non_mainnet_network`; startup rejects sink configuration
|
||||
on any non-mainnet network. This keeps **NO REAL VALUE** test activity out of real books.
|
||||
|
||||
## Operations
|
||||
|
||||
Before enabling mainnet export, prove in a disposable environment that the sink:
|
||||
|
||||
1. authenticates the protected bearer token over TLS;
|
||||
2. deduplicates repeated requests by `Idempotency-Key`;
|
||||
3. durably records the event before returning 2xx;
|
||||
4. rejects malformed events without echoing secrets; and
|
||||
5. supports alerting and reconciliation for a deliberately generated dead letter.
|
||||
|
||||
Keep the outbox in database backups. Do not delete or rewrite a dead-letter row to make monitoring
|
||||
green; reconcile it against the receiving ledger, preserve the evidence, and use a separately reviewed
|
||||
repair procedure. An absent sink is permitted, but confirmed mainnet events then remain pending and
|
||||
must be included in the production accounting handoff.
|
||||
@@ -4,6 +4,32 @@ This implementation makes the running server describe its actual free, paid-pres
|
||||
test-network, and PvP switches. It does **not** claim that the product is licensed or lawful in a
|
||||
particular place. Obtain jurisdiction-specific advice before accepting assets with real value.
|
||||
|
||||
## Operated-product boundary
|
||||
|
||||
Such Software (`apps@such.software`) operates only:
|
||||
|
||||
| Profile | Public service | Exact scope |
|
||||
|---|---|---|
|
||||
| `such-play-wow-prestige` | `play.wowne.ro` | Wownero mainnet free play and pay-for-credits leaderboard/prestige. Credits are non-redeemable; there are no prizes, payouts, cash-out, or crypto racing, and the service is not offered/marketed as gambling. Legal classification depends on applicable law. |
|
||||
| `such-monerogue-stagenet` | `monerogue.app` | Monero stagenet only. Single-player 2×/3× test gambling mechanics using test coins with **NO REAL VALUE**. Never mainnet XMR and no crypto-match payouts. |
|
||||
|
||||
The corresponding `.env.mainnet.example` and `.env.stagenet.example` files opt into a fail-closed
|
||||
startup contract with `OPERATED_PRODUCT_PROFILE`. Preflight and normal startup reject any drift in
|
||||
host, operator identity, chain/network, paid modes, multipliers, or payout gates. Independent MIT
|
||||
self-hosts leave this setting unset and configure a truthful operator identity of their own.
|
||||
|
||||
## Open-source and operator disclaimer
|
||||
|
||||
MIT permission to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies is
|
||||
subject to retaining the copyright and permission notice in all copies or substantial portions.
|
||||
The software is provided “AS IS”, without warranty of any kind, as stated in `LICENSE`. The code,
|
||||
documentation, examples, and disclosure copy are not legal advice, a licence, or a compliance
|
||||
approval.
|
||||
|
||||
A third-party or self-hosted operator is solely responsible for its deployment, product design,
|
||||
legal compliance, funds, players, representations, and support. Such Software does not operate,
|
||||
supervise, endorse, or accept responsibility for third-party deployments.
|
||||
|
||||
## Required production settings
|
||||
|
||||
Every production instance with `PAYMENTS_ENABLED=true` fails preflight/startup unless all of these
|
||||
@@ -13,8 +39,8 @@ are explicit and valid:
|
||||
OPERATOR_NAME=Truthful responsible operator name
|
||||
OPERATOR_CONTACT_URL=mailto:support@example.com
|
||||
OPERATOR_CONTACT_LABEL=Support
|
||||
LEGAL_POLICY_VERSION=2026-07-21-v1
|
||||
TERMS_EFFECTIVE_DATE=2026-07-21
|
||||
LEGAL_POLICY_VERSION=2026-07-22-v1
|
||||
TERMS_EFFECTIVE_DATE=2026-07-22
|
||||
MINIMUM_AGE=18
|
||||
PAID_ACKNOWLEDGEMENT_REQUIRED=true
|
||||
```
|
||||
@@ -60,6 +86,10 @@ The acknowledgement is a player statement, not proof of identity, age, capacity,
|
||||
| Crypto PvP on | Paid ticket escrow and configured winner/fee contract; separate from free/prestige PvP |
|
||||
| XMR stagenet/testnet | Valueless test currency; explicit warning never to send mainnet XMR |
|
||||
|
||||
For the operated products, the generic rows narrow further: `play.wowne.ro` exposes purchased
|
||||
credits only, offers no payout/cash-out, and is not marketed as gambling; `monerogue.app` labels its 2×/3× stagenet mechanics as
|
||||
**NO REAL VALUE** and keeps crypto-match payouts disabled.
|
||||
|
||||
## Unresolved launch decisions
|
||||
|
||||
Technical copy and checkboxes do not resolve these. Keep real-value modes gated until the responsible
|
||||
|
||||
@@ -77,14 +77,17 @@ activation boundary must then:
|
||||
4. restore each fresh pre-deploy dump into a separately named clone database, run
|
||||
`npm run db:migrate:clone` under the exact disposable-target contract in
|
||||
[CLONE_MIGRATIONS.md](./CLONE_MIGRATIONS.md), require its exact manifest proof (currently through
|
||||
migration 040), and run the separately reviewed and hash-pinned fleet copy of
|
||||
migration 042), and run the separately reviewed and hash-pinned fleet copy of
|
||||
`financial-audit.sql` there—the SQL is intentionally not inside the runtime artifact;
|
||||
5. run `npm run preflight` from the candidate with each final protected environment. Preflight is
|
||||
intentionally non-mutating and does not replace database, daemon, or wallet health checks.
|
||||
|
||||
The clone runner is deliberately non-activating; there is still no reviewed activation script. Do
|
||||
not point `current` at a candidate until the clone proof and the complete
|
||||
drain/audit/symlink/health/rollback sequence in `~/src/such-fleet/RUNBOOK-wowngeon.md` are satisfied.
|
||||
The clone runner is deliberately non-activating. Such Software production activation must use the
|
||||
separately reviewed, default-closed `wowngeon_release_activate` role in `~/src/such-fleet`; do not
|
||||
substitute manual extraction, dependency installation, service commands, or a hand-written
|
||||
`current` switch. The role still refuses activation until the matching clone-validation receipt,
|
||||
wallet-promotion receipt, drain/audit gates, literal hashes, and rollback target satisfy the
|
||||
complete contract in `~/src/such-fleet/RUNBOOK-wowngeon.md`.
|
||||
|
||||
## Source and operations boundary
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
# Wallet keyfile recovery proof
|
||||
|
||||
**Status: source and synthetic tests only. This helper has not been installed on or run against
|
||||
`deb`, and this document does not authorize a host mutation or a production ceremony.**
|
||||
|
||||
This runbook covers one deliberately narrow question: can an audited encrypted wallet snapshot and
|
||||
the password already exposed in the matching legacy wallet-RPC process open offline and reproduce
|
||||
the exact public address currently returned by that process? The fixed helper is
|
||||
`scripts/deploy/wallet-keyfile-recovery-proof.py`.
|
||||
|
||||
The answer is emitted as exactly one status line:
|
||||
|
||||
```text
|
||||
result=passed
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```text
|
||||
result=refused
|
||||
```
|
||||
|
||||
There is intentionally no diagnostic mode. Never add shell tracing, a debugger, `strace`, command
|
||||
line capture, wallet-RPC response logging, or exception rendering to investigate a refusal. Those
|
||||
would defeat the secret-handling boundary this proof is meant to preserve.
|
||||
|
||||
## What a pass means
|
||||
|
||||
A pass establishes all of the following for the selected profile at that moment:
|
||||
|
||||
- the expected legacy systemd unit has a stable MainPID, executable, UID/GID, cgroup, listener,
|
||||
network flag, RPC port, and exact `--wallet-file` argument;
|
||||
- its already-exposed inline wallet password could be copied from the NUL-delimited process command
|
||||
line without rendering it;
|
||||
- both fixed, root-only pre-production snapshot files were real, single-link regular files with the
|
||||
audited metadata and were copied byte-for-byte into a newly created private `/run` directory;
|
||||
- the matching wallet-RPC executable opened that copied pair offline on the fixed loopback proof
|
||||
port as the exact legacy UID/GID, with an empty supplementary-group list, using a password file and
|
||||
an ephemeral Digest credential;
|
||||
- the proof endpoint rejected an unauthenticated request with HTTP 401, accepted an authenticated
|
||||
`get_address` request with HTTP 200, returned an address shaped for the expected network, and that
|
||||
address exactly equalled the current live address;
|
||||
- that restored wallet signed one fixed, non-secret proof message and its own wallet-RPC verified the
|
||||
signature as a spend-key signature (or, for the legacy response shape, verified the default
|
||||
spend-key signature), so a watch-only keyfile cannot pass; and
|
||||
- the candidate stopped, its listener disappeared, and only the exact ephemeral files and directory
|
||||
were securely cleaned up.
|
||||
|
||||
The helper does not stop, start, restart, reload, reconfigure, or write to a live unit or live wallet.
|
||||
The proof candidate is offline; it cannot refresh, submit transactions, or contact a daemon.
|
||||
|
||||
## What a pass does not mean
|
||||
|
||||
This is **not seed custody** and it is **not an off-host disaster-recovery test**. In particular, a
|
||||
pass does not establish:
|
||||
|
||||
- that a mnemonic or seed has ever been recorded, is readable, is complete, or is held by the right
|
||||
custodians;
|
||||
- that the encrypted keyfile, its password, or a seed exists independently off this host—the password
|
||||
used here comes only from the already-running process, not from an independent custody source;
|
||||
- that Restic media, repository credentials, retention, or an actual off-host restore path works;
|
||||
- that a restore can rescan chain history, discover the expected balance, or spend funds;
|
||||
- that daemon, database, application, DNS, proxy, Nebula, or fleet recovery works; or
|
||||
- that payouts, wagering, mainnet money movement, or a production release is authorized.
|
||||
|
||||
Do not query, display, export, hash, or automate retrieval of a mnemonic to fill this gap. Seed
|
||||
custody requires a separate human-controlled inventory and an off-host restore drill with an agreed
|
||||
handling ceremony. Until that evidence exists, record seed custody and off-host recovery as
|
||||
**unproven**, even if this helper passes.
|
||||
|
||||
## Fixed audited profiles
|
||||
|
||||
No path, unit, binary, port, user, or RPC method is accepted from the caller. The only accepted
|
||||
profile names are `monero-stagenet` and `wownero-mainnet`.
|
||||
|
||||
| Profile | Fixed legacy identity | Fixed snapshots | Private proof target |
|
||||
| --- | --- | --- | --- |
|
||||
| `monero-stagenet` | `monero-wallet-rpc.service`, `jw:jw`, `/usr/bin/monero-wallet-rpc`, wallet `/home/jw/Programs/monero-x86_64-linux-gnu-v0.18.4.4/test`, `127.0.0.1:38083`, `--stagenet` | `/var/backups/wowngeon/wallets/monero-stagenet-preprod-20260721T1140Z.wallet` and `/var/backups/wowngeon/wallets/monero-stagenet-preprod-20260721T1140Z.keys` | outer `/run/wowngeon-wallet-keyfile-proof-monero-stagenet`, inner `candidate`, `127.0.0.1:39084` |
|
||||
| `wownero-mainnet` | `wownero-wallet-rpc.service`, `jw:jw`, `/usr/bin/wownero-wallet-rpc`, wallet `/home/jw/Programs/wow-11.3.0/game`, audited legacy listener `0.0.0.0:34570` | `/var/backups/wowngeon/wallets/wownero-mainnet-preprod-20260721T1140Z.wallet` and `/var/backups/wowngeon/wallets/wownero-mainnet-preprod-20260721T1140Z.keys` | outer `/run/wowngeon-wallet-keyfile-proof-wownero-mainnet`, inner `candidate`, `127.0.0.1:39570` |
|
||||
|
||||
The live equality observations are unauthenticated because the legacy sources are. Monero is
|
||||
loopback-only; Wownero's wildcard listener is an insecure legacy source contained by the host
|
||||
firewall, not a state this proof endorses. `current_live_address` makes one read-only public-address
|
||||
observation solely for equality; a match is **not** an RPC-hardening or network-security pass. The
|
||||
helper neither changes nor resolves the wildcard/unauthenticated service. Its proof candidate always
|
||||
binds loopback and requires an ephemeral Digest credential.
|
||||
|
||||
Each fixed proof path is a root-owned outer directory, `root:jw` mode `0710`, which prevents the
|
||||
candidate identity from listing or writing its parent. Root builds the inner `candidate` subtree and
|
||||
files before publishing only that subtree as `jw:jw` mode `0700` with `0600` files. Immediately before
|
||||
exec, the child clears supplementary groups, sets all real/effective/saved GIDs and UIDs to `jw:jw`,
|
||||
clears retained capabilities, then reapplies no-dump, no-new-privileges, and zero-core limits. The
|
||||
parent verifies empty inheritable/permitted/effective/ambient capability sets. Wallet-RPC never runs
|
||||
as root.
|
||||
|
||||
The audited snapshot directory is `/var/backups/wowngeon/wallets`, `root:root` mode `0700`; each
|
||||
snapshot is `root:root` mode `0600` with one link. Its parent is `/var/backups/wowngeon`,
|
||||
`root:postgres` mode `0710`. Any deviation refuses the proof.
|
||||
|
||||
## Review and synthetic verification
|
||||
|
||||
Before any future host ceremony, review the helper and run the disposable fixture locally:
|
||||
|
||||
```bash
|
||||
python3 -m py_compile scripts/deploy/wallet-keyfile-recovery-proof.py \
|
||||
scripts/deploy/wallet-keyfile-recovery-fixture-test.py
|
||||
python3 scripts/deploy/wallet-keyfile-recovery-fixture-test.py
|
||||
```
|
||||
|
||||
The fixture uses only generated temporary files and synthetic credentials. It covers exact argument
|
||||
selection and wiping, indirect or duplicate password refusal, network mismatch, link and metadata
|
||||
failures, source mutation during copy, byte equality, cleanup containment, Digest authentication
|
||||
shape, candidate config shape, privilege dropping, fixed-message spend signing/verification,
|
||||
watch-only refusal, and proof-port collision. It never opens a real wallet.
|
||||
|
||||
Review must also confirm that the executable still contains only read-only `systemctl is-active` and
|
||||
`systemctl show` calls; only the `get_address`, `sign`, and `verify` wallet RPC methods; no
|
||||
caller-controlled paths, ports, or messages; and only the two status strings as runtime output. The
|
||||
address and signature are never output or retained as ceremony evidence.
|
||||
|
||||
## Future authorized ceremony
|
||||
|
||||
Run Monero stagenet first. Wownero mainnet remains an offline keyfile-open proof only and must not be
|
||||
treated as approval to enable payouts or submit a transaction.
|
||||
|
||||
Only after a reviewed revision is explicitly approved for installation should an operator install
|
||||
that exact file to a root-owned, non-writable path such as
|
||||
`/usr/local/sbin/wowngeon-wallet-keyfile-recovery-proof` with owner `root:root` and mode `0700`.
|
||||
Do not run it from a mutable checkout. Record the reviewed source revision, but do not record wallet
|
||||
bytes, addresses, passwords, command lines, Digest material, wallet-derived hashes, or RPC bodies.
|
||||
|
||||
Before each profile:
|
||||
|
||||
1. Confirm no debugger, tracing wrapper, shell xtrace, terminal recorder, or broad process collector
|
||||
will observe the root helper.
|
||||
2. Confirm the expected proof port is not reserved for another operation and the exact fixed `/run`
|
||||
proof directory does not exist.
|
||||
3. Confirm the legacy unit is expected to remain online and unchanged for the short proof window.
|
||||
4. Invoke the installed helper as root with exactly one fixed profile argument and capture only its
|
||||
one-line status.
|
||||
|
||||
The future invocations are:
|
||||
|
||||
```bash
|
||||
sudo /usr/local/sbin/wowngeon-wallet-keyfile-recovery-proof monero-stagenet
|
||||
sudo /usr/local/sbin/wowngeon-wallet-keyfile-recovery-proof wownero-mainnet
|
||||
```
|
||||
|
||||
These commands are examples for a later authorized ceremony, not evidence they have been run.
|
||||
|
||||
After each invocation, verify only that its fixed proof port is closed and its fixed proof directory
|
||||
is absent. A pass is invalid if cleanup did not complete; the helper reports refusal in that case.
|
||||
|
||||
## Refusal or interrupted execution
|
||||
|
||||
`result=refused` is intentionally non-diagnostic. Do not weaken the helper or print an exception to
|
||||
learn why. Review the fixed-path metadata, expected unit state, listener ownership, port availability,
|
||||
and synthetic suite through non-secret checks, then arrange a new reviewed ceremony.
|
||||
|
||||
If a fixed proof directory remains after interruption, **do not blindly remove it**. Treat it as
|
||||
potentially containing wallet material. First establish through metadata-only checks that no proof
|
||||
wallet-RPC process or proof-port listener remains, keep the incident off shared logs, and have an
|
||||
authorized operator review cleanup of that exact path. Never use a recursive command against `/run`
|
||||
or a variable/glob target.
|
||||
|
||||
The next independent backup milestone is a human-approved seed-custody inventory followed by a
|
||||
restore from genuinely off-host media into an isolated environment. That is the evidence needed to
|
||||
answer “do we have recoverable wallet seeds?”; this helper deliberately cannot answer it.
|
||||
Reference in New Issue
Block a user