Workers answer requests; Durable Objects own named state
Goal: decide where a piece of state lives on Cloudflare, and address it by name.¶
A Worker is Cloudflare’s program that answers requests. It runs your code in an isolate, a lightweight sandbox inside the Workers runtime. A Durable Object is a Worker with a globally unique name and its own storage. What are Durable Objects?¶
Why a Worker must not rely on module state
Module state can outlive a request, and that is the problem. Cloudflare lists three facts. How Workers works¶
- One instance may handle many requests, including concurrent ones. State one request writes, another request can read.¶
- An isolate can be evicted, for example when the host runs short of resources. Its state goes with it.¶
- Nothing guarantees that two requests reach the same instance of your Worker.¶
So Cloudflare recommends that you do not use or mutate global state.
rat-stack fences that advice with the rat-stack-patterns/no-module-level-mutable-state lint rule:¶
moduleState:
"Module-level let and var hold state that outlives one request. Every request a Worker isolate or server process handles shares it, so one request's state leaks into the next. Fix: keep the state in a `Ref` inside a Layer, a Durable Object, or the database.",The fix names three homes for state. This page covers the Durable Object.¶
What a Durable Object adds
Cloudflare lists these properties. What are Durable Objects?¶
- A name: you send a request to one specific object from anywhere.¶
- Private storage: transactional and strongly consistent, and no other object can read it.¶
- One thread: each object handles its work single-threaded, like code in a browser.¶
- Created on first use: you never create or destroy an object yourself.¶
- In-memory state resets: an idle object hibernates and loses memory. Persist anything you need later.¶
- Alarms: an object can schedule itself to wake up at a future time.¶
Worked example: one object per join submission
The join intake keeps each submission’s contact in its own Interest object.
The contact lives under one storage key:¶
export const JOIN_CONTACT_KEY = "join-contact";export const joinContactCell = (storage: JoinCellStorage) => {
const current = storage.get(JOIN_CONTACT_KEY).pipe(The Interest object hands the cell its own storage:¶
...joinContactCell({
delete: (key) => storage(state.storage.delete(key)),
get: (key) => storage(state.storage.get(key)),
put: (key, value) => storage(state.storage.put(key, value)),
}),The Worker binds the namespaces once, in its init phase:¶
const legacyMcp = yield* LegacyMcp;
const interests = yield* Interest;
const interestIndex = yield* InterestIndex;Then it picks an object by name for each request:¶
() => interestIndex.getByName("index").applicationIds(),
(actor) => interests.getByName(intakeInstance(actor))
),
contacts: (submissionId: string) =>
interests.getByName(`join:${submissionId}`),interests.getByName(`join:${submissionId}`)reaches the object for one submission. Every request about that submission reaches the same object.¶interestIndex.getByName("index")reaches a single index object that lists applications.¶
The name is the design decision. One name per submission keeps each record apart. One fixed name makes a single place that every request shares.¶
Choose the home
The lint fix names three homes: a Ref inside a Layer21, a Durable Object, or the database.
Pick a Durable Object when several clients must work through one named place. Cloudflare describes that use as coordination. What are Durable Objects?¶
Work that must finish after the response needs a recovery record too. See Request lifetime is not durability9.¶
Next
Read Queues, Pipelines, Workflows and K2 do different jobs8 for work that leaves the request. Then read Worker init runs twice8 for a boundary inside the Worker’s own startup.¶
Interest signup14 describes the whole join flow.¶
Sources
- How Workers works. Cloudflare. One instance may handle many requests, including concurrent ones; isolates can be evicted; requests may reach any instance; do not use or mutate global state. Accessed 2026-11-08.
- What are Durable Objects?. Cloudflare. A Worker with a globally unique name and private, strongly consistent storage; single-threaded; in-memory state resets on hibernation; alarms. Accessed 2026-11-08.
- rat-stack pattern lint rules. GitHub joelhooks/rat-stack. no-module-level-mutable-state and its fix message. Accessed 2026-11-08.
- Interest Durable Object. GitHub joelhooks/rat-stack. Backs the join contact cell with the object's own storage. Accessed 2026-11-08.
- Join contact cell. GitHub joelhooks/rat-stack. Stores one sealed contact under the join-contact key. Accessed 2026-11-08.
- Mischief Worker composition. GitHub joelhooks/rat-stack. Binds the Interest and InterestIndex namespaces and addresses objects with getByName. Accessed 2026-11-08.
Last changed 2026-11-08 in fa39fa0. Source on GitHub. Change log.
Linked from
- add-a-feature-flag. Declare a typed feature flag, its scoped evidence ladder, approval roles, removal date, and durable ledger adapter.
Each flag and scope routes to its own Durable Object.
- Agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
Pi Durable and a Cloudflare Container in one Durable Object, compacted together.”
- Agent rules for rat-stack (AGENTS.md). What you may change, which commands to run, and which changes need approval.
85 runs the Worker runtime tests
- Alchemy Domain models a business as commands, aggregates, and events. Alchemy Domain is a domain-driven design framework for Effect whose one model runs in memory for tests and on Cloudflare Durable Objects in production.
On Cloudflare, every aggregate instance, view key, and policy gets its own Durable Object, and the app declares none of them.
- Analytics. One event per HTTP request, stored unchanged for queries to select and interpret later.
withEventCapture wraps the Worker’s fetch handler.¶
- Auth. Email and password sign-in through Better Auth, with the signed-in person provided to each RPC call.
It calls the private LearnFeedbackAuth Worker through a Worker binding.
- Bindings. Sam Goodwin describes bindings as the place where runtime permissions and configuration meet infrastructure.
Goal: connect a Worker to a resource it declares, and see what it gets back.¶
- Build and run rat-stack (README.md). What is in the repo, how the example works, and how to run it.
Role: Private crash-trace archive for the Mischief Worker
- Build-time work stays out of requests. Git reads and highlighting produce static assets before the Worker handles requests.
The Worker reads that output through static assets.¶
- CAFE names the Cloudflare, Alchemy, Foldkit and Effect stack. CAFE is a named stack: Cloudflare runs the app, Alchemy declares it, Foldkit draws the UI, and Effect does the typed work.
The browser and the Worker import the same schemas.
- Change log. What changed in the files served here, newest first.
Workers answer requests; Durable Objects own named state — new page: Initial commit from joelhooks/rat-stack template
- Code snippets quote a pinned source. Shipped design brief: pinned Git objects, typed diagnostics, and one build-only snippet model for both views.
The Worker receives static assets.
- Concurrent work needs an owner and a bound. Limit active work, retain result order, and stop child work when its owning lifetime ends.
Bound worker concurrency and claim every upstream buffer is bounded too.¶
- Database. One interface records action runs and their outcomes in either Cloudflare D1 or Postgres through Hyperdrive.
Auth uses the selected database vendor in the web development backend and the optional private LearnFeedbackAuth Worker.
- durability. Keep a rat-stack app standing when a provider, pool, or dependency slows down or a worker dies. Use at any I/O boundary, connection pool, provider call, shared waiter, fan-out, flag, gate, or work that can outlive a request, or when asked "what happens when this is slow", "add a timeout", "make this durable", or "what survives a crash".
Failure prevented: A healthy restarted worker silently loses accepted work.¶
- Durability. Seven durable Effect patterns, each with its seam, its fence, and the incident that taught it.
The Worker sets it from DROVR_INTAKE_TIMEOUT_SECONDS.
- Durability bibliography. Books, checked short quotations, source limits, and Effect mapping provenance.
Big, Loud Worker-Bees¶
- Durability starts with a recovery record. A reading order for storage, replay, worker ownership, overload, and recovery tests.
Durable work needs storage: A restarted worker cannot recover intent that existed only in memory.¶
- Durable work needs storage. A restarted worker cannot recover intent that existed only in memory.
Choose what must survive termination and how the next worker finds it.¶
- Effect + Alchemy peer patterns. Source-grounded patterns from repos on nearby Effect and Alchemy pins.
These peer patterns guide the web client, Alchemy resources, database, Better Auth, Worker tests, and boundary enforcement.
- Effect + Alchemy peers. Public repositories that share rat-stack's prerelease lines.
Why this tier: Active Oct 27; studied scoped Drizzle, Worker tests, and vendor boundaries.
- Effect 4 study: September 2026. Dated Effect 4 source studies from September 2026; current versions live in pins.md.
For the browser client, Alchemy resources, database, auth, and Worker tests, see Effect + Alchemy peer patterns.
- Effect basics. An Effect describes work, expected failures, and required services before a runtime executes it.
The Worker returns a fetch Effect to its Alchemy Worker adapter:¶
- Effect is not BEAM. Fibers share a runtime; scope ownership does not supply isolated heaps or OTP restart strategies.
Supervision observes worker failure and applies restart policy.
- Every task needs an owner. Give child work a lifetime, a failure observer, and a recovery decision.
Managed worker abstractions make lifecycle policy visible.
- Feature flags. Typed feature flags with owners, removal dates, and stable subject rollouts.
The Worker reads its live value for each captured request.
- find-peers. Refresh public peers, exact stack versions, and tiers after stack bumps. On gardener passes, refresh peers checked more than 14 days ago without blocking other work.
Link context
Ask DeepWiki (the deepwiki MCP server) one narrow question tied to current work, such as “How does this repo bind a Durable Object to an Alchemy Worker?” Treat the answer as a pointer to files.¶
- Flags climb on evidence. A flag ladder connects each rollout step to scoped measurements, approval, and a durable receipt.
The Durable Object owns the current value and its receipt.
- Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
- Hosted agent interfaces. Discovery documents, HTTP, MCP, A2A, and bounded code mode expose the public reference to agents.
Older clients initialize sessions backed by a Durable Object, Cloudflare’s feature for persistent state and compute.¶
- Interest signup. Agent-only applications start email confirmation through interfaces owned by core and delivery implemented in a separate package.
The Worker chooses and supplies that implementation when it starts.¶
- Learn mode presents concepts at useful depths. An agent presents source-derived concepts during real work and keeps learning events in a private local log.
On adds Better Auth table migrations and the Worker bindings that use them.¶
- Learn the stack | rat-stack. Hands-on guides to Effect actions, XState lifecycles, and the seams between stack pieces.
durability — Keep a rat-stack app standing when a provider, pool, or dependency slows down or a worker dies.
- learn-alchemy. Learn how Alchemy 2 turns an Effect program into a planned Cloudflare deployment.
One program declares resources, bindings, deployment state, and the Worker that uses them.¶
- Lint and type escape ledger (debt.md). A source-linked count of repo-owned lint and type escapes.
Reason: the object shares its Worker’s environment, which Alchemy types as a dictionary.
- model-a-protocol. Check a protocol with a small TLA+ model before you build or change it. Use when steps can interleave or die halfway, such as a crash between two writes, a retry after an unknown outcome, an alarm or sweep that redrives work, or two writers on one record. Also use when asked "can this race?", "what if it dies here?", or "model this", or before a durability design review.
The Durable Object store writes a record and its sweep index in two calls.
- Motion. @rat-stack/motion gives Foldkit apps Penner and CSS easings, tweens, fixed-step springs and interpolation, held in the Model and checked by properties.
No Worker graph imports the package, so it adds no startup bytes.¶
- One program can replace several tool calls. Code mode combines typed content calls inside one bounded execute request.
ts runs a fresh Dynamic Worker, a Worker loaded to execute sandbox code, with:¶
- One prompt serves people and agents. Source-owned agent prompts with typed discovery and copyable browser pages.
Existing Worker infrastructure serves the assets.
- profile-a-worker. Capture a CPU or heap profile of a deployed Cloudflare Worker or Durable Object and read it with pprof. Use when a Worker is slow, burns CPU, nears its memory limit, throws Exceeded Memory, or you need to know which function costs the most in production.
Cloudflare can sample a deployed Worker or Durable Object while it serves traffic.
- query-design. Design or review SQL reads, writes, scans, backfills, and slow database paths. Use before changing a store method or any loop that queries. Covers round-trip arithmetic, batching, keyset paging, index evidence, row budgets, PlanetScale Vitess limits, Hyperdrive connections, statement-count tests, and statement-template fences.
Create the connection within the Worker request and reuse it for that request’s statements.
- Queues, Pipelines, Workflows and K2 do different jobs. Pick a Cloudflare product for work outside a request by the unit you track: a message, a row, a step, or a record in a log.
A queue holds messages for a consumer Worker.
- Rat Stack lore | rat-stack. Short, source-grounded notes on the ideas and decisions behind rat-stack.
Durability starts with a recovery record — A reading order for storage, replay, worker ownership, overload, and recovery tests.
- Rat Stack systems | rat-stack. The systems rat-stack ships, the standard each keeps, and how to check it.
Turbo remote cache — A separate Worker stores streamed Turbo artifacts in R2, with one credential per team and lifecycle expiry.
- rat-stack. An Effect stack so pure (aspirational) Kit Langton will blush.
Older clients get their own session in a Cloudflare Durable Object, which stores state between requests.
- Restart must reconstruct known state. Execution recovery and state recovery need separate contracts.
Letting a worker fail is useful when recovery returns it to a known state.
- Run Effect at the integration boundary. Build the runtime where a calling framework enters, reuse its services, and dispose it when that integration ends.
The basics page shows CLI, Worker and test execution boundaries.
- Schema projections: 2026-09-18 history. Historical Effect rc.115 proposal and implementation receipts from 2026-09-18; use the one-capability-every-surface lore page for the current pattern.
Runtimes are pluggable: QuickJS, Deno subprocess, Cloudflare dynamic worker, workerd subprocess.
- security-audit. Security guidance and vulnerability review for codebases, APIs, services, CLI tools, libraries, and daemons. Use for security questions, focused reviews, vulnerability research, security audits, or pen tests. Run the complete workflow only for explicit codebase audit or pen-test requests, full/comprehensive/end-to-end reviews, or requested report artifacts.
The Worker adapter is sandbox-worker-loader.ts.
- Shared caches hold completed values. Keep pending request I/O out of isolate-scoped caches.
A Worker isolate can serve several requests.
- Source change history (log.md). The generated change log as Markdown.
Workers answer requests; Durable Objects own named state — new page: Initial commit from joelhooks/rat-stack template
- The rat-stack bestiary eats excuses. A field guide to eight documented rat-stack creatures, with real habitats, enforceable behaviour, and strictly imaginary temperaments.
Worker names, resource IDs, and TypeScript symbols retain their names.
- The war stories. Seven public repo incidents: a deploy undoes itself, failure says 200, sessions forget their guests, and the rat tests the demolition instructions.
” Alchemy resource IDs, Worker names, and TypeScript symbols keep their names, so Mischief survives the move.¶
- There is no ISR on Cloudflare. SvelteKit's Vercel ISR setting does not configure Workers Cache; our local comparison did not prove cache reuse.
Workers Cache puts a cache in front of a Worker entrypoint.
- Trace operations where they mean something. Name useful operations, add safe context, and distinguish creating a span from delivering it to a backend.
D1 and Durable Object SQL: SQL text; Durable Object SQL also records query bindings¶
- Turbo remote cache. A separate Worker stores streamed Turbo artifacts in R2, with one credential per team and lifecycle expiry.
0 owns a separate Worker, a dedicated R2 bucket, and generated team credentials.
- uncomplect. Find responsibilities that a rat-stack design combines, separate them into capabilities, cartridges, machines, features, and clients, and enforce that separation through types and checks. Use when asked to simplify, review, or replace a design, "what would Rich Hickey do", "optimize for deletion", or "define this error out of existence".
Look for a cache or local copy of server data that decides a mutation independently of the Durable Object or database cartridge.¶
- Web. The Foldkit reader renders on the server and prerenders its public pages; the browser hydrates the same view.
Server rendering means the Worker builds the page HTML for each request.
- Worker init runs twice. Alchemy Worker initialization runs in deploy and runtime contexts, so resource declarations need a boundary.
It runs when Alchemy declares deploy-time resources and when the Worker starts in an isolate, a separate execution environment.
- write-a-wiki-page. Write or revise public wiki pages with short prose, cited claims, meaningful visuals, and pinned code snippets.
Keep it out of new Worker route handlers.¶
Unlinked mentions (32)
- add-a-feature-flag → The normal cloudflareFlagsLayer reads KV and never calls a Durable Object.¶
- Agent rules for rat-stack (AGENTS.md) → Role: Typed flags cartridge: scoped evidence ladders, Config and memory adapters, cartridge-owned Alchemy KV, and a default Durable Object ledger.
- Analytics → A Worker binding, a declared connection to a resource, sends the event to an unstructured Cloudflare Pipelines stream.¶
- Auth → That Worker owns Better Auth, the D1 database, and the signing secret.
- Build and run rat-stack (README.md) → Put a Worker or Deno runtime behind the same Sandbox service to isolate the network too.
- CAFE names the Cloudflare, Alchemy, Foldkit and Effect stack → The Worker serves an Effect HttpApi.
- durability → Use: A Durable Object adapter behind an Effect service¶
- Durability → The default operator adapter commits position, receipt and projection marker in one Durable Object transaction.¶
- Durability starts with a recovery record → Durable timers need recovery: Persist scheduled intent when losing a worker must not lose its deadlines.¶
- Durable work needs storage → Kill the worker after acceptance.
- Effect + Alchemy peer patterns → Durable Object tests give the class a narrow fake state backed by real node:sqlite, including SQL storage, alarms, and waitUntil; they exercise object behavior without pretending to be a full workerd runtime.
- Effect + Alchemy peers → Why this tier: Active Oct 24; studied worker readiness, authorization and reconciliation states.
- Effect basics → The Worker returns a fetch Effect to its Alchemy Worker adapter:¶
- Effect is not BEAM → Test a worker defect and a hard runtime exit separately.
- Feature flags → The default operator adapter uses one Durable Object per flag and scope.
- find-peers → Ask DeepWiki (the deepwiki MCP server) one narrow question tied to current work, such as “How does this repo bind a Durable Object to an Alchemy Worker?” Treat the answer as a pointer to files.¶
- Flags climb on evidence → Readers never call the Durable Object.
- Hosted agent interfaces → execute runs an async function body in a fresh Dynamic Worker, a Cloudflare Worker loaded at runtime.¶
- learn-alchemy → Rat-stack keeps its own store, the Worker alchemy-state-store-ratstack-state-store, which both entrypoints load from apps/infra/src/state-store.ts.
- Lint and type escape ledger (debt.md) → Reason: Alchemy’s RPC Worker initialization returns its request Effect.
- One program can replace several tool calls → ts runs a fresh Dynamic Worker, a Worker loaded to execute sandbox code, with:¶
- profile-a-worker → Cloudflare can sample a deployed Worker or Durable Object while it serves traffic.
- Queues, Pipelines, Workflows and K2 do different jobs → Cloudflare lists three uses: offload work from a request, send data from Worker to Worker, and buffer or batch writes.
- Restart must reconstruct known state → A fresh worker reuses inconsistent state or forgets critical coordination data.¶
- Schema projections: 2026-09-18 history → The MCP or OpenAPI schema becomes a typed TypeScript API with doc comments; the model writes TS against it; it runs in an isolated Worker with no network, only bindings.
- security-audit → Durable Object flag ledger: cloudflare-ledger.ts.¶
- Shared caches hold completed values → rat-stack-patterns/no-shared-pending-cache rejects these cache constructors in module, service, and Worker initialization effects.
- There is no ISR on Cloudflare → Its stale-while-revalidate behavior serves a stale response while the Worker refreshes it.¶
- Trace operations where they mean something → D1 and Durable Object SQL: SQL text; Durable Object SQL also records query bindings¶
- Turbo remote cache → The Worker streams those bodies to R2 and streams stored bodies back.
- Web → Foldkit deploys the outputs as one Worker.
- Worker init runs twice → On 2026-09-24, stage-specific rate-limit declarations used yield* Stage in the Worker initializer.