# Workers answer requests; Durable Objects own named state

> For agents: start with the [agent guide](https://ratstack.sh/llms.txt). Every page is Markdown by default; add `Accept: text/html` for HTML.

New on /news: [tla-ts 0.0.9 adds TLC's -continue](/news).

**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?](https://developers.cloudflare.com/durable-objects/concepts/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](https://developers.cloudflare.com/workers/reference/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`](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/scripts/oxlint-plugin-patterns.ts) lint rule:

scripts/oxlint-plugin-patterns.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 820-821; highlighted none. Title: no-module-level-mutable-state.

```typescript
      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?](https://developers.cloudflare.com/durable-objects/concepts/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:

apps/site/src/interest/join-contact-cell.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 3-3; highlighted none. Title: One key per object.

```typescript
export const JOIN_CONTACT_KEY = "join-contact";
```

apps/site/src/interest/join-contact-cell.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 24-25; highlighted none. Title: The cell reads that key from the storage it is given.

```typescript
export const joinContactCell = (storage: JoinCellStorage) => {
  const current = storage.get(JOIN_CONTACT_KEY).pipe(
```

The `Interest` object hands the cell its own storage:

apps/site/src/interest/interest-durable-object.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 118-122; highlighted none. Title: The cell is backed by the object's storage.

```typescript
        ...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:

apps/site/src/worker.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 386-388; highlighted none. Title: Bind the Durable Object namespaces.

```typescript
  const legacyMcp = yield* LegacyMcp;
  const interests = yield* Interest;
  const interestIndex = yield* InterestIndex;
```

Then it picks an object by name for each request:

apps/site/src/worker.ts at a3f5a14861ce89913e82bea0f7ba02c83e8d4725; lines 403-407; highlighted 407. Title: One object per submission; one object for the index.

```typescript
          () => 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 [Layer](/lore/layers-make-dependencies-explicit), 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?](https://developers.cloudflare.com/durable-objects/concepts/what-are-durable-objects/)

Work that must finish after the response needs a recovery record too. See [Request lifetime is not durability](/lore/request-lifetime-is-not-durability).

## Next

Read [Queues, Pipelines, Workflows and K2 do different jobs](/lore/queues-pipelines-workflows-and-k2-do-different-jobs) for work that leaves the request.
Then read [Worker init runs twice](/lore/init-runs-twice) for a boundary inside the Worker's own startup.

[Interest signup](/systems/interest) describes the whole join flow.

## Sources

1. [How Workers works](https://developers.cloudflare.com/workers/reference/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.

2. [What are Durable Objects?](https://developers.cloudflare.com/durable-objects/concepts/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.

3. [rat-stack pattern lint rules](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/scripts/oxlint-plugin-patterns.ts)
   GitHub joelhooks/rat-stack. no-module-level-mutable-state and its fix message. Accessed 2026-11-08.

4. [Interest Durable Object](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/apps/site/src/interest/interest-durable-object.ts)
   GitHub joelhooks/rat-stack. Backs the join contact cell with the object's own storage. Accessed 2026-11-08.

5. [Join contact cell](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/apps/site/src/interest/join-contact-cell.ts)
   GitHub joelhooks/rat-stack. Stores one sealed contact under the join-contact key. Accessed 2026-11-08.

6. [Mischief Worker composition](https://github.com/joelhooks/rat-stack/blob/a3f5a14861ce89913e82bea0f7ba02c83e8d4725/apps/site/src/worker.ts)
   GitHub joelhooks/rat-stack. Binds the Interest and InterestIndex namespaces and addresses objects with getByName. Accessed 2026-11-08.

## Linked from

- add-a-feature-flag → Declare a typed feature flag, its scoped evidence ladder, approval roles, removal date, and durable ledger adapter. → [Read page](https://ratstack.sh/skills/add-a-feature-flag)

- Agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms.txt)

- Agent rules for rat-stack (AGENTS.md) → What you may change, which commands to run, and which changes need approval. → [Read page](https://ratstack.sh/AGENTS.md)

- 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. → [Read page](https://ratstack.sh/lore/alchemy-domain)

- Analytics → One event per HTTP request, stored unchanged for queries to select and interpret later. → [Read page](https://ratstack.sh/systems/analytics)

- Auth → Email and password sign-in through Better Auth, with the signed-in person provided to each RPC call. → [Read page](https://ratstack.sh/systems/auth)

- Bindings → Sam Goodwin describes bindings as the place where runtime permissions and configuration meet infrastructure. → [Read page](https://ratstack.sh/lore/bindings)

- Build and run rat-stack (README.md) → What is in the repo, how the example works, and how to run it. → [Read page](https://ratstack.sh/README.md)

- Build-time work stays out of requests → Git reads and highlighting produce static assets before the Worker handles requests. → [Read page](https://ratstack.sh/lore/build-time-work-stays-out-of-requests)

- 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. → [Read page](https://ratstack.sh/lore/cafe-stack)

- Change log → What changed in the files served here, newest first. → [Read page](https://ratstack.sh/log)

- Code snippets quote a pinned source → Shipped design brief: pinned Git objects, typed diagnostics, and one build-only snippet model for both views. → [Read page](https://ratstack.sh/lore/code-snippets)

- Concurrent work needs an owner and a bound → Limit active work, retain result order, and stop child work when its owning lifetime ends. → [Read page](https://ratstack.sh/lore/concurrent-work-needs-an-owner)

- Database → One interface records action runs and their outcomes in either Cloudflare D1 or Postgres through Hyperdrive. → [Read page](https://ratstack.sh/systems/database)

- 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". → [Read page](https://ratstack.sh/skills/durability)

- Durability → Seven durable Effect patterns, each with its seam, its fence, and the incident that taught it. → [Read page](https://ratstack.sh/systems/durability)

- Durability bibliography → Books, checked short quotations, source limits, and Effect mapping provenance. → [Read page](https://ratstack.sh/lore/durability-bibliography)

- Durability starts with a recovery record → A reading order for storage, replay, worker ownership, overload, and recovery tests. → [Read page](https://ratstack.sh/lore/durability)

- Durable work needs storage → A restarted worker cannot recover intent that existed only in memory. → [Read page](https://ratstack.sh/lore/durable-work-needs-storage)

- Effect + Alchemy peer patterns → Source-grounded patterns from repos on nearby Effect and Alchemy pins. → [Read page](https://ratstack.sh/resources/same-version-repos)

- Effect + Alchemy peers → Public repositories that share rat-stack's prerelease lines. → [Read page](https://ratstack.sh/resources/peers)

- Effect 4 study: September 2026 → Dated Effect 4 source studies from September 2026; current versions live in pins.md. → [Read page](https://ratstack.sh/resources/effect-4-reference-projects)

- Effect basics → An Effect describes work, expected failures, and required services before a runtime executes it. → [Read page](https://ratstack.sh/lore/effect-basics)

- Effect is not BEAM → Fibers share a runtime; scope ownership does not supply isolated heaps or OTP restart strategies. → [Read page](https://ratstack.sh/lore/effect-is-not-beam)

- Every task needs an owner → Give child work a lifetime, a failure observer, and a recovery decision. → [Read page](https://ratstack.sh/lore/every-task-needs-an-owner)

- Feature flags → Typed feature flags with owners, removal dates, and stable subject rollouts. → [Read page](https://ratstack.sh/systems/flags)

- 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. → [Read page](https://ratstack.sh/skills/find-peers)

- Flags climb on evidence → A flag ladder connects each rollout step to scoped measurements, approval, and a durable receipt. → [Read page](https://ratstack.sh/lore/flags-climb-on-evidence)

- Full agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms-full.txt)

- Hosted agent interfaces → Discovery documents, HTTP, MCP, A2A, and bounded code mode expose the public reference to agents. → [Read page](https://ratstack.sh/systems/agent-front-door)

- Interest signup → Agent-only applications start email confirmation through interfaces owned by core and delivery implemented in a separate package. → [Read page](https://ratstack.sh/systems/interest)

- 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. → [Read page](https://ratstack.sh/systems/learn)

- Learn the stack | rat-stack → Hands-on guides to Effect actions, XState lifecycles, and the seams between stack pieces. → [Read page](https://ratstack.sh/skills)

- learn-alchemy → Learn how Alchemy 2 turns an Effect program into a planned Cloudflare deployment. → [Read page](https://ratstack.sh/skills/learn-alchemy)

- Lint and type escape ledger (debt.md) → A source-linked count of repo-owned lint and type escapes. → [Read page](https://ratstack.sh/debt.md)

- 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. → [Read page](https://ratstack.sh/skills/model-a-protocol)

- 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. → [Read page](https://ratstack.sh/systems/motion)

- One program can replace several tool calls → Code mode combines typed content calls inside one bounded execute request. → [Read page](https://ratstack.sh/lore/one-program-can-replace-several-tool-calls)

- One prompt serves people and agents → Source-owned agent prompts with typed discovery and copyable browser pages. → [Read page](https://ratstack.sh/systems/prompts)

- 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. → [Read page](https://ratstack.sh/skills/profile-a-worker)

- Purpose and boundaries of rat-stack (VISION.md) → What this starter is for and what a useful copy should keep. → [Read page](https://ratstack.sh/VISION.md)

- 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. → [Read page](https://ratstack.sh/skills/query-design)

- 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. → [Read page](https://ratstack.sh/lore/queues-pipelines-workflows-and-k2-do-different-jobs)

- Rat Stack lore | rat-stack → Short, source-grounded notes on the ideas and decisions behind rat-stack. → [Read page](https://ratstack.sh/lore)

- Rat Stack systems | rat-stack → The systems rat-stack ships, the standard each keeps, and how to check it. → [Read page](https://ratstack.sh/systems)

- rat-stack → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/)

- Restart must reconstruct known state → Execution recovery and state recovery need separate contracts. → [Read page](https://ratstack.sh/lore/restart-from-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. → [Read page](https://ratstack.sh/lore/run-effect-at-the-boundary)

- 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. → [Read page](https://ratstack.sh/resources/schema-projections-and-code-mode)

- 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. → [Read page](https://ratstack.sh/skills/security-audit)

- Shared caches hold completed values → Keep pending request I/O out of isolate-scoped caches. → [Read page](https://ratstack.sh/lore/cache-completed-values)

- Source change history (log.md) → The generated change log as Markdown. → [Read page](https://ratstack.sh/log.md)

- The rat-stack bestiary eats excuses → A field guide to eight documented rat-stack creatures, with real habitats, enforceable behaviour, and strictly imaginary temperaments. → [Read page](https://ratstack.sh/lore/shitrat-bestiary)

- 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. → [Read page](https://ratstack.sh/lore/shitrat-war-stories)

- There is no ISR on Cloudflare → SvelteKit's Vercel ISR setting does not configure Workers Cache; our local comparison did not prove cache reuse. → [Read page](https://ratstack.sh/lore/there-is-no-isr-on-cloudflare)

- Trace operations where they mean something → Name useful operations, add safe context, and distinguish creating a span from delivering it to a backend. → [Read page](https://ratstack.sh/lore/trace-meaningful-operations)

- Turbo remote cache → A separate Worker stores streamed Turbo artifacts in R2, with one credential per team and lifecycle expiry. → [Read page](https://ratstack.sh/systems/turbo-cache)

- 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". → [Read page](https://ratstack.sh/skills/uncomplect)

- Web → The Foldkit reader renders on the server and prerenders its public pages; the browser hydrates the same view. → [Read page](https://ratstack.sh/systems/web)

- Worker init runs twice → Alchemy Worker initialization runs in deploy and runtime contexts, so resource declarations need a boundary. → [Read page](https://ratstack.sh/lore/init-runs-twice)

- write-a-wiki-page → Write or revise public wiki pages with short prose, cited claims, meaningful visuals, and pinned code snippets. → [Read page](https://ratstack.sh/skills/write-a-wiki-page)

## Unlinked mentions

- [add-a-feature-flag](https://ratstack.sh/skills/add-a-feature-flag) → The normal cloudflareFlagsLayer reads KV and never calls a Durable Object.¶

- [Agent rules for rat-stack (AGENTS.md)](https://ratstack.sh/AGENTS.md) → Role: Typed flags cartridge: scoped evidence ladders, Config and memory adapters, cartridge-owned Alchemy KV, and a default Durable Object ledger.

- [Analytics](https://ratstack.sh/systems/analytics) → A Worker binding, a declared connection to a resource, sends the event to an unstructured Cloudflare Pipelines stream.¶

- [Auth](https://ratstack.sh/systems/auth) → That Worker owns Better Auth, the D1 database, and the signing secret.

- [Build and run rat-stack (README.md)](https://ratstack.sh/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](https://ratstack.sh/lore/cafe-stack) → The Worker serves an Effect HttpApi.

- [durability](https://ratstack.sh/skills/durability) → Use: A Durable Object adapter behind an Effect service¶

- [Durability](https://ratstack.sh/systems/durability) → The default operator adapter commits position, receipt and projection marker in one Durable Object transaction.¶

- [Durability starts with a recovery record](https://ratstack.sh/lore/durability) → Durable timers need recovery: Persist scheduled intent when losing a worker must not lose its deadlines.¶

- [Durable work needs storage](https://ratstack.sh/lore/durable-work-needs-storage) → Kill the worker after acceptance.

- [Effect + Alchemy peer patterns](https://ratstack.sh/resources/same-version-repos) → 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](https://ratstack.sh/resources/peers) → Why this tier: Active Oct 24; studied worker readiness, authorization and reconciliation states.

- [Effect basics](https://ratstack.sh/lore/effect-basics) → The Worker returns a fetch Effect to its Alchemy Worker adapter:¶

- [Effect is not BEAM](https://ratstack.sh/lore/effect-is-not-beam) → Test a worker defect and a hard runtime exit separately.

- [Feature flags](https://ratstack.sh/systems/flags) → The default operator adapter uses one Durable Object per flag and scope.

- [find-peers](https://ratstack.sh/skills/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](https://ratstack.sh/lore/flags-climb-on-evidence) → Readers never call the Durable Object.

- [Hosted agent interfaces](https://ratstack.sh/systems/agent-front-door) → execute runs an async function body in a fresh Dynamic Worker, a Cloudflare Worker loaded at runtime.¶

- [learn-alchemy](https://ratstack.sh/skills/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)](https://ratstack.sh/debt.md) → Reason: Alchemy’s RPC Worker initialization returns its request Effect.

- [One program can replace several tool calls](https://ratstack.sh/lore/one-program-can-replace-several-tool-calls) → ts runs a fresh Dynamic Worker, a Worker loaded to execute sandbox code, with:¶

- [profile-a-worker](https://ratstack.sh/skills/profile-a-worker) → Cloudflare can sample a deployed Worker or Durable Object while it serves traffic.

- [Queues, Pipelines, Workflows and K2 do different jobs](https://ratstack.sh/lore/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](https://ratstack.sh/lore/restart-from-known-state) → A fresh worker reuses inconsistent state or forgets critical coordination data.¶

- [Schema projections: 2026-09-18 history](https://ratstack.sh/resources/schema-projections-and-code-mode) → 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](https://ratstack.sh/skills/security-audit) → Durable Object flag ledger: cloudflare-ledger.ts.¶

- [Shared caches hold completed values](https://ratstack.sh/lore/cache-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](https://ratstack.sh/lore/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](https://ratstack.sh/lore/trace-meaningful-operations) → D1 and Durable Object SQL: SQL text; Durable Object SQL also records query bindings¶

- [Turbo remote cache](https://ratstack.sh/systems/turbo-cache) → The Worker streams those bodies to R2 and streams stored bodies back.

- [Web](https://ratstack.sh/systems/web) → Foldkit deploys the outputs as one Worker.

- [Worker init runs twice](https://ratstack.sh/lore/init-runs-twice) → On 2026-09-24, stage-specific rate-limit declarations used yield\* Stage in the Worker initializer.
