The place-content.4 transport separates a fully validated source world into a small manifest, one source unit per place, one unit per distinct imported model artifact and independently hashed texture units shared across models. It is the fetch/decode and selective preparation foundation of milestone 8. Ordinary, published and persistent players now bootstrap from the structural manifest and stream selected models. Editor development rooms also stream immutable proposed/accepted revision manifests through the shared activation barrier.
packages/world/content-build.ts owns the source before asynchronous validation and builds the archive only after compileWorld accepts the complete world. The manifest pins the room admission content fingerprint, canonical source digest, compiled world digest, entity identity digest, complete asset digest and compiler profile. Profile 2 adds bounded clip durations so unloaded models keep the same simulation clocks. Readers retain profile 1, 2 and 3 transport compatibility, including legacy embedded images; streamed simulation requires profile 2 or newer. Profile 3 adds a closed texture catalog and per-model image dependencies without changing source, model, texture or compiled-world identities. Place order is retained because it participates in world identity. Place descriptors declare model dependencies and portal destinations, including the one visible destination window. Aliases of the same artifact share one unit. Published/persistent manifests additionally bind the accepted document identity and revision. Room admission returns the exact manifest digest as contentManifest; the player rejects a mismatch before downloading models or joining the room. This publication binding is an attestation by the same-origin retained server, not a new independent checksum proof for a legacy full-source WorldDocument.
Units decode to canonical JSON and use SHA-256 over the exact transferred bytes. Profile 4 can losslessly compress texture units with deflate-json.1; the compressed bytes have their own length and hash, and the reference declares the exact expanded JSON length. Original texture/model/world identities remain unchanged. The manifest includes each unit's length and digest and has its own checksum. The source admission fingerprint uses the existing serialization order; canonicalizing the source before calculating that fingerprint would silently change room identity. Integrity hashes identify content; they do not authenticate an arbitrary server or replace semantic validation.
#HTTP delivery
Each endpoint exposes GET/HEAD at <base>/manifest and <base>/<manifest-sha256>/<unit-sha256>:
| Room | Base |
|---|---|
| Current relay | /api/content |
| Editor development room, current accepted head | /api/development/content |
| Immutable development revision | /api/development/revisions/<document-sha256>/content |
| Published invitation | /api/playtests/<published-document-sha256>/content |
| Direct or regional PostgreSQL persistent room | /api/persistent/<room>/content |
The manifest uses application/json; unit responses use application/octet-stream. Compression belongs to the manifest codec rather than HTTP Content-Encoding, so the digest covers exactly what fetch receives. The mutable manifest response is not cached. Exact unit URLs are immutable and include an ETag and Content-Length. Unknown or stale unit URLs return 404; a source revision changing during preparation returns 409. No response substitutes new bytes at an old identity. The server retains one archive/build per room, coalesces concurrent preparation, retains preparation failures for that revision and caps in-flight content responses at eight. Invalid routes never trigger a compile. Shutdown fences pending results.
Published requests dispatch to the invitation's own retained room engine. Existing retained engines without these routes can return 404; a host must not silently substitute its newer compiler. Development content follows the accepted room revision, not an unsaved editor draft. The persistent route uses the same pinned source as PostgreSQL admission. These read routes expose the same source already available through the existing world-read routes; room joining retains its separate admission checks.
Regional delivery invokes persistentWorldContent from the room's retained engine. It never compiles the release with the doorway's current compiler or starts a private room just to serve assets. The doorway caches at most four release/engine archives, coalesces compilation, retains failures for that cache entry, pins active responses against eviction and limits content responses to eight. A binding change while preparing returns 409; old unit identities do not select a new release. Retained runtimes lacking this optional capability remain on their own older full-source clients.
#Player startup and recovery
The main player fetches/validates the layout without any model units, retains its gameplay rules for UI and admission, then releases that loader. Published links check their document binding before admission. The simulation worker opens the exact manifest accepted by admission and prepares the saved/current place plus one visible destination. Initial graphics creation has an empty authored catalog; source/portal graphics are installed before the ready gate enables presentation. The player bundle no longer embeds the default authored world/model catalog. The shared traveler rig remains a renderer asset.
Graphics recovery rebuilds from the simulation's accepted resident catalog. Worker recovery opens the pinned manifest again and reconstructs the accepted checkpoint/current durable place; it does not fetch a full source document. Checkpoint context keeps the same global structural rules across ordinary place travel. Development checkpoint context additionally pins the immutable revision base and manifest. Recovery can reconstruct an older accepted checkpoint before migrating it to the current room head; local saves use the same retained revision path.
tests/browser/player-streaming.spec.ts uses a published retained engine and two real shared players, one per graphics placement. It observes no /world requests, no unused-model request, and no destination-model request before travel. Corrupt destination bytes reject without leaving the source, and valid retry, device loss, worker loss and return preserve shared identity. A mismatched admission manifest rejects before any model request. The existing original-beta recovery corpus separately checks airborne restoration, concurrent shared progress, bounded retry, invalid checkpoints and delayed admission.
The cooperative and trusted persistent portal journeys verify this bootstrap with real database membership, credential-only reload and both recovery placements. The regional doorway journey additionally checks retained delivery through process recovery and router restart. Player integration acceptance records the browser, PostgreSQL, headless and build evidence, including initial failures and qualification limits.
#Shared development revisions
The current development manifest identifies the accepted head. After reading it, players pin /api/development/revisions/<document>/content so a concurrent Apply cannot change their recovery source. Proposed manifests must match the coordinator's content fingerprint, document and revision before any candidate model download. The immutable delivery cache owns at most four archives and eight pending responses, including asynchronous history reads; concurrent requests coalesce. Missing-history reads are retryable and cannot poison a later publication of the same document. Known archived compile failures remain attached to that cache entry. This memory cache does not evict the durable snapshot history.
Simulation.prepareStreamedRevision prepares current-place/window dependencies, fences source disposal, cancellation and changed source place/content, then synchronously forks native state through the existing revision adapter. The old simulation and its leases remain intact. DevelopmentSession stages the candidate projection and GPU catalog, swaps only after the existing durable room acceptance and graphics commit, then disposes the old runtime and closes its owner. Rejection/cancellation closes only the candidate owner. A disconnect between local commit and room resume cannot abort the newly adopted owner; an older asynchronous abort cannot unfreeze a newer proposal.
Socket recovery checks the accepted manifest and migrates when necessary. An older lightweight save or native checkpoint reconstructs its pinned revision before migrating to the accepted head. Main-thread UI receives structural gameplay rules and manifest metadata without embedded model geometry. Legacy full-document HTTP routes and full-source simulation APIs remain available to existing tools.
Shared revision acceptance records native parity, bounded archive HTTP, two-player corrupt/held downloads, offline history, lost commit, checkpoint/worker recovery and portal journeys. These are software-GPU functional checks; hardware capacity and sustained-load qualification remain open.
#Client ownership
PlaceContentClient.open(base, {expectedContent, expectedManifest?, signal?, residentBytes?}) reads and verifies the manifest. Pass the room's admitted content fingerprint. Where a manifest identity was already pinned, pass that identity too. loadPlace(id, signal?) fetches only that place and its declared models. Profile 3 additionally acquires the models’ independently declared images. Its result is a decoded source container, not an active simulation place. Imported models additionally pass the existing artifact semantic and checksum reader before exposure.
Concurrent consumers share unit requests and immutable decoded models. A returned lease owns those references until release(). Place source is copied for the caller. Cancelling a consumer retires its references, preserving requests still owned by siblings. Cancelling a queued destination prevents its download; cancelling the last pending consumer aborts the native request. Reservations remain until an in-flight fetch settles. Closing the owner aborts all work, retires accepted leases and prevents late results from installing. Replace/close the owner when switching content revisions.
| Bound | Current value |
|---|---|
| Manifest | 256 KiB |
| Unique units | Profiles 3/4: at most 1,088 (32 places, 32 model names, 1,024 textures); legacy profiles: 64 |
| Texture bindings | At most 32 per model; image artifacts deduplicate across bindings/models |
| Individual unit | 16 MiB transferred and 16 MiB expanded JSON |
| Unique declared residency cost | 32 MiB: transfer bytes plus expansion bytes for compressed units |
| Client residency reservation | At most 32 MiB, configurable lower |
| Native fetches per client | 2 |
| Queued unit requests | 8 |
| Pending plus accepted place leases | 8 |
| Native request deadline | 15 seconds |
Reservations account for encoded unit sizes plus declared expansion for compressed units, including pending work, and deduplicate shared ownership. Cooling and speculative limits use this same cost. A highly compressible asset cannot consume only its tiny wire size against a residency budget. They are not a measurement or hard bound on JavaScript heap, parser copies, expanded geometry or GPU memory. Callers retaining results after releasing their leases own that extra memory. Low-level clients default to immediate release. Normal player owners opt into an 8 MiB, five-second cooling cache, described below.
#Independent texture ownership
Profile 3/4 models with textures use a format-2 model unit: texture bindings keep their sampler and replace the embedded image with its artifact digest. Each catalog image points to a format-1 texture unit containing the original complete texture artifact, including its mip chain. Untextured model and place units keep their format-1 envelopes. The builder deduplicates images by their validated artifact digest, preserving distinct usage/color-space derivatives.
The client verifies exact transport bytes, validates each image’s checksum and derived mips, checks that model bindings match the manifest’s sorted dependency list, then reconstructs the original model and runs the complete imported-model reader. Invalid samplers, material/image usage, geometry, bounds or original model checksums still fail before exposure. A transport checksum is never semantic admission.
At most two image consumers per preparing model feed the existing two-fetch global scheduler. The first failure is preserved while it cancels the other consumer and waits for both consumer promises before releasing the place’s acquired leases. Native cancellation retains its reservation until the transport settles; another live owner preserves a shared request. Texture unit leases participate independently in cooling, expiry and pressure eviction. The shared hydrated model is retained only while its model unit has owners; last release clears that copy so a cooled geometry descriptor cannot silently retain retired images. Reacquisition from cooling rebuilds and validates the model without another download.
F2 and exported reports expose decoded texture counts, decoded canonical payload bytes, resident wire bytes and expansion reservations. These numbers do not measure GPU allocations or expanded JavaScript copies: strict hydration currently copies the original image data into each distinct model. A destination still needs all its required images, collision and GPU preparation before acceptance. Progressive mip delivery and GPU-compressed texture variants remain open. Lossless transport compression is described below. Static model detail now reduces distant draw work after loading; it does not reduce downloaded model/image bytes.
#Lossless texture transport
The compiler uses deterministic zlib/DEFLATE level 6 through the already-pinned fflate dependency and keeps raw JSON when compression would not reduce bytes. Only profile-4 texture references may declare the codec; place/model references and all older profiles remain raw. Descriptor aliases must agree on wire size, codec and expanded size. Both declared limits are checked before downloading.
The reader verifies transferred length and SHA-256 before expansion, then feeds the scheduled inflater in 256-byte input slices. It rejects output beyond the declared length at each output callback rather than continuing through the remaining compressed input. Exact expanded length, zlib Adler checksum, strict UTF-8 and canonical JSON are checked before the usual texture semantic/mip validation and full model admission. The decoder uses temporary internal output/dictionary buffers; reservations are not a total JavaScript heap or CPU bound. Cooperative runtime asset work now yields during expansion, base64 conversion and mip validation; aggregate scheduling, remaining synchronous operations and physical-device qualification remain open.
npm run bench:content-compression compares six analytic 64/256/512-pixel repeating-pattern and seeded-noise textures with complete mip chains. It records exact bytes and full transport-plus-texture-admission CPU observations. The retained initial compression-only measurements, before cooperative scheduling, show the 512-pixel examples shrinking from 1,864,830 bytes to 9,655 bytes (pattern) and 1,355,368 bytes (noise). In those initial runs, median local elapsed time rose from about 169 to 177 ms and 181 to 256 ms respectively. Current task yielding and updated total-time/heartbeat measurements are documented in asset work; rerunning the benchmark includes that scheduling. These three-observation results demonstrate a bandwidth/CPU tradeoff, not a measured network-startup or frame-rate improvement. Pixels and artifact identities match exactly. See measurements.
#Adjacent preloading, priorities and cooling
Normal players sample movement interest at most four times per second. The simulation resolves authored/anchored exit positions and conditions, then selects one nearby destination in the direction of actual horizontal motion (a bounded 6–10 metre reach). A reached hint can remain while stationary. Moving away, pausing, changing places/revisions or closing the owner cancels obsolete interest. The current place and its already-resident visible window destination are excluded; the destination's graph is never traversed speculatively.
PlacePreloader owns one decoded-place lease and at most one preparing job. Its unique declared place/model/texture bytes are capped at 8 MiB, or the client's smaller reservation budget. Replacing a pending hint waits for cancelled native reservations to settle before admitting another. Generation checks prevent obsolete results from becoming ready. Failures stay diagnostic and retry no faster than every two seconds while that same hint remains relevant; they do not freeze the accepted simulation or display a fatal player error. A ready preload is only decoded, not collision/GPU-ready or active.
Queued unit requests use stable priorities: current collision models, interaction-bound models, other current-place models, visible-window content, then speculative content. Shared requests can be promoted without duplicate downloads. A speculative request does not start alongside another unpromoted speculative fetch; the overall native-fetch limit remains two. In-flight transport work is not evicted merely to reorder the queue. Current-place models are ordered by their strongest collider/interaction use. Independent texture requests inherit the owning model’s priority and use the same global queue and reservation limits. Static imported-model LOD is selected after preparation; streamed LOD variants and progressive mip selection remain open.
StreamedWorld.preparePlace first cancels obsolete speculation and waits for its native reservations. For a matching pending or ready hint it shares/promotes the request and acquires candidate ownership before releasing the preload, so transfer bytes are reused even when cooling is disabled. Collision validation, native simulation construction, GPU preparation and durable room acceptance retain their existing separate gates. Preloading cannot change place membership or remove the floor beneath a player.
Player owners retain last-release decoded units for up to five seconds within an 8 MiB cooling budget. A single timer expires them; pressure evicts oldest unowned units first. Cooling bytes remain included in the existing 32 MiB total encoded reservation limit. Active leases and in-flight reservations cannot be evicted. Closure cancels the timer, revokes leases and fences late native completion. These figures describe encoded payload reservations, not expanded geometry, parser copies or total JavaScript heap. GPU ownership remains governed by its independent model pool.
ContentClientOptions exposes coolingBytes, coolingMs (at most 30 seconds) and an optional monotonic now function for deterministic expiry checks. collect() expires unowned units. F2 and exported reports include reserved/cooling bytes, fetch/reuse counts and the selected preload's status, failures and cancellation counters. Preload/cooling state belongs to one manifest owner and is not carried into a different revision or recovered worker.
Preload acceptance records native cache/queue/cancellation tests and both real player placements. The browser fixture demonstrates held/corrupt speculation, recovery and zero additional model requests at crossing; published recovery, shared revisions and portal journeys also pass.
#Structural compilation and place preparation
compileWorldLayout accepts an explicit WorldLayoutDefinition with model hash references instead of embedded artifacts. It validates the whole structural graph: global entity identities, gateways, anchored interactions, inventory definitions, materials and behavior bindings. It produces immutable identities, transforms and material tables. It does not produce a complete-world content signature, model payloads or collision data, and cannot be substituted for a compiled simulation world.
PlaceContentClient.loadLayout() reads all place source units without fetching any models. One layout lease is allowed per client. StreamedWorld.open() owns that lease, compiles the structural graph and checks its compiler profile and global entity identity digest against the pinned manifest. Downloaded model clip durations must also match the manifest metadata. Source definitions for all places are currently resident to preserve cross-place validation; this is selective model payload loading, not on-demand loading of every authored placement.
StreamedWorld.preparePlace(id, signal) downloads just the selected place's models and runs preparePlaceContent. The compiler validates exact dependencies, artifact identities, model semantics, draw/triangle/palette budgets, transformed bounds and imported collision triangles. The prepared result includes the place's compiled objects, model catalog and collision data. Its valid flag is revoked by release or owner closure. Native physics creation, GPU preparation, authoritative membership and activation still have their own gates. No caller should interpret CPU preparation as permission to expose a destination.
The complete-world compiler uses the same layout and collision helpers. Golden outputs captured from commit c6a4489 verify unchanged canonical output and content identities for formats 1–17. In-memory legacy transforms also preserve signed zero; an initial refactor regression here was detected by the real Darkwind projection comparison and fixed without changing existing identities.
The isolated browser fixture builds a client containing the streaming implementation without embedding its world/model fixture. It retrieves the world through real relay HTTP, creates native collision and WebGPU resources for the selected place, stages another place, then explicitly commits. Corrupt and cancelled downloads preserve the accepted frame. A round trip restores the exact PNG hash, and closure retires every content reservation, buffer and texture. This test harness demonstrates the preparation path; it is not the normal multiplayer player's activation integration.
#Simulation residency and travel
Simulation.createStreamed(source, save?, program?, signal?) owns leases for the saved/current place and its single visible window destination. That destination's own window is not followed. All authored entities, gameplay rules and material identities remain available globally; imported geometry and native collision are resident only for those selected places. Unloaded animated entities advance using validated clip metadata. Model semantics and exact duration agreement are checked again when their payload arrives.
The runtime world contains complete gameplay definitions but descriptors for unloaded models; it is not complete authoring source and must not be serialized as a new release. Its content carries the pinned complete-release signature. Full compilation still emits identical outputs for formats 1–17, with no clip-summary field added to legacy compiler output.
Simulation.fromStreamedCheckpoint validates the existing checkpoint envelope against the pinned release, prepares the checkpoint's place and visible destination, checks the complete compiled signature and restores native physics/ECS through the same restoration code as full-world simulation. Checkpoint profiles remain unchanged. Tests compare exact checkpoints against fully loaded simulation after native movement and distant animation advancement, and restore in both directions.
prepareTravel downloads before cloning live state. The candidate briefly combines source and destination collision for the synchronous native swap, then retains only destination/visible-window assets. Failure, cancellation, source disposal or changed source place rejects the candidate and releases its leases. Direct synchronous streamed interaction/travel rejects before mutating gateway flags. Disposal releases residency; the shared StreamedWorld owner remains usable by replacement simulations until its caller closes it. Releasing that owner revokes accepted content and prevents further simulation steps/checkpoints.
The existing PlaceTravel controller now cancels pending downloads and prepares a fresh projection from the candidate's resident assets. Destination graphics prepare before the existing durable membership/graphics publication boundary. A failed candidate leaves the accepted runtime intact. Shared editor revisions use prepareStreamedRevision with a new immutable owner. The streamed API rejects full-source revision preparation explicitly.
The second isolated native browser journey uses the production Simulation and PlaceTravel implementations, real content HTTP and WebGPU. It verifies failed-download rollback, a held commit gate, retirement of the source model, same-checkpoint reconstruction, round-trip pixels and zero content/GPU resources on closure. The durable gate in this fixture is controlled locally; it does not itself prove streamed multiplayer admission or database travel. This earlier fixture remains a focused native check; the normal published player journey now verifies actual multiplayer bootstrap separately.
#Verification and remaining activation work
tests/streamed-simulation.test.ts, tests/streamed-place.test.ts and tests/browser/streamed-content.spec.ts verify the structural compiler and selective native preparation described above. tests/content-streaming.test.ts covers selective loading, alias reuse, rollback, malformed/corrupt responses, independently invalid model artifacts, cancellation, late completion and fetch/lease budgets. tests/content-http.test.ts uses real relay HTTP, published and development rooms, revision changes, restart, source fencing, a bounded request burst and complete CPU reconstruction of its fixture. tests/content-persistent.test.ts binds the client to real PostgreSQL admission in cooperative and trusted rooms; run it with node tools/cli/test-control.mjs tests/content-persistent.test.ts.
Lossless expansion, malformed descriptors, actual profile-3 compatibility, expanded reservation/cooling/preload limits and real binary HTTP delivery are covered by tests/content-compression.test.ts. Independent texture transport is covered by tests/content-textures.test.ts and the two-player worker/main texture journey. Published-player recovery and shared editor revision fixtures also use textured assets. See texture delivery acceptance.
Still required: progressive mip delivery, GPU-compressed texture variants and independently streamed detail variants, richer transport measurements, broader delivery/long-session fault coverage and physical-device qualification. Shared inventory, script and cross-place validation must retain whole-world invariants when definitions are admitted incrementally. The published browser fixture proves selective authored-model requests; it does not quantify production bandwidth or complete the streaming milestone.