Guide59 min read31 sections

Dusk SDK and The Meridian

Define a game, build a portable release, and learn from The Meridian.

docs/GAME_SDK.mdSource updated 2026-10-28
Markdown
On this page 31 sections

#Imported/sloping navigation and editor rebake (F53)

The editor sidebar now includes NPC navigation…. Open an SDK world with a registered NPC (for example npm run dev:navigation), choose the character and bounds, then Rebake paths. The map previews the connected routes and their height range. Preview these paths → Save retains the bake; saved Undo/Redo uses the ordinary authoring journal. Publish a new playtest to run the rebaked NPC. Existing combat-room live migration restrictions remain explicit.

bakeWorldNavigation(world, entity, settings, bake) is exported by the SDK. It reads admitted static GLB collision plus transformed primitive collision, clips the walkable triangles to the selected bounds and builds centroid/shared-edge routes. Paths retain full Y coordinates. Slope defaults to 45 degrees (1–60), step height to 0.3 m (0–0.6). Radius support and head clearance prune unusable nodes. Dynamic doors remain runtime blockers; vertical static collision retains height-aware path/perception bounds.

The version-2 mesh receipt is rederived from actual admitted collision at full compile and streamed-place preparation. Geometry/model changes require a rebake. Legacy version-1 flat bakes keep their old behavior. Limits are 8192 input collision triangles, 128 clipped floor triangles and 256 connected path nodes; use a simplified collision proxy or smaller bounds for dense models. The set-navigation command binds the resulting definition to a registered actor; its normal world validation, source preconditions and durable inverse apply.

Focused smoke covers imported heightfield slopes, NPC movement through changing heights, source compilation and saved rebake/undo. Type/boundary/wire checks pass. General avoidance, moving support surfaces, dense/degenerate geometry, route presentation polish and broader testing remain for the qualification pass. No new rendered screenshot was captured for this source/runtime increment.

The Dusk SDK is the public source API of the Dusk engine: packages/sdk/index.ts, version 0.1.0. Its serialized API value remains darkwind-game-sdk.1 for compatibility with existing game releases; see Dusk compatibility. Darkwind is the reference game built with Dusk. The independent cooperative puzzle at games/cooperative-puzzle/game.ts imports only that entry point. The package boundary check enforces this restriction, including type and dynamic imports. This is the first SDK delivery, not completion of all roadmap-11 game modules.

#Play and edit

Terminal
npm ci
npm run dev:meridian

Open http://localhost:5188/ in two browsers. The command also prints the private atelier URL. It uses ports 5188/8798 and .data/meridian.1; existing Darkwind rooms remain separate. If this instance is already running, the command prints its links. Stop its terminal before restarting after source updates.

Walk behind the amber sunstone and press E. Follow it and push four times into its square. A friend can then move the blue moonstone the same way. Leave the tracks clear: a traveler blocks a push. Wake the golden globe, walk around its pedestal and enter the star terrace. The notes stand near the entrance explains the sequence. Shared stone positions and discovery flags survive room restart; each browser keeps its own journey. This is a cooperative puzzle, not a trusted competitive economy. It can also be completed by one player.

Use the printed atelier link to edit the game, Save, and Publish an invitation. Published worlds use the existing pinned-release workflow. Editing a draft does not replace the standalone source room; use a published invitation or the editor's shared development room for your edited version.

The player shell now derives independent-world headings from world content and uses separate save/return-token keys per world/release. Standard controls, traveler appearance, audio and the engine HUD remain shared. Custom HUD/plugin installation and richer gameplay modules are still required work.

#Game presentation adapters

The reusable Dusk player hosts every game. A game's name, welcome and journal text, objectives, controls, traveler and sound come from a presentation adapter, defined in apps/player/presentation.ts. The player chooses the adapter in one trusted registry, apps/player/installed-games.ts. It selects by the admitted world ID. A display name, story content or URL parameter never selects an adapter. Adapters are trusted source installed with the player build. They are never read from a world file, downloaded or evaluated.

Field Supplies
title, story.tagline, story.welcome, story.chapter Identity and welcome screen
story.objective, story.journal, story.speaker, satchel and interaction text Game interface copy
controls, gamepad Action map and controller bindings
traveler Model artifact and rig options. This is structured-clone data passed to the render worker, including after device recovery and graphics revisions.
sound Clip generator and per-frame emitter graph. Omit it for silence.
saveNamespace 'legacy' keeps a game's original unscoped default saves

Darkwind's adapter is games/darkwind/presentation.ts. Other games, including The Meridian and data-only portable releases, use the neutral Dusk presentation: their world ID as the title, "Made with Dusk", place names as objectives, Dusk's shared default controls (packages/input/defaults.ts) and no sound. This installation explicitly chooses Darkwind's approved Vita traveler, with its lantern attachment, as the starter traveler for those games. To install a dedicated adapter for another game, add it to the registry beside Darkwind's. npm run check enforces that no other player module imports games/.

#Build a portable release

A trusted source module exports game=defineGame({api:SDK_API,world}). Use PlaceBuilder for geometry and authoredEntityId(namespace,key) for stable source identities. Keep identity keys when changing visible labels. The SDK exposes world/interaction/material types, sky presets, versioned component and behavior definitions, action maps, and normal authoring transactions. Definitions do not install arbitrary code in a hosted room. Operator-installed runtime extensions and content-only restricted behaviors retain their existing boundaries.

Terminal
npm run game:build -- games/cooperative-puzzle/game.ts /tmp/meridian-release.json
npm run sdk:reference

Build writes a new file exclusively; it never overwrites an existing release. The package contains the SDK API, checked world document, compiler identity and world hash. The generated reference reports the actual behavior hooks and structured authoring schema. readGameRelease verifies the document and compiles against the declared identity. Unsupported SDKs, changed content and mismatched compilers reject explicitly. Keep the corresponding engine pin; rebuild from source deliberately when upgrading. No automatic migration of live player state is implied by rebuilding a game file.

A normal built relay can serve the data-only package through DW_GAME_FILE:

Terminal
DW_GAME_FILE=/tmp/meridian-release.json \
DW_DATA_DIR=.data/my-independent-room \
DW_ALLOWED_ORIGINS=http://localhost:8787 \
node server-dist/main.js

Build the client/server first with npm run build. The source module is trusted operator code executed only by the build CLI. The hosting loader reads bounded JSON and never imports or executes that source. Do not set both DW_GAME_FILE and DW_PLAYTEST.

For Coolify, use a separate application on codex/engine-foundations, Docker Compose build pack, file /compose.meridian.yaml, service meridian, internal port 8787, and an HTTPS domain. Set DW_ALLOWED_ORIGINS to that exact origin; optionally set DW_ROOM_PASSWORD and DW_EDITOR_TOKEN. The Docker build creates the example release through DW_GAME_SOURCE; Compose creates the independent meridian-v1-room persistent volume. This change does not deploy anything.

#Delivery evidence and review later

Two focused native checks pass reproducible compilation, stable identities, portable file loading, SDK/compiler/content rejection, public-import boundaries, normal authoring edits, two-player progression, stale pushes and shared-state validation. The software-WebGPU browser journey passes two real players pushing all eight steps, shared unlock, terrace travel/reload, editor Check/Preview/Save and publication of revision 1 (38.2 seconds). The courtyard and terrace were visually reviewed; see the courtyard. Type/import, client and relay builds pass. The compiled production host serves the portable release and healthy content; the Compose configuration validates. Tests: game-sdk.test.ts and meridian-browser.test.ts; the browser check uses DW_MERIDIAN_CLIENT_DIR and PLAYWRIGHT_EXECUTABLE_PATH.

The first browser run completed play and publication but expected the wrong status wording; the corrected check waits for “Playtest ready”. Deferred: Docker/Coolify execution, two physical machines, mobile/gamepad qualification, long-session and restart fault combinations, SDK package distribution/declarations, generated API reference completeness, general native extension installation and the full roadmap-11 gameplay modules. Review independent-game audio, HUD copy and art in the final presentation pass. The puzzle is an ordered sequence, not simultaneous occupancy: reversing the sunstone after completing the moonstone does not clear the moonstone completion flag. Revisit that design before expanding the puzzle. Current characters remain unapproved by the user.

#Trusted resources and abilities: The Lantern Trial

Terminal
npm run dev:trial

Open http://localhost:5189/?persistent=lantern-trial in two browser profiles. Node 22 and PostgreSQL's initdb/pg_ctl must be on PATH. The launcher builds the current client/engine, creates its own socket-only PostgreSQL under .data/lantern-trial.1/postgres, and uses the normal retained persistent runtime. It does not use the operator's database connection. Stop with Ctrl+C; the data, traveler credentials and retained engine remain. Subsequent starts use the room's pinned engine, as other persistent rooms do. Rebuilding alone does not upgrade an existing room. Do not delete this directory to update a room you want to retain.

At the amber plinth on the left, press E, wait 1.2 seconds, then press E again. A spark costs 30 focus and 30 vitality. Finish at the plinth within ten seconds of readiness; arriving late starts a new cast and pays its cost. The central plinth releases a pending channel without refunding focus. The blue spring on the right restores vitality and focus. Spend one spark at the amber seal, then have either traveler spend another at the tide seal. Raise the lantern at the far end; each traveler can claim their own keepsake in I · Satchel. Resources and channel/cooldown state are private; seal flags and their glowing appearance are shared. The resource HUD shows the server's saved values.

