Guide4 min read3 sections

Published Dusk engine builds

docs/ENGINE_PINNING.mdSource updated 2026-10-28
Markdown
On this page 3 sections

These are retained builds of the Dusk engine. The rename does not change runtime ABI 1, the engine-dist layout, /engines/<hash>/ routes or existing pins; older retained builds keep their original bytes and names.

Newly published invitations retain the exact built player (including workers/assets) and a bundled multiplayer relay with its JavaScript dependencies. Updating the Docker image changes the engine for new publications. Existing invitations keep their original engine and room state.

#Test it

  1. Start the atelier with npm run dev:editor, save a draft, and publish an invitation. Open it in two browsers and change something shared, such as a door.
  2. Keep the invitation URL. Restart the editor after updating code, or redeploy the Docker image with the same persistent /data volume.
  3. Open that invitation again. The address resolves to /engines/<original-build-hash>/?playtest=<snapshot-hash>. Both players retain their original engine and shared state. Exported player reports include pinnedEngine.
  4. Change and save the draft, then publish it. This new snapshot uses the new build. Republishing the exact same snapshot preserves its original binding.

Development builds capture their published engine when npm run dev:editor starts. Restart that command after engine code changes before publishing a new engine. The draft editor and development room continue to use current code. The ordinary playtest invitation redirects to its retained player even when opened through Vite.

#Deployment and persistence

npm run build now also runs build:engine, producing engine-dist/engine.json, client/, and runtime/. The relay entry and dependencies are bundled separately from the current gateway. Docker copies this artifact into /app/engine-dist; DW_ENGINE_BUNDLE selects it. Production with the editor enabled requires a valid bundle. No additional port or volume is needed.

On the first publication for a build, the registry validates the manifest, copies and verifies every file, fsyncs it, and atomically installs it under DW_DATA_DIR/engine-builds/<hash>. A checksummed binding in engine-pins/<snapshot-hash>.json is made durable before publication is acknowledged. Include both directories in backups alongside authoring/ and playtests/. Starting an unused deployment consumes no retained-build slot. Archive/Bring back does not remove the engine or binding.

The gateway dispatches published rooms to the retained relay module through runtime ABI 1. The retained module reads its own document format and owns its room checkpoint and sessions. The gateway supplies deployment origins, passphrase and room storage. Admission and WebSocket upgrades require the same engine identity as the invitation. The root invitation, client asset route, document read and room opening fail if the necessary retained bundle is unavailable or damaged; they never silently substitute the current engine.

#Explicit bounds

  • Existing snapshots without a binding are legacy, using the current engine. Their historical executable cannot be recovered retroactively. Explicitly republishing an unchanged legacy snapshot adopts the current engine for future visits. Merely reading the catalog does not adopt one. If a legacy room is already open, restart and republish before joining it; adoption is blocked while that legacy executable is resident.
  • Retained engines require the exact Node version recorded when built, currently the Docker-pinned Node 22.23.4. Cross-Node runtime hosting/migration is not implemented; incompatible invitations fail explicitly. Preserve the matching runtime image when backing up or changing Node versions.
  • At most 16 engine bundles, 512 files per bundle, 32 MiB per file, and 128 MiB per bundle. The registry never automatically deletes a pinned build. Retention/migration tooling remains future work. Failed publication can leave an installed bundle or orphan binding, but never an acknowledged invitation without its pin.
  • Runtime ABI changes, binary/network schema migration, cross-host placement, and hostile-code isolation are not covered. Bundles come from trusted deployments; creators cannot upload executable modules.
  • Four resident published rooms and the existing player/session budgets remain unchanged. Browser/GPU compatibility is still required by the captured client.

The automated two-generation test builds real player assets and two independently bundled copies of the real relay with distinguishable test response hooks. It verifies exact player bytes, execution of the original relay after redeployment, two-player shared state and resumed session tokens, new-build admission, mismatched clients, legacy adoption, failed pin publication and damaged/missing/incompatible artifacts. Browser acceptance exercises published navigation, workers, two-player movement, host handoff, reload, archive and draft restoration through the pinned route.

End of Retained engine builds