Reference7 min read5 sections

Server-authoritative rooms

docs/TRUSTED_HOST.mdSource updated 2026-10-26
Markdown
On this page 5 sections

The opt-in trusted profile runs the same character simulation on the room server and in the browser. Players still move immediately; the browser replays its unacknowledged inputs against server motor snapshots. Inventory, chest transfers, doors, beacon interactions and gateway travel use the server's position and commit in PostgreSQL before acknowledgement.

This profile supports the three static Darkwind places and bounded floating boxes in Driftwood cove or authored worlds. Moving route platforms, patrols, scripted sliding props and authored behaviors remain refused at startup. The cooperative beta/editor and persistent cooperative profile remain separate supported paths.

#Run locally

Use Node 22.23.4 and PostgreSQL as described in persistent rooms. With the database connection variables set:

bash
DW_PERSISTENT_ROOM=trusted-test-1 npm run dev:trusted -- --rebuild

Open http://localhost:8793/. Use a second browser profile for another traveler. Take the lantern, hand it through the chest, open the garden door, visit the sanctuary and light the beacon. Reload to recover your server-saved traveler.

The room's immutable world pins its authority and engine. Use a fresh room ID when testing the new engine: changing an environment variable does not convert an existing cooperative room. Source edits alone do not replace a retained build. This build requires schema 6. Use a new dedicated database alongside any schema-5 or older rooms; automatic startup deliberately refuses to change a database used by retained older engines. Existing rooms keep their old world and runtime.

For Coolify, use compose.persistent.yaml, a new DW_PERSISTENT_ROOM, and DW_ROOM_AUTHORITY=trusted, plus the database password, room password and exact public origin described in the persistent-room runbook. The Compose file declares both database and engine volumes. This increment has native production-bundle verification; local Docker-host execution remains unqualified.

#Trusted floating worlds

With a schema-6 database and a new room ID, start the cove using:

bash
DW_PERSISTENT_ROOM=trusted-driftwood DW_ROOM_TEMPLATE=driftwood npm run dev:trusted -- --rebuild

On Coolify set DW_ROOM_TEMPLATE=driftwood and DW_ROOM_AUTHORITY=trusted on the persistent room service. For an editor-authored world, Save and Export, then use DW_WORLD_DOCUMENT=/absolute/path/to/document.json instead of the template. An exported document without an explicit authority can be bound to the selected hosting profile when the room is created. An explicitly authored authority must match the selected profile; retained rooms keep their original authority. The source document is validated before binding, and the resulting immutable release includes the chosen authority in its content hash.

Use two browser profiles, walk onto the jetty and press E at Gently push. The server checks its own player position. Restart the room: the timber and travelers recover together. The floating-world acceptance describes the checked latency, persistence and rendering paths.

Each motor snapshot in a floating world includes the complete body collision frame used by that tick. The browser replays pending inputs against that frame; visual interpolation stays independent. Body positions and impulses cannot be changed by local replay. While a physics save is pending, server motors continue against the last committed frame. Bodies advance at the room's bounded checkpoint cadence, so contact corrections remain possible as newer frames arrive.

This is character collision against server-owned floating boxes, with explicit validated pushes. Walking into a box does not apply an authoritative body impulse. Moving-platform riding, coupled character/body forces, vehicles, ragdolls and broader long-session/capacity qualification remain open.

#Authority and transport contract

  • Format 12 adds the world policy; compiler identity authority-world.12 includes it in the content hash. Older formats remain cooperative. The HTTP world, admission content hash and retained engine all identify the same release. A browser cannot select another authority mode.
  • Each admitted actor gets a server-owned simulation and a random input stream. Only the 30 Hz server clock advances it. Frames contain bounded movement axes, yaw, jump, sprint and sequential uint64 IDs. Client durations, poses, checkpoints and arbitrary reward commands are not accepted on the trusted public socket.
  • The input queue allows twelve frames. The host applies at most one per tick, substitutes neutral input when empty, and admits at most eighty messages per second. Catch-up is bounded to five ticks. Overflow closes the offending connection. The browser issues at most twelve unacknowledged frames, then pauses new prediction until credits return. Two seconds without acknowledgement progress enters the existing recovery flow even if presence messages continue. The underlying replay reader still caps imported histories at sixty frames.
  • Motor snapshots include position, velocity, grounding, held-jump state and, for floating worlds, the admitted body collision frame. Acknowledgements trim the input history; the browser replays the suffix through the production physics adapter. Snapshots go only to that actor. Other travelers receive presentation poses through the existing place-interest filter.
  • A transaction carries an input boundary. The host waits for those preceding inputs, briefly locks that actor's motor to capture its authoritative pose, and checks reach/requirements against that pose. Ordinary interaction and checkpoint commits do not hold the motor while SQL waits. Gateway commits keep the source motor locked until their transition is published. The original command remains the idempotency fingerprint. An exact retry from an expired stream can return its committed receipt; an uncommitted request from that stream cannot mutate state.
  • Travel prepares local collision/render resources and checks the server destination before committing. Accepted travel replaces the host simulation/input stream. The browser reconciles the destination motor after the prepared place becomes active.
  • The server saves changed poses every four seconds while a traveler is connected. An abrupt disconnect can lose movement since the last commit; acknowledged inventory and travel survive. This interval is not an end-to-end production recovery guarantee.