If vitality reaches zero, actions are disabled except rekindling at the entrance. You can still walk back there, or use Esc → Make yourself comfortable → Return to the starting point. This is a recovery-walk policy, not a combat movement freeze or a death animation. Rekindling resets that traveler's resources and timers; shared discoveries remain.

games/lantern-trial/game.ts uses only the SDK. Format 54 adds world.gameplay (resources-abilities.1): up to eight bounded resources, a health resource, and 32 abilities bound to normal beacon interactions. Casts declare minimums, costs, effects, windup and cooldown. Cancel and respawn are explicit ability kinds. initialGameplay, readGameplay, and useAbility are public pure contracts; the trusted host supplies the time and verified pose, applies the same reach/requirement checks as normal interactions, and commits resources, shared flags, reward and receipt atomically. No client-authored health, damage or time is accepted. Start/finish is explicit, with reach checked at both actions; moving away between them does not automatically cancel. Travel/return clears a channel. Cooldowns use database wall-clock milliseconds and elapse while disconnected. Retries of an acknowledged operation return its existing receipt without paying or granting twice. Failed transactions leave the previous resources intact.

The cooperative relay explicitly rejects gameplay worlds; use trusted hosting. For a separately configured persistent/Coolify instance, export its normal world document and set DW_WORLD_DOCUMENT to the mounted file:

Terminal
node --import tsx tools/cli/trial-world.ts /tmp/lantern-trial-world.json

Keep the existing persistent deployment/database-volume instructions. Control schema 10 adds a nullable gameplay column to saved travelers; existing backup snapshots include it automatically. Startup protects occupied older-schema rooms: use a dedicated database for this new profile until a coordinated upgrade is qualified. Gameplay rule changes during a room upgrade explicitly reject until an appropriate resource migration exists. The local trial does not perform a production deployment or enable runtime plugin installation.

Focused delivery record: SDK compilation/portable read, windup/cost/cooldown, cancellation, expiry, damage/downed/respawn and bounded-state checks pass. The PostgreSQL check passes forged reach/authority rejection, exact retries, independent private resources, two-player shared seals, reward/reconnect and rollback. The real two-browser journey passes channel completion, spring recovery, both seals, shared finish and the reward after reload. The resource HUD and arrival view were visually reviewed; trial arrival. Type/import and client build checks pass, and the one-command retained launcher serves a healthy trusted room and admits the browser. Tests: gameplay.test.ts and gameplay-browser.test.ts (DW_GAMEPLAY_CLIENT_DIR, PLAYWRIGHT_EXECUTABLE_PATH).

Review later / still unfinished: targeting other entities, hit/damage events, combat animation and death presentation, NPC navigation/perception/spawning, general native extension installation and custom HUDs remain implementation work. Restricted creator packages are now supported below. This trial exercises resource effects, not a complete NPC/PvP combat system. Gameplay authoring currently uses SDK source; general editor controls and resource migration remain open. Qualify clock changes, multi-tab/restart/fault combinations, backup restoration with gameplay, physical devices, accessibility, localization, mobile/gamepad and hosted Docker operation in the final pass. Revisit interaction labels, channel feedback, balance, audio and court/character art then. Characters still need the user's final approval. Do not claim these deferred checks passed.

#Shared combat targets: The Sentinel Garden

Terminal
npm run dev:sentinel

Open http://localhost:5190/?persistent=sentinel-garden in two browser profiles. This uses the same local PostgreSQL/retained-engine launcher as the trial, with a separate .data/sentinel-garden.1 directory. The original trial remains available. Walk around the central channel-release plinth toward the brass guardian. Within reach, press E, wait 0.7 seconds, then press E again to finish a lantern bolt. Wait another half second before starting the next bolt. Each bolt costs 20 focus and deals 30 damage against the guardian's shared 120 vitality. A surviving guardian returns 18 damage to the attacker. Four completed bolts defeat it; split the work with a friend and recover at the spring on the right as needed.

While the guardian rests, visit the ember plinth on the left to keep a personal reward. Every traveler can claim theirs; already earned rewards remain in the satchel. The guardian returns after 20 seconds and has at most three lives in this room. Its material and both players' target meters reflect the committed state. The final defeat is permanent for that room. Resources, target vitality, spawn count, deadlines and rewards survive reconnect; timers use database wall time. A restart after a deadline performs the one pending respawn without simulating extra encounters or increasing the authored spawn limit.

Format 55 adds optional gameplay.targets and ability hit bindings. A target is a stable authored entity with bounded health, retaliation, respawn interval, maximum spawn count and defeat condition. An attack must anchor to that same static entity, so its normal host-verified reach follows the authored transform. The client chooses an authored interaction, never a damage amount or hit result. Only a completed cast changes target health; a concurrent defeat rejects the later hit without spending additional resources. A successful nonlethal hit does not grant rewards bound to that attack. Target state and damage to the attacker commit with the usual operation receipt and shared state. The room's single queue and lease fence spawn transactions; the existing outbox records each committed spawn. Spawn polling does no database work when no respawn is pending.

The SDK exposes CombatTarget, TargetHit, CombatState, initialCombat, readCombat, hitTarget and respawnTargets. The example remains SDK-only and imports the original trial's environment from its own game package. General room upgrades with combat targets explicitly reject pending a target-state migration; do not reset retained rooms merely to pick up new code. For a new separately configured persistent host, export a document with:

Terminal
node --import tsx tools/cli/trial-world.ts /tmp/sentinel-world.json --sentinel

The downed recovery path also now permits trusted movement checkpoints. A player walking back to the entrance can save normally; forbidden combat/actions remain blocked. The prior check rejected that checkpoint and could fail the room's periodic save. This demonstrated blocker is fixed with a focused database check.

Focused delivery record: two native checks pass portable compilation, bounded damage, defeat, finite spawn generations, host reach checks, exact hit retries, shared target persistence, private retaliation, downed checkpoint/return and lease-fenced respawn. The two-browser journey passes both players' hits, shared defeat, the immediate reward panel, reload and automatic respawn (42.5 seconds). The first browser route walked into a plinth and was corrected. The next run exposed a stale satchel after opening before the reward receipt; the panel now refreshes when committed keepsakes change. Arrival and defeated appearance were visually reviewed; see the garden. Type/import, client build, standalone document export and launcher syntax checks pass. Tests: combat.test.ts and combat-browser.test.ts (DW_COMBAT_CLIENT_DIR, PLAYWRIGHT_EXECUTABLE_PATH). The new launcher option has not had a separate retained-process/Coolify qualification run; it uses the trial's existing launcher.

Review later / unfinished: this is a stationary, interaction-driven guardian, not navigation AI or PvP. NPC pathfinding/perception and ongoing attack behavior remain the next implementation slice. Line-of-sight occlusion, moving targets, attack/death animation, particles/sound, targeting controls, regeneration design, spawn migration and richer encounter logic remain open. Qualify host loss at hit and spawn commit, long sessions, clock changes, backup restore, multiple places, large target populations and physical devices in the final pass. The target meters currently list this small room's authored targets; broader interest/UI budgets need review before scaling encounters. Final character/environment art and user approval are still required.

#NPC navigation and perception: The Watch Garden

Terminal
npm run dev:watch

Open http://localhost:5191/?persistent=watch-garden in two browser profiles. The separate retained room/database lives in .data/watch-garden.1. The guardian starts behind the central wall. Walk to the right or left of cover: it notices visible travelers, follows the clear route around the wall and strikes nearby players automatically. Its HUD reports watching, following, striking, returning or resting. Use the same two-press lantern bolt as the Sentinel Garden, now with a 0.5-second windup. Defeat it together, collect the ember at the left plinth and recover at the right spring. Downed travelers can rekindle at the entrance. The shared world continues while a menu is open; find cover before opening one.

Breaking sight or leaving its perception/leash range makes it return along the navigation graph. It chooses the nearest visible living traveler, with stable identity ordering for ties. It remembers an acquired target across its field of view until range or sight is lost. Respawn resets navigation to its authored home. Offline time does not advance movement or accumulate attacks. Automatic damage is saved only for active, host-verified travelers; reconnect restores target position, health, pursuit state, cooldowns and private player resources.

Format 56 adds CombatTarget.navigation, profile ground-graph.1. Up to eight navigating actors each own at most 64 nodes, 128 undirected edges and 16 visual parts. Author home, body clearance, speed, notice/leash range, field of view and attack range/damage/cooldown in the game package. Graphs must be connected, flat and supported by axis-aligned box ground; every segment is checked conservatively against static primitive collision with the actor's clearance. Unsupported dynamic, rotated, grouped or model collision rejects explicitly. Artwork is non-solid and may be a static imported visual; it cannot be shared between navigating actors. The public SDK exposes definitions, state readers, position/sight helpers and the pure stepNavigation reducer. This is authored ground-graph navigation, not an automatic navmesh builder.

