World / Cloudflare
GitHub

Inside the World

Execution and replay

Workflow 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.

Replay work, illustrated
QuickJSRestore snapshot + read new events

Snapshot boundary ↑. Earlier decisions are already in the saved VM.

Native engineReplay recorded history

The body of a completed step is not rerun. Core supplies its saved result.

Conceptual work, not a timing scale. Snapshot availability and compatibility determine whether restore is possible.

Both engines consume the same durable event model. They do not turn a step into an exactly-once operation, and neither gives each request its own Workers isolate.

Workflow code is not step code

CodeWhere it runsWhat it should do
Workflow functionInside the workflow runtime.Make replayable decisions, await steps, hooks and sleeps.
Step bodyOrdinary host code outside the workflow VM.Call APIs, use bindings, process streams and perform external work.
World methodsAdapter code calling storage and delivery resources.Implement the persistence and transport contract core expects.

Core substitutes recorded outcomes when replay reaches completed steps. That avoids repeating the body just to rebuild decisions. A new attempt without a durable outcome can still run the body again.

QuickJS is our default

QuickJS runs in WebAssembly with its own VM heap. Core can capture that heap at an eligible suspension after enough events have been processed. The default threshold is 1,000 events; this is not a synchronous snapshot on every thousandth write.

A later delivery restores a compatible snapshot and consumes the event tail. The log remains the source of truth if no usable snapshot exists. Saved snapshots need encryption unless the deployment explicitly permits unencrypted ones.

The snapshot codec caps plaintext capture at 32 MiB. Snapshot eligibility and compatibility still depend on SDK policy, encryption and the workflow build.

The native engine uses a build-time shim

Workers do not provide Node’s node:vm. Our build transforms the workflow bundle into a factory and supplies the VM API core expects. Each replay receives its own deterministic Date, Intl and crypto context; most shared intrinsics are frozen.

Error.stackTraceLimit and Error.prepareStackTrace remain writable for host libraries. Changes affect other runs and host requests in the same isolate, even across awaits. Do not change them in workflow code. Use QuickJS when code needs independent built-ins.

This is not an untrusted-code sandbox. Dynamic source requires QuickJS. The native path does not persist VM snapshots, so more history means more replay work.

What the replay numbers mean

The recorded 10k-step probe measured 185 ms for QuickJS and 3.9 seconds for native on build adff891. The metric is the event-time gap from a completed sleep to creation of the next step, not isolated VM CPU time.

The workflow first has to execute those 10k steps. A small replay gap does not mean the whole run took milliseconds. Store/runner placement, RPC time, competing load and whether a snapshot exists all change the result.

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