docs: record renderer Tavern and production state

This commit is contained in:
jwinterm
2026-07-25 08:37:52 -04:00
parent a9d6f8a1bd
commit a5a210e426
6 changed files with 179 additions and 49 deletions
+8 -2
View File
@@ -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
+26 -16
View File
@@ -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/<instance>/releases/<release-id>`, 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/<instance>/releases/<release-id>`, 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.
+3 -2
View File
@@ -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
+58
View File
@@ -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.
+37 -3
View File
@@ -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`.
+47 -26
View File
@@ -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,