The server runs bounded updates through the room queue every 200 ms. Each search expands at most 64 nodes; each step clamps movement time to 250 ms and limits path traversal. Obstacle sight, field of view, health, reach and cooldown checks precede attacks. The database commits shared navigation and private damage together under the room lease, with an outbox event. No active players and no return/respawn work means no NPC database work. The renderer's entity transforms and interaction reach use the same committed root pose, including attached visual parts. Player attacks also check cover. NPCs do not push or physically block travelers in this profile.

Navigation state participates in simulation checkpoint validation and presentation invariants. Standalone local player saves exclude this server-owned state; the room restores it on admission. Checkpoint compiler selection now also recognizes the preceding format-54 resource and format-55 combat profiles, fixing recovery validation that their normal play paths did not exercise.

For a new persistent host, export its normal world document with:

Terminal
node --import tsx tools/cli/trial-world.ts /tmp/watch-world.json --watch

Review later / unfinished: navigation is restricted to supported flat static ground. Automatic mesh generation, slopes/stairs, dynamic blockers, crowd collision, non-ground movement, perception memory beyond the current target, richer tactics and attack/death animation are not provided by this profile. Visual motion uses committed 5 Hz poses; interpolation and animation remain presentation polish. Menus do not pause the shared encounter. Review the generic menu copy, targeting controls and feedback during the final art/UI pass. The visual review also found small, poorly legible garden lettering and an oversized close-up guardian silhouette; include both in the required character/environment art pass. Qualify maximum graph/actor budgets, database query cost, room event-retention limits, clock changes, disconnect and host-loss timing, multiple places, restore drills, controller/mobile and physical-device performance in the final pass. General combat-state room upgrade migration remains explicitly unsupported. No final character/environment approval is implied by this reference guardian.

Focused acceptance — October 26: the reducer check covers cover, routing, automatic attacks, return home and rejection of a graph through a wall. The checkpoint/reach check covers moving-target transforms, interaction reach and restoration; the PostgreSQL check covers saved automatic damage/cooldown and stale owner rejection. Resource/combat checkpoint compiler identities also round-trip. Type/import checks and the client build pass. The real two-browser journey passes in 34 seconds: cover, pursuit, individual damage, a four-bolt shared defeat, private reward and reload with saved health/defeat. The first routes stayed behind cover and later ran into a plinth; the test route was corrected without weakening collision or perception. Arrival and pursuit were visually reviewed; the pursuit screenshot retains the current prototype art. Broader qualification remains deferred above.

#Installed creator extensions: The Shared Lantern

Terminal
npm run dev:extensions

Open http://localhost:5192/?persistent=shared-lantern in two browser profiles. Walk toward the center lantern and take turns pressing E. Four alternating touches awaken it; repeated touches by the same traveler do not advance it. The small ember to the left then becomes collectible. Reload either browser: progress and the shared finish remain. This profile owns .data/shared-lantern.1. Requires the same Node 22/PostgreSQL setup as the other trusted gameplay samples.

The independent, portable package is extensions/team-lantern/extension.json. The reference game imports that data and the public SDK. It installs and binds the package using authoring commands; no game-specific behavior is added to the engine. The package declares its API, namespace, revision, restricted execution profile, state/defaults, parameter schema, requested capabilities and JSON behavior source. World format 57 and compiler extension-world.57 pin the full artifact in content hashes and retained releases. The server keeps custom state private and shares only the allowed material result. That result can gate author-selected pickups, conversations and gateways. Normal reach checks, exact retries and database commits still apply. Failed scripts discard staged changes; repeated faults park the response.

To install a package in an existing exported world document:

Terminal
npm run extension:install -- world.json extensions/team-lantern/extension.json world-with-extension.json state material unlock

The output must be a new filename. The command reads data only, checks the artifact and explicit capability grants, compiles its source and writes a revised document. It preserves the document's other authoring data. It does not deploy or change a running room. install-extension is also available through applyWorldTransaction; combine it with set-object-behavior to attach the installed module, as shown in games/lantern-trial/extensions.ts. Installation is a world-wide authoring conflict boundary, with exact snapshot undo. Reinstalling identical content is allowed; replacing an existing package is rejected until an explicit state-migration path is supplied, including during room revision.

In the editor's response form, installed packages appear in Behavior. Choose one, enter its schema-checked Package settings, choose the active finish and permitted unlock targets, then use normal preview/save. Existing bindings retain their module and parameters when opened; editing them no longer silently replaces the package with a built-in response. Configuration changes currently reset that object's private behavior progress. Package source is changed in its artifact, not through the legacy response-source textarea.

Supported contract: darkwind-extension.1, profile object-response.1, up to 16 installed packages and 16 object bindings. Packages have an 8 KiB canonical JSON budget, private namespaced object state and typed object parameters. The only hook in this profile is onInteract. Capabilities are state, optional material (entity-local active/inactive finish) and optional unlock (up to eight configured actions in the same place). Restricted-language instruction/allocation/command budgets apply; imports, JavaScript execution, network and storage APIs are absent. The package cannot grant itself arbitrary ECS writes. npm run sdk:reference includes the extension API, capabilities, example and authoring command.

Focused acceptance — October 26: three native checks cover install rejection, source capability enforcement, exact authoring undo, checkpoint recovery, private custom state, atomic faults and a PostgreSQL two-player/retry/reconnect/unlock journey. The explicit replacement guard also passes. The real two-browser journey passes in 19 seconds, including repeated-touch rejection, shared activation, reload and collecting the unlocked ember. Its first run sent an interaction before the resumed simulation was ready; the driver now waits for visible interaction, unpaused simulation and a committed revision. The editor form check verifies schema errors and applies changed settings through real authoring validation. Type/import, client builds and the CLI install pass. The sample and editor form were visually reviewed; sample appearance.

Review later / unfinished: this installs restricted object-response packages; operator-installed native/server modules, general system/command registration, custom scoped panels and asynchronous job/result hooks remain part of the broader plugin work. Package state migrations, dependency resolution, distribution/signing, uninstall UI and a richer settings form are not provided. Test coordinated room rollout, actor limits, malformed package corpus, mobile/controller access and fault/load behavior in final qualification. Keep the garden's signage, interaction feedback, character and environment art in the required final presentation pass. A green sample does not close roadmap 11 or 13.

#Operator plugins and the garden palette

Run npm run dev:editor, select a saved object, open Panel workshop…, and choose The garden palette in Tool collection. Build the preview, allow the suggestions and choose a finish. Review suggestion → Check → Preview → Save uses the ordinary authoring flow. Undo/Redo works; Apply to shared room updates connected travelers. The installed collection comes from the independent games/plugin-atelier/plugin.ts package, which imports only packages/sdk.

npm run demo:plugins demonstrates the same package's native system, typed async job and native checkpoint restore in a headless simulation. Its clock advances to three, the job returns “Fern & stone,” and restoring advances the clock to four. This runtime example has no additional browser scene.

defineEnginePlugin checks darkwind-plugin.1, a stable namespace and executable revision. Packages register namespaced component contracts (including explicit migration chains), behavior modules, native systems, typed jobs, authoring commands and declarative panels. installEnginePlugins(packages, grants, authorize) returns an owned BehaviorProgram for Simulation.create and installed packages for the editor. Operator grants explicitly allow each registration class, external component reads/writes, cross-package job calls and authoring command capabilities. Package metadata cannot add its own grants. The editor's build registration is apps/editor/plugins.ts; world data never loads executable packages.

Native systems use defineNativeSystem with revision, phase, component reads/writes, before/after dependencies, cadence and operation budget. They run after the built-in behavior, nearest-interaction or final-state step in their selected phase. Their context provides tick/dt, sorted component queries, frozen reads and staged writes. Each system commits atomically; a caught capability error still rolls back its writes. Contexts expire on return. Ambiguous conflicting systems fail scheduler construction, rather than relying on registration order. Defaults are 1,024 operations per invocation (maximum 4,096), with at most 64 installed systems and a cadence from one to 1,000,000 ticks. A fault follows the native scheduler's recovery path. Native code remains operator-trusted JavaScript; these APIs do not sandbox arbitrary globals or interrupt CPU loops.

Checkpoints pin plugin identities and system contracts alongside behavior/job contracts. Ordinary restore rejects a changed program. Simulation.migrateBehaviors uses declared migration chains for same-named added component schemas as well as behavior state/configuration. Adding/removing component types still requires an explicit entity migration. The native fixture upgrades the clock's component from version 1 to version 2, restores it and continues execution without changing the source checkpoint. Executable changes require a new revision.

Typed authoring commands receive only their validated arguments and one frozen selected object. Their declared capabilities currently cover object transforms, material, visibility, rename and object behavior/dialogue/portal bindings. Output is limited to sixteen commands/32 KiB, all targeting that selected entity. Commands return a revision-pinned proposal; the normal authoring validator/compiler and Save remain authoritative. Panels bind fixed arguments to installed commands. The host resolves them before displaying the opaque iframe; the frame receives presentation and action IDs, never callbacks, credentials, source documents or mutable command arguments. Existing session, source-window, grant, revision and selection checks apply. Custom JSON panels retain their original format and permissions.