The PostgreSQL control API, database credentials and operator upgrade tools are trusted administration surfaces. Keep them private. The public room never accepts a trusted pose override from a client. A return credential controls one traveler and is not an account or anti-account-abuse system.

Current chest contents, item ownership, doors and quest flags remain public shared world state. Self motor/input streams and durable player records are individually addressed. Personal keepsakes use the private projection below. Arbitrary nested-field policies, secret shared inventory, competitive economies, cross-host routing, broader dynamic-world prediction, WAN congestion/long-soak tests and broad hardware qualification remain open.

#Personal keepsakes

New format-13 trusted rooms grant An ember of the old light when a traveler lights or tends the sanctuary beacon while holding the keeper's lantern. Press I to see the amber keepsake in the satchel. Another traveler can earn their own by taking the lantern through the shared chest and tending the beacon. The global beacon flag does not grant everyone a keepsake. Reloading or reconnecting retains the earned keepsake; an existing room must be explicitly upgraded before its world gains this rule.

Reward definitions are public authored data (id, action, name, description). Possession, entity UUID and earned date are private. Format 13/compiler reward-world.13 pins these rules into the release. Only successful, host-verified beacon, dialogue and pickup actions may grant rewards; no client reward command exists. Equipment toggles never grant pickup rewards. The server checks reach and requirements and commits the award, action, receipt and outbox in one fenced transaction. A database primary key enforces one grant per actor/definition even with retries or repeated actions. There are at most 16 definitions per release and 64 retained keepsakes per actor, also subject to a 64 KiB projected-state budget.

Canonical award entities use the component registry: Keepsake is owner-visible, AwardAudit is server-only, and the entire entity is owner-visible. The server projection intersects entity and component policies before encoding. It omits hidden entities, IDs, values and unused schema descriptors. server and local components never cross this boundary. Private fields are separate components; this is not arbitrary nested-field redaction. Queries additionally select only the recipient's rows. The browser accepts only the exact keepsake contract and inserts authored text through text nodes.

Keepsakes cannot be traded, equipped or deposited. Names and descriptions are captured at grant time and survive removal of their definition. Upgrade previews retain awards in their snapshot hash; the backup captures their rows and audit metadata (introduced in schema 5). Database/control operators remain trusted and can inspect private state. A return credential identifies a traveler, not a human account; this is not an abuse-resistant monetary economy.

#Verification

bash
npm run check
node --import tsx --test tests/trusted-motion.test.ts tests/private-replication.test.ts
npm run test:control
npm run test:browser:trusted
npm run test:browser:trusted -- --portals
npm run test:browser:trusted-floating

The motor tests use production simulation, jumping and wall collision, bounded input admission, malformed-state rejection and an exact 150 ms round-trip transport fixture. Database/socket tests exercise forged reach, flooding, forbidden client checkpoints, saved server motion across restart, self-stream isolation and lost-receipt deduplication. The browser command adds 75 ms of delay in each direction at the actual simulation-worker WebSocket boundary and runs the complete two-traveler persistent journey against the trusted profile, including chest handoff, staged travel, reload and simulation-worker recovery. See the acceptance record for the verified source and remaining limitations.

Private reward acceptance also covers six application-crash boundaries, database loss around commit, exact backup restoration, retained metadata after content upgrade, and unauthorized raw socket/session/browser captures. See the keepsake acceptance record and satchel screenshot.

For graceful deployment, final position saves, readiness and failed-process recovery, see host lifecycle.

End of Authoritative rooms