diff --git a/README.md b/README.md index a458b86..3ec9db8 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,10 @@ third-party deployment. - **Persistent chat** with 30-day history - **Transaction history** - view payment and payout records - **Multiple game modes**: Free play, per-game payments, or credit bundles -- **Social Tavern** with chat, live solo spectating, and in-place race queues +- **Anonymous social Tavern** (no account required) with chat, live solo spectating, and in-place + race queues +- **Shared render kit** with Tiled, ASCII, Iso, and locally served 3D techniques across the dungeon + and Tavern - **Real-time multiplayer races**: free, prestige-credit, or crypto winner-take-pot races every block - Configurable difficulty with house edge tuning - Automatic wallet RPC integration for payments and payouts @@ -137,7 +140,7 @@ Click **"📜 History"** to view your payment and payout records. Shows: ### Session Persistence -Sessions use an anonymous token stored in localStorage: +Sessions, including anonymous Tavern entry, use an anonymous token stored in localStorage: - ✅ Persists across refreshes, browser closes, server restarts - ⚠️ Lost when clearing cookies/localStorage, using incognito mode, or switching browsers/devices @@ -264,6 +267,9 @@ See [docs/DEPLOY.md](docs/DEPLOY.md) for detailed production deployment instruct - Database permissions and security hardening - Multi-instance deployment (running Wownero and Monero on the same server) +The dated [`play.wowne.ro` production record](docs/PRODUCTION_STATE_2026-07-25.md) distinguishes the +live schema-neutral renderer/Tavern overlay from the still-gated full mainline migration cutover. + --- ## Operations diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index 343ca94..0805880 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -2,6 +2,10 @@ This guide covers deploying Wownerogue to a production environment. +For evidence about the release currently serving `play.wowne.ro`, see the dated +[production-state record](PRODUCTION_STATE_2026-07-25.md). That record is not a substitute for this +runbook or authorization for another activation. + ## Operated profiles versus independent deployments Such Software (`apps@such.software`) operates only `play.wowne.ro` (Wownero mainnet @@ -44,6 +48,8 @@ claims, and support; Such Software neither endorses nor accepts responsibility f - [ ] Set up database backups (see [LOGS_AND_BACKUP.md](./LOGS_AND_BACKUP.md)) - [ ] Configure log rotation - [ ] Run database migrations +- [ ] If 3D is offered, provision the gitignored runtime GLBs and verify library/model requests + stay same-origin --- @@ -74,10 +80,12 @@ root-owned mode-0700 candidate inbox and compares its remote SHA-256 with the in recorded literal digest. Follow the fleet Wowngeon runbook; the role is a one-shot custody boundary, not deployment. -The current fleet repository deliberately has no executable extraction/activation helper yet. -Until that helper exists, the safe host boundary is blob-only staging: do not manually -extract beneath `releases/`, install dependencies there, change `current`, restart an application, -or run migrations merely because the blob has arrived. +The executable extraction/activation boundary lives only in the separately reviewed fleet +repository as the default-closed `wowngeon_release_activate` role. This application checkout cannot +attest which fleet revision is installed or authorize its execution. Follow the hash-pinned fleet +runbook and literal receipt contract; do not manually extract beneath `releases/`, install +dependencies there, change `current`, restart an application, or run migrations merely because a +blob has arrived. --- @@ -95,12 +103,13 @@ node --version npm --version ``` -The eventual reviewed extraction/activation helper installs exactly the locked production graph in its private, -writable staging directory with `npm ci --omit=dev --ignore-scripts --no-audit --no-fund`, verifies -it with `npm ls --omit=dev`, and seals the finished release root-owned and non-writable before it can -be selected. Never make a release tree writable by `wownerogue`. Online advisory lookup is a -separate explicitly authorized source-review operation; never run `npm audit fix` in staging or on -a live release. +The reviewed fleet extraction/activation role installs exactly the locked production graph in its +private, writable staging directory with +`npm ci --omit=dev --ignore-scripts --no-audit --no-fund`, verifies it with +`npm ls --omit=dev`, and seals the finished release root-owned and non-writable before it can be +selected. Never make a release tree writable by `wownerogue`. Online advisory lookup is a separate +explicitly authorized source-review operation; never run `npm audit fix` in staging or on a live +release. --- @@ -114,7 +123,7 @@ sudo chmod 0640 /etc/wownerogue/app.env ``` Do not use recursive ownership changes on `/var/www/wownerogue`; they can hand the service process -write access to immutable code or the rollback selector. The reviewed extraction/activation helper must verify +write access to immutable code or the rollback selector. The reviewed fleet role must verify every individual release's ownership and modes before activation. **Database ownership** - the application currently runs its own migrations at startup, so its @@ -228,11 +237,12 @@ location /socket.io/ { ## Updating the Deployment Run the full suite from the clean source commit, then build and verify the runtime artifact using -[RELEASE_ARTIFACT.md](./RELEASE_ARTIFACT.md). Today, use the fleet blob-staging playbook and stop at -the independently hash-verified root-owned candidate blob because the reviewed extraction/activation -helper does not yet exist. When that helper implements the documented contract, it—not an application service identity or a -manual shell session—will build a new immutable `/var/www//releases/`, install -the locked graph, run the clone/preflight gates, seal ownership, and atomically select `current`. +[RELEASE_ARTIFACT.md](./RELEASE_ARTIFACT.md). Use the fleet blob-staging playbook, then proceed only +through the reviewed, hash-pinned `wowngeon_release_activate` role after its clone, wallet, +accounting, drain, predecessor, and receipt gates pass. That role—not an application service +identity or a manual shell session—builds a new immutable +`/var/www//releases/`, installs the locked graph, verifies the clone/preflight +evidence, seals ownership, and atomically selects `current`. Never run `git pull`, `npm install`, or `npm audit fix` inside the active release. Keep the previous release and database backup until the new version has passed public health, WebSocket, payment-intake, and (stagenet only) payout smoke tests. diff --git a/docs/DEPLOY_INSTANCES.md b/docs/DEPLOY_INSTANCES.md index 5988b84..8e33d9b 100644 --- a/docs/DEPLOY_INSTANCES.md +++ b/docs/DEPLOY_INSTANCES.md @@ -107,8 +107,9 @@ Neither wallet role reads, exports, or proves a mnemonic. A passing keyfile/pass 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. +Wownero mainnet wallet. Wownero wallet promotion and the corresponding full mainline application +activation remain blocked pending that separate review. The dated +[schema-neutral production state](PRODUCTION_STATE_2026-07-25.md) does not clear this gate. ## Step 3 — Create the monerogue identity, immutable layout, and DB diff --git a/docs/PRODUCTION_STATE_2026-07-25.md b/docs/PRODUCTION_STATE_2026-07-25.md new file mode 100644 index 0000000..af0b7d8 --- /dev/null +++ b/docs/PRODUCTION_STATE_2026-07-25.md @@ -0,0 +1,58 @@ +# `play.wowne.ro` production state — 2026-07-25 + +> This is a dated evidence record, not a deployment procedure or authorization to repeat an +> exceptional release path. The normal gates in [DEPLOY.md](DEPLOY.md), +> [DEPLOY_INSTANCES.md](DEPLOY_INSTANCES.md), and [RELEASE_ARTIFACT.md](RELEASE_ARTIFACT.md) remain +> controlling. + +## Active schema-neutral release + +`play.wowne.ro` is serving the immutable release +`20260725T121348Z-28d513-overlay-a9d6f8a`. It was prepared from exact predecessor +`20260721T143902Z-28d513b29b36` and carries feature source marker +`a9d6f8a1bd1af7e77f7872629e325452ed5ae46a`. + +The overlay changes only browser/static renderer and Tavern behavior plus the same-origin +Three.js runtime: + +- single-player Tiled, ASCII, Iso, 3D, pack, and camera controls return keyboard focus to the game; +- Tavern entry is explicitly anonymous, waits for session readiness, exposes pending/error states, + and remains retryable; +- Three.js `0.160.0` is loaded from versioned same-origin vendor routes; +- production CDN renderer execution remains off; +- the existing Wownero mainnet purchased-credit profile remains unchanged, with every payout path + off. + +The live database migration ledger still ends at +`038_economic_identity_immutability.sql`. The overlay neither applied nor marked migrations +039–043 and did not adopt their payment/accounting behavior. + +## Verification evidence + +- Complete source suite: 123 suites passed, 979 tests passed. +- Local and public readiness probes passed with PostgreSQL, Wownero daemon, and wallet dependencies + ready. +- External HTTPS and Socket.IO/WebSocket smoke tests passed through the public domain. +- A fresh browser anonymously entered the Tavern and rendered local Three.js plus the generated GLB + model from same-origin resources. +- Single-player mode/pack clicks retained exact `#game-display` focus; a live 3D run rendered WebGL, + attached the animated model, and removed its temporary fallback. +- Activation rollback was exercised by failed smoke gates before the corrected release passed. + +Operator-host evidence: + +| Record | SHA-256 | +|---|---| +| Release manifest | `8ffd5077d906c7307094eda9f1c2332fd7d1edf1bf3b337807dc8c8e5a54b780` | +| Activation receipt | `be9ee6c8657222e11464a6e4aff6aebede067e54e83d5d70aa1cf2ac7fa5b437` | + +## Full mainline cutover remains STOP-SHIP + +This production state does **not** mean the complete mainline runtime artifact has been activated. +Mainline contains migrations through 043; the existing Wownerogue database remains five migrations +behind. A full cutover must not proceed until the reviewed fleet runbook's clone-validation, +daemon/wallet, accounting, drain, activation-receipt, and rollback gates all pass for the exact +candidate and predecessor. + +The schema-neutral overlay is not precedent for bypassing those gates. Future activation or +rollback must use the reviewed fleet control plane with literal targets, hashes, and receipts. diff --git a/docs/RENDER_PACKS.md b/docs/RENDER_PACKS.md index e6c4954..471fff5 100644 --- a/docs/RENDER_PACKS.md +++ b/docs/RENDER_PACKS.md @@ -17,8 +17,39 @@ art can be added as products. packs interchangeable across both. Entitlement: packs are gated by the operator-owned cosmetic catalog (`cosmetic_catalog` table, -`src/multiplayer/entitlements.js`) — free / grant / lifetime-spend / subscription-tier. `?unlock=1` -sets a sticky local QA bypass. See `MONETIZATION.md`. +`src/multiplayer/entitlements.js`) — free / grant / lifetime-spend / subscription-tier. See +`MONETIZATION.md`. + +For browser QA, `?unlock=1` sets a sticky local bypass for render-mode and pack display gates; +`?unlock=0` clears it. This affects only that browser's `localStorage`, grants no server +entitlement, and is not an operator access-control mechanism. + +## Gameplay focus contract + +Single-player keyboard movement is deliberately accepted only while `#game-display` owns browser +focus, so typing in chat or another form cannot move the player. Choosing Tiled, ASCII, Iso, 3D, a +pack, or a camera control immediately returns focus to that gameplay area without scrolling. + +## Local 3D runtime + +Three.js `0.160.0` is an exact production dependency. The server advertises the local version in +`/runtime-config.js` and exposes only the browser build and required addons beneath: + +```text +/vendor/three/0.160.0/three.module.min.js +/vendor/three/0.160.0/addons/loaders/GLTFLoader.js +/vendor/three/0.160.0/addons/utils/SkeletonUtils.js +``` + +The modules are imported lazily when 3D is selected. Local delivery is always enabled in the +application runtime and takes precedence over external sources. `RENDERER_CDN_ENABLED=true` makes +the jsDelivr renderer source path eligible only in a runtime where local delivery is disabled. All +checked-in environment profiles keep external renderer execution off, including production, so +Three.js remains same-origin under CSP. + +Three.js library delivery is separate from generated model delivery. Runtime GLBs under +`html/assets/generated/3d/` are gitignored and must be provisioned independently; the renderer +keeps a low-poly fallback when a model cannot load and removes it once the animated GLB is attached. ## The FX layer (`fxLayer.js`, `RK.fx`) @@ -44,5 +75,8 @@ the moment the generator emits their chars (`L`/`P`/`^`) — pending the generat | `zoomControl.js` | `RK.attachZoom(host)` — wheel/pinch zoom (0.4–4.0), dblclick reset, pixelated scaling. | | `catSprites.js` | Animated tavern cat (Pet Cats Pack idle strips). | | `packRegistry.js`| Multi-pack-per-projection registry: active-pack selection persisted per projection, entitlement-gated, graceful fallback. Node-testable. | +| `threeRenderer.js` | Same-origin Three.js scene, fog-aware entity retention, animated GLB cloning, fitted model scale, camera follow, and complete renderer/resource cleanup. | +| `spGameRenderer.js` | Single-player render bridge, player-centred camera, local 3D lazy-load, and explicit gameplay-focus restoration. | -Tests: `test/renderPackResolver.test.js`, `test/packRegistry_ultra.test.js`. +Tests: `test/renderPackResolver.test.js`, `test/packRegistry_ultra.test.js`, +`test/rendererCdnPolicy.test.js`, and `test/pvpUiPolish.test.js`. diff --git a/docs/TAVERN_AND_MULTIPLAYER.md b/docs/TAVERN_AND_MULTIPLAYER.md index 48932f3..eafad22 100644 --- a/docs/TAVERN_AND_MULTIPLAYER.md +++ b/docs/TAVERN_AND_MULTIPLAYER.md @@ -24,6 +24,19 @@ transport-agnostic makes it unit-testable in isolation and reusable as a standal choose an avatar, move around a shared map, and chat. A spectator camera lets them watch live single-player or multiplayer games from inside the room. +### Entering anonymously + +The Tavern requires no account or login. On connection, the browser registers the client and waits +for the normal anonymous session handshake (`session_token`, `session_resumed`, or the associated +identity response) before enabling **Enter tavern**. The token is stored as `wownerogue_token` in +that browser's `localStorage` and is reused after refresh or reconnect. + +The join form accepts its submit action, including Enter from the optional name field. Its button +shows `Connecting…`, `Preparing…`, or `Entering…` while the corresponding step is pending. A +disconnect or connection error resets readiness and retries automatically; a join with no server +response becomes a visible retryable error after ten seconds. Cosmetic availability is still +normalized and enforced by server entitlement policy. + **Multiplayer Match** — a shared dungeon Room using the operator-selected ruleset. Players may race to escape, fight to remain last alive, compete for score, or cooperate. Block-bounded rulesets end on the first advancing header after their active-play duration floor; every match also has a hard @@ -151,16 +164,18 @@ into a `Scene` (a tile grid + entities), and any renderer draws it. This lives i `html/js/render/` and is used by the Tavern, multiplayer client, spectators, and the single-player render bridge. -Render tiers: +Selectable render techniques: - **ASCII** — monospace glyph grid (canvas). Always-available fallback; accessible. - **Tiled** — coloured tiles + entity sprites (canvas 2D). The default. Real tilesets/atlases slot in here later. -- **Fancy** — WebGL via PixiJS: a blurred bloom on entities, flickering warm lights along the - bar, drifting dust motes, a vignette, and smooth movement — all programmatic (no art assets). - **Iso** — canvas isometric projection using Kenney's Isometric Miniature Dungeon runtime PNGs. -- **3D** — Three.js projection. It loads generated GLB avatars with `idle` / `run` / `jump` - clips when available and falls back to lightweight low-poly avatars during asset iteration. -- Planned: **Fancy ASCII** — shader-lit, animated glyphs. +- **3D** — Three.js projection, loaded lazily from the pinned same-origin `three@0.160.0` runtime. + It attaches generated GLB avatars with `idle` / `run` / `jump` clips when available and keeps a + lightweight low-poly fallback only when the model cannot load. + +`fancyRenderer.js` remains an internal PixiJS prototype, not a registered selectable mode. +Production top-down styling comes from interchangeable packs plus the shared canvas FX layer. +Planned: **Fancy ASCII** — shader-lit, animated glyphs. Render mode is a per-user choice (persisted) with an operator default. Modes can be marked **premium** — a cosmetic entitlement intended to be unlocked with credits (the same Operator @@ -169,10 +184,9 @@ not a hard boundary; premium-only *assets* (special tilesets, shaders) are the r stronger gating is wanted. The render kit provides the engine and programmatic tiles/effects — polished pixel-art tilesets, sprite sheets, and shader effects are a separate content effort. -**CSP note:** PixiJS compiles shaders with `eval`, which the app's strict CSP blocks. The Fancy -tier therefore loads `@pixi/unsafe-eval` (it precompiles shaders without eval) immediately after -`pixi.js`, so no `'unsafe-eval'` is added to the CSP. Any page using the Fancy renderer must -include both scripts, in that order. +**CSP note:** If the internal PixiJS prototype is invoked, it loads `@pixi/unsafe-eval` +immediately after `pixi.js` to precompile shaders without adding `'unsafe-eval'` to CSP. It also +requires the server's explicit external-renderer policy; no current mode button invokes it. ## Asset delivery & performance @@ -181,17 +195,20 @@ Rules for keeping the tree lean and the client fast: - **Heavy source artifacts never go in git.** 3D models and generation intermediates (`*.glb`, `*.fbx`, `*.blend`, …) are produced by the sprite pipeline (`~/src/docs/animated-sprite-pipeline.md`) and are gitignored. Only the *outputs* the game loads are considered for shipping. -- **Generated / premium assets are hosted, not committed.** AI-generated premium skins live under - `html/assets/generated/` (gitignored) and are delivered from an external asset host / CDN by URL. - The base tier's Kenney CC0 tiles are small and stay in-repo. +- **Generated / premium assets are provisioned, not committed.** Premium skins and runtime GLBs + live under `html/assets/generated/` (gitignored). They may be provisioned into the same-origin + runtime path or delivered by an explicitly reviewed asset host and CSP policy; they are not + present in the Git release artifact. The base tier's small Kenney CC0 tiles stay in-repo. - **Lazy-load by mode / entitlement.** Nothing heavy loads until it's actually needed: - - PixiJS (+ `@pixi/unsafe-eval`) loads only when the **Fancy** mode is first selected — ASCII and - Tiled need no WebGL library. + - ASCII and Tiled need no WebGL library. The unregistered Fancy prototype requests PixiJS plus + `@pixi/unsafe-eval` only if called directly. - A premium skin's sprite sheet is fetched only when that skin is selected and the player is entitled to it. - - Three.js loads only when the **3D** mode is selected. Generated GLBs are emitted by - `scripts/build_kenney_3d_characters.py` from the local Kenney FBX sources into - `html/assets/generated/3d/` (gitignored). + - The exact production Three.js dependency is served beneath `/vendor/three/0.160.0/` and + imported only when **3D** is selected. Local delivery takes precedence; external renderer + execution remains off unless `RENDERER_CDN_ENABLED=true`. + - Generated GLBs are emitted by `scripts/build_kenney_3d_characters.py` from local Kenney FBX + sources into `html/assets/generated/3d/` (gitignored). - **Optimize what ships.** Sprite sheets are delivered as **WebP** (alpha), right-sized to display resolution, content-hashed with a long cache TTL. (Example: the demo walk sheet is 633 KB as PNG, 65 KB as WebP — ~10×.) @@ -213,8 +230,9 @@ Multiplayer: - **M3** — Free, credit-prestige, and gated crypto-race economies wired in. (Done) Rendering: -- **R0** — Shared render kit: scene model + ASCII / Tiled / Fancy (PixiJS) renderers + mode - switch, wired to the Tavern. Premium-mode gating scaffolded. (Done) +- **R0** — Shared render kit: scene model + selectable ASCII / Tiled / Iso / 3D techniques, + internal Fancy prototype, and a mode switch wired to the Tavern. Premium-mode gating + scaffolded. (Done) - **R1** — Wire the main game to the render kit behind the existing display layer. (Done) - **R2** — Real tilesets / sprite sheets and the Fancy-ASCII tier. - **R3** — Wire premium render modes to credit entitlements (Operator Policy). (Done) @@ -233,8 +251,9 @@ All milestones are additive and config-gated; the single-player path is unchange and Socket.IO room broadcasts. Wired into `SocketHandlers` (`tavern_join` / `tavern_move` / `tavern_leave`, plus disconnect cleanup and shutdown). Inert unless `TAVERN_ENABLED=true`. -`html/tavern.html` — the browser client: connect, choose a name/avatar, enter the room, walk with -keyboard or on-screen controls, chat, watch solo/multiplayer games, or join a match. +`html/tavern.html` — the browser client: establish/resume an anonymous session, choose an optional +name/avatar, enter the room, walk with keyboard or on-screen controls, chat, watch +solo/multiplayer games, or join a match. No account login is required. `src/network/chat/` — the `ChatProvider` seam. `ChatProvider` (interface) + `SocketChatProvider` (default: Socket.IO delivery + Postgres history). Both the global chat (`ChatHandler`) and tavern @@ -242,14 +261,16 @@ chat deliver through it, so the backend can be swapped (e.g. a Nostr channel) wi callers. Tavern chat is room-scoped and ephemeral. `html/js/render/` — the render kit: `sceneModel.js` (renderer-agnostic adapters), ASCII, tiled, -Fancy (PixiJS), isometric, and Three.js renderers, plus `renderModes.js` (registry, factory with -graceful fallback, entitlements, persistence). Clients expose a mode toolbar where applicable; -premium modes are marked and gated by `RK.entitlements`. +isometric, and Three.js renderers plus the internal Fancy (PixiJS) prototype, and +`renderModes.js` (registry, factory with graceful fallback, entitlements, persistence). Clients +expose a mode toolbar where applicable; premium modes are marked and gated by `RK.entitlements`. Tests: `test/tavernRoom.test.js` (engine), `test/tavernManager.test.js` (manager incl. chat), `test/chatProvider.test.js`, `test/renderScene.test.js` (scene adapter), `test/entitlements.test.js`, `test/identityService.test.js`, and `test/productGrants.test.js`. -End-to-end join/move/leave and chat over a real socket verified out-of-band. +`test/pvpUiPolish.test.js` covers the anonymous readiness/join states, and +`test/rendererCdnPolicy.test.js` covers the same-origin Three.js runtime policy. End-to-end +join/move/leave, local 3D, and chat over a real socket are verified out-of-band. Payment/product entitlement pieces: - `src/migrations/020_user_appearance_and_pack_entitlements.sql` — persisted appearance,