Acceptance record: three plugin checks pass for API/namespace/grants, typed scoped proposals, atomic native writes/context expiry, checkpoint compatibility and explicit migration. The 33 affected behavior/simulation/panel cases pass. The real browser journey saves, undoes/redoes and applies a second finish to two connected travelers; both resume on that revision. Type/import/generated-contract checks and the client build pass. The panel was visually reviewed.

Return during integration/qualification: restricted creator-script job requests are still separate unfinished work; native registrations must not be exposed as untrusted creator capabilities. Automatic package discovery/distribution, gameplay HUD panels and dynamic uninstall/dependency resolution are not implemented. The operator registry and durable private-component/job path below now provide shared hosting. Creator-authored async package installation and broader public component projections still require integration. Complete recorded external-result replay, native-system capacity/fault/device checks, cross-package dependency/version upgrades and broader live-revision migration cases. Simplify the installed-tool source view in the later UI polish pass. Final character, environment and arrival art remain required.

#Durable hosted programs · The Listening Lantern

Run npm run dev:programs, then open http://localhost:5193/?persistent=listening-lantern in two browser profiles. Walk toward the lantern and press E once. An installed private coordinator requests a typed job; after its saved result arrives, a native system lights the lantern for both travelers. Reload or stop/restart the launcher: the light remains active. The demo uses its own .data/listening-lantern.1 PostgreSQL database and retained engine. The launcher was smoke-tested to its serving state and then stopped.

The world reuses The Shared Lantern's environment and installed creator response. games/plugin-atelier/shared.ts supplies the independent native plugin; the operator registry in services/persistent/program-registry.ts combines it with that world's behavior program. The private coordinator is an explicitly declared internal binding. A native system observes the creator's saved touch count and projects only the resulting active/inactive material selection. The palette lookup is a local asynchronous demonstration job, not an external provider integration.

DW_ROOM_PROGRAM=listening-lantern selects this build-installed program. Registry names are allowlisted; executable module paths never come from world data. Program identity, grants, schemas, systems, jobs and private bindings are pinned in the room. Starting it without the matching program or with changed grants rejects. Private components and job entries persist in the existing behavior snapshot; clients receive only validated public material projections. Private native ticks persist without consuming public room revisions; public changes and completed job records use the room's sequence/outbox transaction. The host advances this bounded profile at 5 Hz with a separate saved program tick.

Claims are committed before invoking an executor. A claim includes the room lease, request and binding fingerprint; the executor's request ID is qualified by room and fingerprint for external idempotency use. Arguments are frozen typed data. Typed results are stored before the next program tick consumes them. Result consumption, private state, public projection and replay records commit together. Four execution slots and declared deadlines bound the host; an executor that ignores abort retains its slot until it settles. Results from an expired/replaced lease or changed request cannot commit. A successor marks an unfinished prior-host claim as failed instead of silently executing it again. External services must still enforce their own idempotency; an interrupted external effect may have happened.

Database migration 11 adds program identity/tick, job claims and result records. The backup catalog includes all new tables. Existing databases keep their retained engine/schema; the launcher uses a dedicated database instead of automatically upgrading old rooms. Program-aware stopped-room upgrades are now available for explicit compatible migration chains (see The Living Lantern below); uninterrupted hot upgrades remain open. Do not reuse an older room database with this new engine.

Acceptance record: the focused PostgreSQL installed-program check and three existing extension cases pass, including two-player convergence, private-state filtering, duplicate-claim prevention, reconnect, grant/version mismatch and old-host fencing/interruption. Backup schema/catalog validation passes. A real two-browser journey activates the light with one touch and recovers it after host restart; the light was visually reviewed. Type/import/ generated checks, persistent SSR build, and the complete dev launcher build/start pass with existing chunk-size warnings. The browser evidence uses the shared reference scene; the launcher supplies Listening Lantern title/instructions.

Return during implementation/qualification: the operator reference retains its private coordinator; the creator-authored alternative is documented below. Dynamic-entity public replication, uninterrupted program upgrades, explicit retry UI for interrupted jobs, journal retention and full backup/restore drills remain open. Qualify empty-room scheduling, 5 Hz snapshot costs, prolonged admission/load, ignored-abort executors, crash windows and device/ network combinations. Keep final character/environment/arrival art and approval.

#Creator async packages — The Creator Lantern

Run npm run dev:creator, then open http://localhost:5194/?persistent=creator-lantern in two browser profiles. Walk to the lantern and press E. A restricted JSON response requests the approved palette lookup and lights the object for both travelers after the result is saved. Reload either player or restart the launcher: the light remains. The demo owns .data/creator-lantern.1; Ctrl-C stops its host and database. Existing reference rooms are unaffected.

The independent source is extensions/listening-lantern/extension.json; the scene in games/lantern-trial/creator.ts installs/binds it through ordinary authoring commands. There is no private native coordinator. async-response.1 requires a trusted world, format 58 (async-extension-world.58), typed job contracts, the jobs capability and exactly onInteract/onJobResult. Store the returned request ID and match it before using an outcome. The reference clears pending state after failure so another touch retries; this is an explicit player action.

In the editor, select an object → Give it a response → Install a response package. Choose JSON, review the package identity/job list and check its requested permissions. Preview installation → Save adds it to this world. Open the response editor again, select the package, supply its typed settings and preview/save the binding. For the Creator Lantern use {"finish":"garden"}. Installation supports Undo/Redo; source replacement still requires a deliberate migration. The CLI also accepts the jobs installation grant. Async packages can only be installed into trusted worlds.

Installation permission does not enable a server executor. The operator separately uses creatorRoomProgram(world, catalog, grants); grants map package IDs to allowed job IDs. DW_ROOM_PROGRAM=creator-lantern selects the reference's explicit policy. Missing grants, job executors or mismatched revision/request/result/deadline contracts reject before room creation. Worlds never supply executable imports. Compilation and previews use typed unavailable-job stubs; they cannot perform external I/O. Shared execution requires the configured persistent host. Program identity and source/configuration are pinned by the existing room manifest and private binding.

Review later: program-aware live upgrades and hosted editor Apply integration; dynamic-entity public projection; browser replay-tool wiring; permission UX for many packages, package migrations, large schemas and accessibility/device coverage; long captures, retention, empty-room costs and full crash/backup/restore qualification. An installed async package is not runnable in the cooperative editor playtest; use the trusted configured host. Final character/environment/arrival art and user approval remain required. These are unfinished items, not passed acceptance.

Acceptance record: five focused package/PostgreSQL cases pass, covering format identity, installation undo, denied undeclared requests, missing/mismatched operator contracts, retry after failure, two-player convergence and reconnect. A real two-browser journey completes the creator-authored job and retains its light after host restart. The editor import/grant/Save/Undo/Redo journey passes; installation was visually reviewed, as was the shared light. Type/import/ generated checks, client/persistent builds and the full dev:creator build/start pass; port 5194 serves the expected redirect and Ctrl-C shuts down its own database cleanly. Existing chunk-size warnings remain. The first editor test used an ineligible bound object; using a newly created sphere exercises the supported flow.

#Public plugin state and room upgrades — The Living Lantern

npm run dev:public launches http://localhost:5195/?persistent=public-lantern with its own .data/public-lantern.1 database. Join from two browser profiles and alternate E at the lantern. Touches shared advances for everyone; four alternating touches light the lantern. The counter comes from an independently registered native component, not a special field in the game client. Existing reference databases are not changed.

Format 59 (public-component-world.59) adds world.publicComponents: each entry pins a complete component descriptor and an allowlist of authored entities. The component must be namespaced, public, trusted, persistent/session and unpredicted. At most 16 contracts, 32 entities per contract and 16 KiB of projected values are accepted. An optional display: {label, field} presents a scalar field in the player HUD; richer game presentation can consume the same typed ECS state. Contracts are validated against the installed native catalog. Only explicitly declared values leave the host: private behavior state, internal coordinator IDs and job payloads remain private. Unknown components/entities/fields and invalid values reject.

Room baselines and ordinary shared updates carry the complete public projection; absence removes a previously replicated component. Client simulation applies it atomically with the rest of shared state and includes it in checkpoints. The binary catalog has a new negotiated digest and updated golden frames; mismatched retained engines cannot silently share a codec. Field-level compression, interest filtering for large plugin projections and dynamic plugin entity replication remain review items, rather than assumptions about this bounded authored-entity profile.

The reference has component versions 1 and 2 in games/plugin-atelier/public.ts. Version 2 explicitly migrates {touches} to {touches, lit}. The host retains the count and computes the new flag on its next tick. A stopped-room upgrade now uses registered behavior/component migration chains and pins the new program manifest and private tick in the same transaction as the new release and public state. Both source and target must be available in the operator's configured registry. Changing a program contract requires a new program revision. Pending jobs must finish before upgrade; unsupported entity/module changes and incompatible contracts reject before any room state changes. Configuration changes also need a migration that produces the target binding; they are not silently reset.

Set DW_ROOM_PROGRAM=public-lantern for the existing room:upgrade CLI or internal control service. Use the same database/room/retained-engine directory as the room. Stop the room, preview the candidate with recovery container memory-chest, review that plan, apply its exact hash, then restart through the persistent bootstrap. Private ticks can stale a preview made while the host is running, so preview after stopping it. The bootstrap loads the upgraded saved release, not a fresh scene. The library/browser checks use publicLanternWorld(2) as the candidate. Retention, lease fencing, atomic receipts/outbox and retry identity remain the normal room upgrade path. No database migration is added in this increment.

