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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- packages/world-cloudflare/src/types.ts
- packages/world-cloudflare/src/lib/shards.ts
- packages/world-cloudflare/src/runner/runner.ts
- packages/world-cloudflare/src/store/context.ts
- packages/world-cloudflare/src/streams/run-streams.ts
Repository links may require access. Vercel links point to public source or published documentation. Logical API names do not establish backend internals.