World / Cloudflare
GitHub

Inside the World

Payloads and byte streams

Use values for control data and byte streams for bulk data. Offloading to R2 removes storage-message pressure, not every memory limit.

A value still has to fit in memory

Binary payload fields over 1 MiB go to R2 before the referencing event commits. That avoids putting a large value in a SQLite cell or a single RPC message. Reading it back still involves buffers, decoding and the VM’s representation.

SizeMeaning
1 MiBOffload threshold for large binary event values and stream chunks.
18 MiBEnforced serialized payload limit. This is not the same as 18 MiB of raw step input.
18,790,000 bytesRandom step output demonstrated on retained executors at 8cddcbd. Not a universal raw-value cap.
14,155,722 bytesRaw step-input boundary for the tested unencrypted byte-array shape; one byte more failed.
32 MiBMaximum encoded World stream chunk. Some probes encode raw bytes first.

At build 8cddcbd, the getWritable probe passed 25,165,800 raw bytes and rejected one byte more at 33,554,433 encoded bytes. Other framing or encryption can change the raw allowance. Returned byte streams have a different path.

Pass a handle, then move bytes incrementally

Producer step
Stream handle in result
RunStreams metadata
R2 chunks
Consumer step

A step returning a byte stream lets the SDK persist a handle rather than serialize all bytes as one return value. Large chunks reach R2. Readers stream them back in 1 MiB pieces instead of loading the whole stream.

The World’s bounded-retention test writes and reads 512 MiB while holding at most two write chunks or two read pieces. A real Workers probe transferred 1.75 GiB in 16 MiB producer chunks and checked every byte at build 8cddcbd.

The producer must outlive the step result

A returned stream can keep writing after the step’s function has returned. Ending its delivery too soon cancels that background work. The flow request therefore waits for tracked background writes, within a 13.5-minute budget from delivery start.

Returned-step streams renew a 30-second writer lease. If the writer stops and the lease expires, readers get a StreamError rather than waiting forever. Registration starts at the producer’s first attempted write. A producer that never attempts a write has no lease yet and is not detected. Named writable streams have different lifecycle rules; do not assume every stream uses that lease.

The current multi-GiB reader problem

In the installed SDK, flushablePipe races each small read against a long-lived closed promise. Reactions accumulate until close, retaining roughly 40 MiB per GiB read. At build 8cddcbd, a QuickJS step read 1.75 GiB correctly but a 2 GiB probe produced an executor exceededMemory event. The earlier 4 GiB failure is not a safe allowance.

The published SDK is not patched here. Keep one reading step’s volume bounded, or wait for an upstream fix before relying on multi-GiB reads. World-side chunk bounds do not repair retention in the consumer SDK.

Sources and implementation

Repository links may require access. Vercel links point to public source or published documentation. Logical API names do not establish backend internals.

Browse topics or type to search

IntroductionA Workflow SDK v5 World built on Cloudflare Workers, Durable Objects and R2. Here is what runs where, why each part exists, and what the measurements actually show.Same SDK, different infrastructureA workflow is a log you can replay.Three jobs, kept separateA workflow is a log you can replay.Recorded results, not promisesA workflow is a log you can replay.Read the system in orderA workflow is a log you can replay.The architectureAn ordered store for each run, delivery capacity shared across shards, and execution kept off the queue objects.Where everything livesThe architectureOrder state, distribute executionThe architectureRouting without a global directoryThe architectureWhat the design buys, and costsThe architectureLife of a requestFollow the ordinary static-workflow start path, then the deliveries that turn the stored intent into completed work.Our World: accepting the runLife of a requestOur World: delivering and runningLife of a requestVercel: the corresponding pathLife of a requestWhat a response does and does not guaranteeLife of a requestEvery service, explainedWhat each component owns, why it exists, how it helps, and the limit it introduces. These are logical roles, not twelve separate deployed services.Router Worker and World clientEvery service, explainedRun-store shardsEvery service, explainedQueue shardsEvery service, explainedWorkflow runners and step runnersEvery service, explainedConsumer and retained executor WorkersEvery service, explainedIndex shardsEvery service, explainedHook token registriesEvery service, explainedRun streams and VM snapshotsEvery service, explainedR2 payloads and dead lettersEvery service, explainedAlarms and cron recoveryEvery service, explainedState and deliveryHow an event becomes durable work, why the outbox exists, and where transactions stop.The event is the durable recordState and deliveryWhy commit an outbox?State and deliveryHook claims cross transaction boundariesState and deliveryAt-least-once means side effects can repeatState and deliveryRetries, limits and recoveryState and deliveryExecution and replayWorkflow decisions execute inside a deterministic runtime. Step bodies do the I/O outside it. QuickJS and the native engine trade replay work for memory and compatibility.Workflow code is not step codeExecution and replayQuickJS is our defaultExecution and replayThe native engine uses a build-time shimExecution and replayWhat the replay numbers meanExecution and replayShards and placementSpread independent runs, preserve old routing, and keep state near execution where possible. More shards are capacity, not free speed.Time picks the epoch, hash picks the shardShards and placementA location hint is a hintShards and placementWhy creation throughput is highShards and placementMany runs scale better than one enormous runShards and placementPayloads and byte streamsUse values for control data and byte streams for bulk data. Offloading to R2 removes storage-message pressure, not every memory limit.A value still has to fit in memoryPayloads and byte streamsPass a handle, then move bytes incrementallyPayloads and byte streamsThe producer must outlive the step resultPayloads and byte streamsThe current multi-GiB reader problemPayloads and byte streamsCompared with VercelTwo implementations of the same World contract. We can inspect Vercel’s public adapter, not the internals behind its managed APIs.The services Vercel’s public client usesCompared with VercelThe same jobs, different ownersCompared with VercelHow their transport worksCompared with VercelWhere our design helpsCompared with VercelRead the Vercel limits with their runtime contextCompared with VercelMeasurements and limitsRecorded Workers results, the workload behind each number, and the failures that keep these from being unlimited-capacity claims.Latest regression checkMeasurements and limitsCreation is not completionMeasurements and limitsExecution resultsMeasurements and limitsLimits are not all the same kind of numberMeasurements and limitsKnown failure pointsMeasurements and limitsRun the probesMeasurements and limitsIdentity and deploymentsBindings authorize internal access. Your application protects public routes. Encryption and retained release routing solve separate problems.Internal calls use capabilitiesIdentity and deploymentsEncryption has a narrower scope than “all data”Identity and deploymentsKeep the code that owns old historyIdentity and deploymentsHow Vercel’s identity differsIdentity and deploymentsBuild, deploy and operateThe repository root is an example application. The reusable World lives in packages/world-cloudflare. Deploy the minimal mode first, then add retained executors if you need them.Start with the exampleBuild, deploy and operateKnow which config changes affect old runsBuild, deploy and operateDeploy state first, then the retained executorBuild, deploy and operateTest the adapter, the SDK and the deploymentBuild, deploy and operateOperational work still leftBuild, deploy and operate
Local search. Nothing sent to a server.Esc to close
Documentation