Review later: uninterrupted hot upgrades and hosted editor Apply; entity addition/removal/renaming and pending-work conversion; projection interest and field-level deltas, larger component catalogs, long retention and full restore/load/ device qualification. The simple scalar HUD needs mobile/accessibility and final art review alongside the required character/environment/arrival pass. Public projection does not grant creator code new write capabilities.

Acceptance record: three focused native/PostgreSQL checks pass for declared projection privacy/types, binary round-trip, client checkpoint restore/removal, saved two-player state, explicit version migration, incompatible contract rejection, retry identity and refusal of pending external work. The real two-browser journey keeps three touches across the stopped-host upgrade, then continues to four with the new component field. The shared display was visually reviewed. All five existing upgrade transaction checks (including six process-kill boundaries) and three wire checks pass; three pure upgrade-plan checks also pass. Type/import/ generated and client build checks pass. Integration found and fixed streamed-header admission and checkpoint catalog/version registration, including the prior async format's checkpoint compiler ID. Existing chunk-size warnings remain. The full dev:public launcher also built its retained engine and served port 5195 (expected HTTP redirect); interrupting the smoke run stopped its host and database.

Creator responses also support portable browser job recordings: npm run replay:record -- /tmp/my-lantern-replay.json, then load it in /diagnostics.html under Follow the light again. This reproduces ordered inputs and saved results without calling external jobs. Production room-journal export and native operator-program replay adapters remain separate integration work.

#Live hosted program revisions — The Living Atelier

Run npm run dev:live. This opens a separate persistent demo at http://localhost:5196/?persistent=live-lantern and prints its private editor URL. Its database and authoring draft live under .data/live-lantern.1; older demos keep their pinned engines and saved journeys. Open the room in two browser profiles. In the printed editor URL, select release.light, open Behavior, change the package setting turns (for example from 4 to 6), preview, Save changes, then Apply saved revision. The running room adopts those settings and retains the counter. Join the room returns to that persistent room.

The host stages a copied native program snapshot and the new public projection. Every connected traveler prepares native simulation and graphics before SQL commits the release pointer, program manifest, private state and upgrade receipt together. Each traveler receives a fresh movement stream on acceptance or rejection; old content/stream packets and late revision acknowledgements cannot become new input. Pending external jobs reject the proposal without deleting them. After a successful commit, the old job runner is disposed and the new runner starts from saved state. A restart reads the accepted document from the upgrade journal.

This live profile supports installed operator programs, compatible saved response settings and explicit native component migrations. It keeps world/release identity, physical geometry, interaction identities/reach, inventory and gameplay contracts unchanged. Structural changes, floating/combat state, program replacement and unmapped entity changes still require their explicit migration paths. Changes to executable contracts need an operator-supplied program revision; uploading a package does not grant permission to execute it. The native v1→v2 public counter migration is exercised through the host API; the editor does not yet author native component migration functions. Published snapshot invitations remain in the ordinary editor; this hosted editor uses Save/Apply and its persistent invitation.

For a standalone hosted room, set DW_EDITOR_TOKEN (24–256 characters), retain DW_DATA_DIR/authoring, and use its /editor.html. The private token protects editing/Apply; proposed shared state additionally requires a room session. The current binary protocol adds content fencing to movement and transaction envelopes; its negotiated digest is 0f3bec34f5bcc9a3bcce7583739d4158e02577c5a757bc097133fbe37a168ffe. Existing retained engines need an explicit upgrade to gain this feature.

Focused acceptance: the real two-browser journey saved/applied a six-touch setting, kept three existing touches, rejected a structural change, migrated the native counter contract, continued to six and restored six after restart. Two revision/SQL checks, three prior program checks and three wire checks pass. Type/import/generated checks, client/retained-engine builds and the new launcher health smoke pass. The shared display and revision notification were visually reviewed in evidence/live-program-revision.png.

Review later: kill/restart at each live decision boundary; interrupted GPU staging, slow/disconnected clients and reconnect during commit; concurrent author saves and in-flight transactions; maximum room and archive capacity; routed Coolify authoring and persistence deployment; broader mobile/device/performance qualification. A client lost during the handoff may need to reload its saved traveler. The existing 32-upgrade retention budget applies. Dynamic plugin entity lifecycle, production recording/export and native replay adapters remain implementation work. Final character/environment/arrival art remains required and unapproved.

#Dynamic plugin entities — The Drifting Lantern

Run npm run dev:dynamic and open the printed play URL on port 5199. Two travelers can touch the lantern to wake three drifting lights, watch them move, restart the host and find the same lights, then touch again to let them rest. Another touch creates a fresh generation. The private editor exposes the existing record/finish/download flow; the browser replay studio reproduces the native creation, movement and removal sequence. The demo owns .data/drifting-lantern.1 and uses control schema 12, preserving other retained demo databases.

Native systems declare entities: {prefix, limit} alongside component read/write capabilities. context.spawn(id, components) and context.despawn(id) enter the same atomic native transaction as component writes. IDs must be in the declared plugin:<package>:<collection>: namespace with a bounded suffix; limits are at most 64 entities per owning system. Duplicate ownership, foreign namespace access, capacity overflow and invalid components reject the whole transaction. The plugin installer additionally requires the explicit entities:<prefix> operator grant. Systems read their starting snapshot; queued writes become visible at commit. Use a persisted generation counter or recorded ingress identity when respawning, so old identity references do not accidentally address a new instance.

World format 60 adds instances to public-component declarations. The release pins the allowed namespace/count, an authored static prototype and a transform field whose schema is the engine Transform contract. Each projected component row creates one visible entity, and removing the row removes its presentation. A native system must own that namespace and have write permission for the declared public component. Private clock/state components are not published. The ordinary shared state transport carries the validated values; clients reconcile complete instances into their ECS and use the normal renderer, scene deltas and checkpoint machinery. The new compiler identity is dynamic-entity-world.60; previous format/compiler identities remain supported.

The prototype supplies admitted mesh/model/material assets and place membership. Edit ember.prototype in the editor to change the reference appearance; publish that authored world with the matching operator program. Imported prototype instance counts are charged against existing draw, triangle and skin-palette admission budgets. Prototype colliders, behavior bindings, parents and moving mechanisms are rejected. The runtime transform is supplied by the native public component. Current instances are visual native entities: they do not automatically acquire physics, interaction targets or independently bound behavior/job runtimes. System-owned private components persist in their native checkpoint and disappear with despawn; raw spawn component maps cannot supply reserved behavior/job metadata. Declared bindings now create it atomically as described below; spawned physics and interaction capabilities remain explicit full-plan work.

The reference plugin persists a private generation/phase clock and three public Ember components. It does not use wall-clock time or replay randomness. The statically bundled drifting-lantern adapter restores its complete checkpoint, runs its native system and requires the same final state. Existing owner-only privacy and recording budgets apply. Native persistence and projections remain under the room lease and transaction fence; this adds no independent storage authority.

Review later: populated backup restore and host takeover during spawn/despawn, long-running generation/retention limits, imported model prototype edits and worst-case admission, live contract/prototype migrations, large multi-plugin scenes, interpolation of low-rate transforms, device/network qualification, bound dynamic behaviors/jobs, spawned physics/interaction capabilities and cross-place movement. Those cases are not passed acceptance. Final character/environment/arrival art remains unapproved.

Acceptance record: two focused lifecycle checks pass. Capability/capacity failures roll back structural commands, handles become stale after deletion, and real hosted entities survive lease-owner restart, update their client transforms, restore from client checkpoints and disappear from native/client state. Respawn uses fresh IDs; private clock data is absent from shared state and forged namespaces reject. The recording reproduces the exact lifecycle. Three existing public-program checks pass. The real two-browser journey captures, restarts, removes and respawns the lights, downloads its recording and matches it in the browser with zero external executions. Type/import/generated checks, client/retained-engine builds and the isolated launcher health/shutdown smoke pass. Lit scene and resting scene were visually reviewed. The larger lifecycle milestone stays active for bound gameplay entities and the gaps above.

#Spawned behavior owners — The Listening Embers

Run npm run dev:embers for The Listening Embers on port 5200, with its own .data/listening-embers.3 database (the earlier .1 and .2 databases are preserved). Touch the lantern to wake three small lights. Each light owns a separate behavior and requests its own saved atelier.wake job. After about two seconds they grow. Walk to the right of the lantern, approach a wandering ember and press E at “Share your warmth with this ember.” That light grows larger; touch it again to return it to its usual size. Both travelers see the change. Restart the host and the sizes remain; touch the lantern again to welcome them home. Both travelers see Lights welcomed home increase by three; that count survives restart. You can also call them home before their jobs finish. A later wake creates a fresh generation. Use the printed private editor to record, finish and download this lifecycle and replay it in the browser studio.

