World / Cloudflare
GitHub

Inside the World

Every service, explained

What 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 client

Worker · src/dispatcher.ts · src/router.ts · world.ts

Owns
The example’s HTTP API and the adapter through which SDK calls find their objects. The client itself is a library running wherever the caller runs.
Why needed
Your app needs a front door for starts, results and hook resumes. Core needs an implementation of the World interface.
How it helps
The dispatcher keeps the workflow bundle out of the public front door. Stateless requests can scale independently from stateful execution.
Trade-off
The example’s public routes are not an application auth system. Add authorization, tenant isolation and rate limits appropriate to your app.

Run-store shards

WorldStore · WORLD_STORE · store-<n>

Owns
Many runs’ append-only events, current run/step/hook/wait rows, and outboxes. Each run stays on one store.
Why needed
Replay needs an ordered, durable history. Checking a transition and recording it must agree.
How it helps
One SQLite transaction writes an event and the rows it changes. Sharing warm stores avoids a new store object’s first-touch cost for every run.
Trade-off
One store is still finite. Other runs share it, and a giant fan-out still brings all outcomes back to that run’s store.

Queue shards

QueueShard · QUEUE_SHARD · flow-<n>

Owns
Persistent messages, due times, deduplication, in-flight recovery, hold leases and dead-letter handling.
Why needed
A start, sleep or retry must survive the request ending and the object restarting.
How it helps
Separate replay and step lanes keep long orchestrator deliveries from occupying the step lane. Starts route by run; dispatched steps spread by key.
Trade-off
This is our queue on SQLite and the durability library, not managed Cloudflare Queues. We own its correctness, tuning and recovery behavior.

Workflow runners and step runners

Runner · RUNNER · runner-<n> / runner-steps-<n>

Owns
Delivery execution outside the queue object. Workflow runners serialize replays per run; step runners serve a queue shard’s non-replay deliveries.
Why needed
Queue bookkeeping must keep progressing while workflow code consumes CPU or waits on I/O.
How it helps
A run hashes to a shared workflow runner. The store first touches it to favor proximity. Queue alarms establish runner stubs with a fresh subrequest chain.
Trade-off
Runners are shared, not a dedicated object per run. Placement is best-effort, and the extra hops still cost latency.

Consumer and retained executor Workers

QueueConsumer · CONSUMER · EXECUTORS · EXECUTOR_ROUTES

Owns
The consumer constructs a flow request and selects the release. The executor contains the built workflow bundle and SDK runtime.
Why needed
A new deployment should not make an old event log replay through different workflow code.
How it helps
With Workers for Platforms, the deployment ID maps to a retained script. Each release can keep serving its queued runs while new ones use the current release.
Trade-off
You must keep scripts and routes until their runs drain, and preserve RPC compatibility. One unencrypted QuickJS test kept an old waiting run on A while B added 1000 to its result. Arbitrary upgrades still need review. Worker isolates may handle multiple requests.

Index shards

WorldStore · WORLD_STORE · index-<n>

Owns
Compact listing rows for runs and hooks across the store shards.
Why needed
A normal list request should not scan every run store.
How it helps
Store outboxes push row updates. Creation need not wait for the index round trip. Incomplete indexes request missing reports and use fallback reads until backfill is ready.
Trade-off
Listings are eventually consistent. Hook-changing and terminal event writes wait for indexing or a one-second bound, not unconditional visibility. Forced-disposal callbacks schedule the victim’s update without waiting.

Hook token registries

WorldStore · first-epoch store shards · hash(token)

Owns
The token-to-run mapping used when a caller resumes a hook without knowing its run ID.
Why needed
A hook token has no ULID timestamp from which to derive its run’s epoch.
How it helps
One deterministic token lookup replaces searching all stores. Run stores persist accepted claim intents; token shards retain decisions and reserve pending takeovers so resets resume the same operation.
Trade-off
Tokens stay in first-epoch registries even when new epochs add run capacity. Cross-shard claims have separate transactions. An admitted claim can delay its run’s later writes during a peer outage, and compact receipts remain stored.

Run streams and VM snapshots

RunStreams · RUN_STREAMS · named by run ID

Owns
A run’s stream metadata, chunk references and latest VM snapshot. Unlike stores and runners, this object is per run.
Why needed
Large data must cross steps without becoming one huge event value. QuickJS also needs somewhere to persist VM state.
How it helps
Large chunks are offloaded to R2. Reads are piped in 1 MiB pieces. Snapshots are split into storage-sized parts. After a producer attempts its first write, returned-step streams renew a writer lease so its loss ends readers with an error.
Trade-off
The cap is 32 MiB per chunk, not unlimited memory. SDK 5.5.0 still retains memory while reading several GiB in one step.

R2 payloads and dead letters

PAYLOADS and DLQ buckets

Owns
Payloads and stream chunks over 1 MiB, plus exhausted queue deliveries in the separate dead-letter bucket.
Why needed
Object storage keeps big byte arrays out of SQLite values and RPC messages. Poison messages need an inspectable destination.
How it helps
A large value is uploaded before its event references it. A dead letter preserves delivery information for diagnosis and supported redrive paths.
Trade-off
R2 and SQLite are not one transaction. Failed writes can leave orphaned objects. Automated orphan and finished-run cleanup is not implemented.

Alarms and cron recovery

Durable Object alarms · scheduled Worker handler

Owns
Queue due times, outbox retries and periodic queue-shard wakes.
Why needed
No caller may be around when delayed work becomes due or a shard needs recovery.
How it helps
Alarms drive normal persisted work. A minute cron wakes queue shards in bounded slices as a recovery backstop.
Trade-off
Scheduled time is not a strict wall-clock execution guarantee. Treat sleeps and retries as “not before,” with operational delay possible.

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