NativeSystemContext.spawn accepts an optional third argument: {module, configuration, seed}. The owning system must declare that module in entities.behaviors, and the plugin requires spawn-behavior:<module> in its operator grants. The module must be installed, configuration must match its schema, and the seed must be a positive uint32. The runtime prepares defaults, binding, execution, outbox and job rows together with the native entity. The complete spawn commits atomically; missing permission or invalid data cannot leave a half-bound entity. onSpawn executes at the next admitted behavior step before ordinary ticks. Bindings count against the runtime's instance budget, including batched creations.

Private binding state remains inside the host's native snapshot. The public Ember component exposes only its declared transform, which changes after the saved job result. The operator adapter restores the binding and repeats its hooks/native system using recorded outcomes, without invoking job executors. Both job-completed and job-pending deletion sequences are represented by native steps in the recording.

Default despawn(entity) immediately removes the entity and all its component/ outbox/job state. This explicit hard removal does not run onDespawn or retain outgoing events. The optional graceful path below runs the farewell first. The accepted room step deletes orphaned durable job claims in the same SQL transaction. The runner checks current request fingerprints and aborts work whose owner disappeared, even when every execution slot is occupied. Executors that ignore abort retain their bounded slot until they settle, and their late result is ignored. The database independently rejects completions without the same live request, owner and lease. Completed job outcomes remain in the room's result journal/recording as applicable.

This increment adds independently bound behavior/job ownership to native entities. Collision bodies and cross-place movement remain unfinished; whole-room capture and the required final art/qualification pass remain open.

#Live spawned interactions

The format-60 instance contract can now include interaction: {module, label, radius}. The bounded radius is in world units (maximum 5); it is independent of render scale. The release pins the module and label. The installed native owner must allow that spawned module and the module must implement onInteract. No executable code travels in the public projection. Client targeting uses the live projected position and the prototype's place. The host independently reads the current private entity and binding, verifies the host-admitted traveler position/place and radius, then dispatches onInteract with {action: 'toggle', actor}. Client-supplied poses cannot override admission. Missing, removed, unbound and out-of-reach targets do not execute a response.

Committed changes share the existing durable room transaction and native recording timeline. Removal drops the rendered entity and nearest prompt. A replacement uses a new generation ID; an old target cannot address it. Browser checkpoint restore admits a dynamic nearest target only when its declared public row is still present. Replay requires a live, correctly bound target at the recorded dispatch boundary. The Listening Embers release/operator use revision 2; the bundled adapter retains the revision-1 behavior for older worlds and recordings without interactions.

Review later: touch/gamepad targeting, prompt stability near moving-target boundaries, high-latency reach rejection, target density/occlusion and framing (the nearest prompt can refer to a light outside the camera view), authoring-panel controls for the interaction contract and collision bodies. This is the trusted hosted native-program path; solo/cooperative execution and cross-place dynamic movement are not implemented by this increment.

Interaction acceptance: five focused native/PostgreSQL checks pass (the existing two dynamic-lifecycle checks and three expanded spawned-behavior checks). The expanded journey rejects forged-near/far-admitted poses, wrong-place, private clock and removed targets, retains a touched size across restart, restores a browser checkpoint with a live nearest entity and exactly replays the journal. The real two-browser journey lets both travelers target/touch live embers, checks the shared size changes and restart retention, removes the generation and stale prompt, then downloads/replays six saved job results with zero external execution. Type/import/generated checks and the production client build pass. The interaction prompt and replay UI were visually reviewed; art, target framing and the broader review items remain unfinished. Interaction prompt.

Review later: job failure/cancellation during lease takeover, executor processes that ignore abort indefinitely, repeated host drain with pending spawned jobs, large mixed binding budgets, cross-owner event routing, explicit binding migrations, populated backup restore, routed Coolify and cross-device qualification. No database migration is added beyond control schema 12; existing demo databases and operator revisions are preserved.

#Graceful removal and homecoming

Call context.despawn(entity, {graceful: true}) from the native owning system. The owner must declare the bound module in entities.behaviors, and the native step must receive its behavior runtime. The call runs onDespawn on an isolated draft and returns the entity's queued outbox events, including its farewell events, in sequence order. The native system consumes these events into its declared writes; those retained effects and entity removal commit in one ECS/room transaction. A later invalid write rolls everything back. Events are data returned to the owner, not automatically broadcast or routed to other behaviors. The Listening Embers owner consumes ember.home events into its public atelier.Homecoming count.

A never-started entity skips the farewell hook rather than invoking onSpawn during removal. A disabled entity or a started entity with no farewell hook can still depart without executing a hook. A fault, parked response or exhausted invocation budget rejects graceful removal; the entity remains unchanged. New job requests during onDespawn are rejected. Local state/component writes on the departing draft disappear with the entity; only explicitly retained events survive. Existing pending job claim cleanup and late-result rejection still run with committed removal. Native replay executes the same hook/event consumption without external job execution. Release/operator revision 3 retains the revision-1/2 adapters and uses a separate demo database.

Review later: large-store draft cost, high-volume event backpressure, a creator UI for choosing hard removal after a faulty farewell, queued cross-owner routing, failure/retry under lease takeover, populated backups, device/Coolify qualification. This implements synchronous farewell hooks, without a timed animation phase. Physical presence remains unfinished. Final character/environment art is not approved.

Farewell acceptance: six focused native/PostgreSQL checks pass. The added rollback check covers a successful farewell followed by an invalid native write, a faulting hook and a successful retained event/removal. The durable journey checks counts of three and six, restart retention, pending-job cleanup and exact replay. The real two-player capture/restart/removal/respawn journey checks the visible shared count and replays six saved results with zero external execution. Type/import/ generated checks and the production client build pass. The homecoming counter was visually reviewed. Larger performance/failure/deployment checks above remain review work, not passed qualification.

Earlier binding acceptance record: seven focused native/PostgreSQL checks pass across spawned bindings, dynamic entities and native replay. These include atomic binding permission, actual job completion, persisted per-entity state after restart, removal of pending SQL claims, rejection of late results, cancellation with four occupied runner slots, and exact replay of completed and pending-deletion lifecycles. The real two-browser journey observes each light grow, restarts the host, removes/respawns the generation and imports the actual downloaded recording: six saved results, zero replay executions and an identical native checkpoint. Type/import/generated checks, client/retained-engine builds and isolated launcher health/shutdown pass. The visible lights and their replay were visually reviewed. Current visuals remain subject to the final art pass; broader checks above are recorded for later qualification, not claimed as passed.

#Physical spawned objects — The Solid Steps

Run npm run dev:solid on port 5201 (.data/solid-steps.1). Use the printed private editor URL for capture, and the play URL in two browser profiles. Touch the lantern to Raise or rest the stones, then walk right toward the three jade steps. They block walking; jump onto the first one and press E to Leave a warm memory. Both travelers see that stone's count. Restart while standing on it to retain the stone and response. Have the other traveler rest the stones: the first traveler drops to the ground and the shared homecoming count increases.

The format-60 public instance contract accepts collision: 'fixed-box'. Its prototype must be a box in a 3D place without authored floating bodies. Collision uses the same public transform as the visible instance: yaw-only rotation, each size axis 0.05–20 units, position within ±10,000 and a total declared capacity of 32 physical instances. Pose and size are fixed for the lifetime of an ID; touching other fields is allowed. Repositioning/resizing requires removal and a fresh ID. This is stationary physical presence, not a moving kinematic/rigid-body profile.

The host locks traveler movement during native-program placement admission and checks live native worlds plus saved offline traveler poses before the SQL commit. Live checks supersede stale saved positions for connected travelers. An occupied candidate defers the entire native step, including its job-result delivery; it is retried when the site is clear. Nothing from that candidate is broadcast or recorded as accepted. A low-level physical program step requires an admission callback. Live collision checks use the configured physics backend. Deletion, farewell effects and pending-job cleanup retain the same atomic program transaction.

Clients install accepted geometry with their public ECS projection and remove both together. Predicted clients can temporarily overlap a newly accepted shape until ordinary host motion reconciliation; projection does not crash merely because a prediction ran ahead. Checkpoints restore the native geometry against the declared public rows. Public instance display fields now show a numbered row per live object. The bundled native adapter captures accepted lifecycle/actions; it still records response state rather than the whole room's motion.

Review later: moving kinematic/rigid spawned bodies, contact with room-owned floating bodies, cross-place transfer, 2D physical instances, overlap/reconciliation under high latency, collapsed travelers and complex shapes, crowded/offline pose admission policy, placement deferral feedback, long-lived native-step deferral, large-store collision rebuild cost, live scene/program revision and streamed terrain coverage, device/Coolify/backup qualification and final art. The default prototype and arrival scene are functional examples, not approved final art.

Physical acceptance: two focused PostgreSQL/native journeys pass, one per physics backend, including occupied-spawn deferral, live-versus-stale saved poses, contact, standing, touch count, checkpoint restore, restart, immutable-shape rejection, safe removal and exact response replay. Four existing spawned behavior/ job/farewell checks also pass. The real two-browser Rapier journey stands on a stone, touches it, restarts the host while standing, removes it from the other traveler, and replays the exported recording with three saved results and zero external execution. Type/import/generated checks and the production client build pass. Contact view and homecoming were visually reviewed. The broader review list above is deferred, not qualified.

#Moving spawned platforms — The Rising Steps

Run npm run dev:rising on port 5202, using .data/rising-steps.1. Open the printed play URL in two browser profiles. Touch the lantern to Raise or rest the stones, walk right and take a short run-up to jump onto the first jade stone when it is low. The stones pause at the bottom, rise, pause at the top and settle again. Press E to leave a shared memory. A second traveler can rest them while you ride, returning you to the ground and increasing the homecoming count. The private editor can capture, export and replay the response program, including its target commands.

Declare collision: 'kinematic-box' on a format-60 public instance contract. The same box/prototype/place/capacity restrictions as fixed instances apply. The public transform is the owner's target, while the rendered ECS Transform and Collider follow the native body's actual pose. Size is immutable for an ID. Only the room program changes targets or removes instances; local platform mutation APIs reject these identities. Native movement approaches targets at at most 2 units/second and π/2 radians/second, within the existing per-step sweep budgets. Target changes do not rebuild the static scene. Creation uses the occupied-space admission checks; removal drops support and deletes the body and public row together.

Trusted motor packets include the actual platform poses for the traveler's place. Clients validate declared identities, restore those collision poses and then replay pending inputs against the native carry path. A shared creation/removal can arrive before the next motion packet; correction applies only to still-live instances. The room program owns desired motion; an obstructed rider cannot stop the platform for everyone else. The rider's carry is swept against obstacles. This does not add a general planner that prevents a platform itself from crossing scenery; creators must choose clear routes. The demo uses an unobstructed vertical lift.

Targets, generation, touch counts, jobs and farewell counts survive the host's program checkpoint. Native simulation checkpoints also retain body poses and rider contact. A reconnecting traveler joins current room motion; this is not a promise to retain an offline rider's relative anchor through arbitrary movement. Response replays reproduce the owner program exactly; whole-room motor recording remains unfinished. Earlier fixed/ember profiles and databases remain available.

Review later: free-moving rigid spawned bodies and room-owned contact impulses; kinematic/floating coupling; obstacle/crush and crowded-rider policy; rotating and horizontal route qualification; high latency and skipped/reordered lifecycle/motion boundaries; disconnected riders while platforms move; cross-place transfer and inactive-place catch-up; collapsed travelers; large-instance cost; streamed terrain and live revisions; device/Coolify/backup qualification; final character, environment and arrival art. Rapid jumping while continuously pushing into a side needs review: the contact can report temporarily ungrounded, so back away slightly for a short run-up before jumping onto the demo stone. Deferred cases are not accepted qualification.

Moving-platform acceptance: both Rapier and owned PostgreSQL/native journeys pass: occupied placement, physical contact, grounded boarding, rider lift, matching native poses for two travelers, owner-only mutation, immutable size, checkpoint restore, validated host motion frames, pending-input reconciliation, host restart, safe removal and exact response replay. The two earlier fixed-collision checks and six existing trusted-motion checks pass. The real two-browser Rapier journey passes with a short run-up, lift above 2.3 units, shared touch, host restart, removal by the other traveler and exported replay of three saved results with zero external execution (97 seconds, no page errors). Type/import/generated checks and the production client build pass. The ride view was visually reviewed. The checks above do not qualify the deferred cases or approve final art.

#Shared spawned rigid bodies — The Rolling Stones

Run npm run dev:rolling on port 5203 (.data/rolling-stones.2). Open the printed play URL in two browser profiles. Touch the lantern to Raise or rest the stones, walk right and push a jade stone by walking into it. The other traveler sees the same movement. Approach a moving stone and press E to Leave a warm memory; both travelers see its count. Rest the stones from the lantern; the homecoming count increases. Stop and restart the room to retain their positions. The private editor can capture and replay the response program; that recording does not capture room physics or player motion.

A format-60 trusted public instance may declare collision: 'rigid-box' and rigid: {density, friction, restitution}. Its box prototype and initial public transform pin the body's identity, shape and spawn pose; dimensions are 0.2–4 m, rotation is initially yaw-only, density is 100–8000, friction 0–1 and restitution 0–0.5. Up to 32 declared physical instances are allowed. A live identity cannot be resized or repositioned by the program: remove it and spawn a new identity instead. The native room owns its subsequent free motion and contact impulses.

The host locks live movement during placement admission. Occupied spawn candidates are deferred using connected and saved traveler poses. A prepared native lifecycle checkpoint and its owning program state commit together; failed or stale commits do not publish the candidate. Physics ticks save through the same release- and lease-fenced checkpoint revision. Clients install native collision bodies and receive authoritative body poses in motion packets, including when replaying pending input. Existing database schema is reused; the rolling demo has its own database.

Rigid bodies currently share one 3D home place, while travelers may visit other places. Fixed spawned boxes can coexist with rigid bodies. The profile also supports authored boxes, compound shapes and hinges in the rigid home place; it does not yet support linked bodies, rafts or ragdolls, changing doors, or a distributed physics owner. The body may expose its declared spawned behavior through an interaction. The player prompt follows the native body; the trusted host checks the actor against the committed body pose and matching physics revision before dispatch. A removed body or stale revision cannot authorize a new action; reach is checked against the current pose rather than the old spawn position. The program's response state stays separate from the native physical pose.

In the private editor, select the declared prototype (in this demo, search for ember.prototype) and choose Spawned physics. Adjust density, grip and bounce, preview and Save changes; undo and redo retain the settings. Choose Export to save the document, then start its separate room with:

Terminal
npm run dev:rolling -- --draft /path/to/exported-draft.json

The launcher prints a play URL on port 5204 and uses a database keyed to the exported document, so the default demo and other drafts are not overwritten. Live revision of the current rigid room remains disabled: export and start a new room to try saved settings. This editor control tunes an existing release-declared prototype; it does not yet create the public component, ownership grant or behavior binding.

Review later: authoring a new public contract and binding from the editor; line-of-sight for body interactions; moving bodies between places, additional body types, buoyancy and mixed rigid/kinematic scenes; doors, live revision and migration; distributed physics owners; contact at dense spawn boundaries, rider support and offline recovery; high-latency/loss reconciliation; long-session performance, backup/restore, Coolify and device qualification; and the final character, environment and arrival art pass. Placement deferral can also hold job results until space is clear. These cases are not qualified by the smoke checks.

Acceptance: two PostgreSQL journeys pass, one per physics backend, covering atomic spawn/removal, a stale-revision rollback, fenced motion saves and restart. Two native journeys cover simultaneous traveler contact, push, checkpoint restore, individual addition/removal and retained momentum. The two-browser Rapier journey moves a stone, checks both server-side traveler replicas track its pose, shares a touch, restarts, removes it from the second traveler, saves and reloads editor settings, and replays three saved response results with zero external executions. Focused tests reject old-pose and stale revision interactions and verify authoring undo/redo. The exported-draft launcher starts and shuts down cleanly in its own room. Type/import/generated checks, client and retained-engine builds, and launcher health/shutdown pass. The pushing view and physics dialog were visually reviewed. The example art remains provisional and has not been approved as final.

#Visiting another place — The Stone Crossing

Run npm run dev:crossing and open the printed play URL on port 5205 in two browser profiles. Raise the stones at the lantern. One traveler can use Visit the little garden at the edge of the court while the other pushes a stone; use Return to the stones in the garden to see the current shared position. The separate room uses .data/stone-crossing.1 and retains its state after restart.

The room keeps ownership of spawned rigid bodies in their home place. Travelers leaving that place retain a parked representation, and arrival validates and reconciles against the current committed body positions. The saved response-program recording does not capture the physics or player movement timeline.

Acceptance: native travel and checkpoint restoration pass on Rapier and owned physics. A two-browser Rapier journey covers a shared push during a visit, return to current poses, restart, removal and replay of saved program responses. The client build and type/import/generated checks pass. The garden view was visually reviewed; its geometry and character art remain provisional.

Review later: transfer of rigid bodies between places, more than one rigid home, mixing with advanced authored bodies, arrival under high latency or concurrent contact, other destination types, broad regression and deployment qualification, and the final character, environment and arrival art pass.

#Mixed spawned physics — The Stone Garden

Run npm run dev:mixed, open the printed play URL on port 5206 in two browser profiles, and use the lantern to raise the stones and the gold wall. Walk into a stone to push it toward the wall; a second traveler shares the scene. The lantern rests both kinds of object, and restarting preserves the room in .data/stone-garden.1. The garden crossing remains available.

A release may declare both fixed and rigid public boxes. The room owner incorporates fixed instances in its static geometry and commits changes to that geometry together with the program state and the rigid checkpoint. New obstacles that intersect a rigid body are deferred; restoration reconstructs the same fixed collision from the committed public state. Rigid bodies continue to share one home place.

Acceptance: Rapier and owned native checks cover fixed-obstacle admission, rejection of an overlap, a pushed stone stopping at the wall, fixed removal and checkpoint restoration. Replica checks on both backends confirm the wall blocks a player, survives checkpoint restoration, and stops blocking after removal. A two-browser journey passes shared creation, player contact with the wall, restart, removal and response replay. Type/import/generated checks pass; the demo launcher builds, starts and shuts down. The shared scene was visually reviewed and remains provisional.

Review later: a scripted browser push missed the moving stone and should be retested interactively; more varied collisions, high latency, kinematic and authored body mixing, body transfer, broader deployment/regression qualification and the final character, environment and arrival art pass remain open.

#Authored and spawned boxes — The Stone Workshop

Run npm run dev:workshop and open the printed play URL on port 5207 in two browser profiles. Walk into the copper authored block or approach and press E to push it, then raise the stones and wall at the lantern. The room uses .data/stone-workshop.1 and retains its state after restart. Spawned and authored boxes are simulated together, so they can push one another; clients receive separate, synchronized authored and spawned motion frames.

The supported mixed profile places simple authored dynamic boxes and spawned rigid boxes in one 3D home place, within a combined budget of 32. Existing fixed spawned boxes may coexist. Both physics backends pass focused tests for a spawned stone moving the authored block, synchronizing both motion frames and restoring the shared checkpoint. A two-browser journey passes shared creation, restart, removal and response replay. The workshop was visually reviewed.

An intentional authored-block push checks native reach and commits its impulse with the action receipt; retries do not push twice. Both physics engines pass PostgreSQL checks for push, idempotency, stale revision, reach and restoration. The two-browser journey confirms both travelers see the pushed block move.

Review later: body transfer between places, authored mechanisms (rafts, linked bodies, ragdolls and planar bodies), sensor coupling, broader interactive browser push scenarios, high latency, broader regression and deployment qualification, and the final character, environment and arrival art pass. The current example art is provisional.

#Moving platforms and rigid bodies — The Stone Lift

Run npm run dev:lift and open the printed play URL on port 5208 in two browser profiles. Use the lantern to raise the stones, wall and lift. Step onto the lift when it is low or guide a stone toward it. The room's separate state is stored in .data/stone-lift.3 and survives restart.

Spawned kinematic platforms and rigid bodies share the room's native physics owner. The program sets platform targets; the room advances and checkpoints the resulting motion and supplies platform poses to travelers. Existing fixed spawned boxes and authored boxes, compound shapes and hinges can coexist. Runtime platforms and rigid boxes currently share one 3D home place.

Acceptance: both physics backends pass focused checks for a platform lifting a rigid box, rider carry through reconciliation, hosted creation, movement, removal and checkpoint restoration. A two-browser journey checks shared platform positions, restart, removal and response replay; it does not test riding. The lift scene was visually reviewed. Response recordings capture program responses, not the physics timeline.

Review later: interactive multi-traveler riding and stone boarding, high latency and loss, rider correction across larger drift, linked bodies, rafts, ragdolls, body transfer, broader regression and deployment qualification, and the final character, environment and arrival art pass. This example's art remains provisional.

#Shaped and hinged bodies — The Stone Atelier

Run npm run dev:atelier and open the printed play URL on port 5209 in two browser profiles. Raise the stones and lift at the lantern; approach the little bridge and press E to raise or lower it. The cedar arch is a compound body with a real open passage. Both are authored objects in the same physical room as the spawned stones, fixed wall and moving lift. The room keeps its state in .data/stone-atelier.1 across restarts.

Both physics backends pass focused checks for authored compound and hinge state, coexistence with spawned rigid bodies, client frame reconciliation and checkpoint restoration. PostgreSQL checks cover the shared hinge action, idempotency, stale revision and restoration. A two-browser journey reaches the bridge by walking to the right of the lantern, then toward the bridge, and verifies that both travelers receive its raised state after pressing E. The scene was visually reviewed; the example's art remains provisional. Existing editor controls can tune these authored body settings; export a draft and start a separate room to apply it.

Review later: wider compound-body contact scenarios, interactive multi-traveler riding and hinge obstruction, linked bodies, rafts and ragdolls, planar mixing, transfers between places, sensor coupling, high latency and loss, broad regression and deployment qualification, and the final character, environment and arrival art pass. Response recordings capture program responses, not the physics timeline.

#Linked bodies with spawned stones

Run npm run dev:links and open port 5210 in two browser profiles. Raise the stones at the lantern and look for the linked pair beyond the right side. Existing authored body-link settings can now coexist with spawned rigid bodies and platforms in their home place. The separate room uses .data/stone-links.1.

Both physics backends pass focused joint/coexistence and checkpoint restoration checks. Both browsers received the linked pair and spawned bodies, and a two-player test with the editor open raised the bridge for both travelers. A prior run saw slow autosaves delay that action. In-flight transaction retries now share one queue entry, but the underlying latency, direct shared manipulation of the linked pair, and broader load, recovery, raft, ragdoll and transfer cases need review. The art remains provisional and the final character, environment and arrival art pass is still required.

#A mannequin with spawned stones

Run npm run dev:puppet and open port 5211 in two browser profiles. Raise the stones at the lantern, walk behind the bridge, and press E near the mannequin to tip or reset it. Reset waits until travelers clear its standing space. Its separate room persists in .data/stone-puppet.1.

Both physics backends passed focused coexistence, reset and checkpoint tests. Two browsers observed the tip; a PostgreSQL/socket test covered the shared action, restart and repeated transaction. Direct browser reset, performance under load and broader regression remain to be checked. The mannequin and scene are provisional; the final character, environment and arrival art pass remains required.

#A raft with spawned stones

Run npm run dev:cove and open port 5212 in two browser profiles. Raise the stones at the lantern, walk past the back of the court to the jetty, step or jump onto the raft and press E for a seat. The first traveler can steer with movement controls; the second can ride as a passenger. The separate room persists in .data/stone-cove.1.

Both physics backends passed focused helm, seating, steering and checkpoint checks. Two browsers observed the helm and movement; the browser test started aboard, so boarding from the jetty and passenger riding still need direct testing. Shared ownership and restart were checked against PostgreSQL. Review disconnects, load, broader regression and the final character, environment and arrival art pass.

#Baked navigation and shared gates

Run npm run dev:navigation on Node 22, then open http://localhost:5215/ in two browser profiles. Walk toward the timber gate and press E. The watchkeeper can cross the open threshold; closing it blocks future routes for both players. This demonstration does no damage. Its separate persistent room uses .data/gate-garden.1; the launcher also prints the private editor link.

The public bakeNavigation(place, settings, home, {min, max, cellSize}) SDK function produces ground-mesh.1 navigation for world format 66. It bakes convex square polygons, shared-edge connectivity, a home-connected component and a geometry revision. The entire polygon has radius clearance and supported floor; neighboring floor boxes join, while gaps remain excluded. Static collider edits require an explicit rebake. Compilation recomputes the artifact and rejects stale or altered cells, routes and revision. The token is a cache identity, not a cryptographic digest; the world still uses its existing content checksum.

Shared door states rotate the actual door bounds around the authored hinge. Routes filter those blockers when selecting their next edge. A closing door causes an in-flight edge to reverse when its return is clear, or wait when trapped, rather than teleporting the actor. NPC state records the mesh and door revision; a state from a different mesh is rejected. Saved SQL updates, native checkpoints and recorded NPC reconstruction use the shared door state. Legacy ground-graph.1 worlds retain their original compiler and movement rules.

Bounds: eight actors, 256 candidate polygons per actor, flat axis-aligned primitive collision, and unparented box doors. Open rotated doors use conservative bounding boxes. Baking keeps only the island containing home. This is SDK authoring; a visual rebake control and arbitrary imported/sloping geometry are not implemented. NPC movement still uses the existing lower-cadence authority step; per-tick locomotion and smoother presentation remain follow-up work. Geometry-changing live upgrades need an explicit NPC state migration/reset; old node indices are never silently reused. Door changes need no static rebake.

Focused acceptance (October 27): five new checks passed, including PostgreSQL door transactions and a fresh connection, plus four legacy navigation checks. Type, package-boundary and wire checks passed. The physical-GPU two-player journey passed opening/closing, visible keeper crossing and reconnect; the shared gate screenshot was visually reviewed. The initial screenshot heuristic incorrectly rejected the large flat closed door; visual inspection confirmed rendering and the open-scene check passed. Broader device/load tests, interrupted mixed-action replay, live mesh migration, narrow angled openings, avoidance between actors, smooth path presentation and the final character/environment art pass remain qualification or follow-up work. No user art approval is implied by this reference scene.

#Authored windows into another room

addRoomCrossing accepts roomWindow and promotes the world to format 68:

ts
roomWindow: {
  offset: [0, 0.5, 0], yaw: 0, width: 2.3, height: 2.8,
  exit: { position: [0, 1.35, 6], yaw: Math.PI }
}

The source center is the crossing position plus offset; yaw values use radians. The destination plane views the other room's entry place and leaves its arrival position unchanged. One visible window is allowed per place. The editor exposes these settings in Room doorways and preserves them through save/reload and undo. Runtime setup and remaining qualification are recorded in Room routing.

Add walkThrough: true beside roomWindow to opt into format-69 automatic entry. The standing traveler must fit inside the aperture. The host maps the frozen position and facing using the same source/destination planes as rendering, then checks destination collision before ownership moves. Existing windows and E/menu entry keep their previous behavior. See walking room entry for the demo and remaining seamless-traversal work.

End of SDK & The Meridian