Understanding Durable Objects in Cloudflare OS Gadgets: Architecture and Implementation
Durable Objects serve as the fundamental stateful compute primitive in Cloudflare OS Gadgets, providing persistent storage, capability-based isolation, and type-safe cross-worker RPC for every long-living entity in the platform.
Cloudflare OS Gadgets is an open-source framework for building distributed applications on Cloudflare's edge network. According to the cloudflare/cloudflare-os repository, Durable Objects form the architectural foundation of this system, powering user accounts, administrative settings, and scheduled workflows. This analysis explores the specific implementation patterns found in the source code, from the UserDurableObject class in packages/workshop-backend/src/user.ts to the facet-based concurrency in packages/gatekeeper-scheduler/src/schedule-driver.ts.
Persistent State Management in User Accounts
In Cloudflare OS Gadgets, each user is backed by a dedicated Durable Object that owns a DurableObjectStorage instance. This storage survives worker restarts and can be queried from any worker within the same deployment, ensuring credentials and preferences remain available across edge locations.
The UserDurableObject class in packages/workshop-backend/src/user.ts demonstrates this pattern by storing credentials, preferences, and capability records. Similarly, the UserAccount implementation in packages/gatekeeper-google/src/google.ts leverages this durability to ensure OAuth tokens and user metadata persist through worker upgrades or crashes.
Capability-Based Security Isolation
Durable Objects enforce strict security boundaries through unique identifiers and isolated storage namespaces. Each DO receives a distinct DurableObjectId, guaranteeing that data cannot be accessed by other users or workers without an explicit capability grant.
The administrative layer relies on this isolation. The AdminSettings class in packages/workshop-backend/src/admin-settings.ts stores deployment-wide configuration behind a durable capability obtained via AuthenticatedApi.getAdminApi(). Likewise, the OverseerDurableObject in packages/workshop-backend/src/overseer.ts protects global workspace state, orchestrating gadget execution while maintaining security boundaries between tenants.
Cross-Worker RPC Communication
Cloudflare OS Gadgets utilizes the Cap'n Web RPC system to route method calls to Durable Object instances. This mechanism automatically handles promise pipelining and stub disposal, allowing agents and frontends to interact with DOs through well-typed interfaces rather than raw storage operations.
The ContextCollectionDurableObject in packages/gatekeeper-context/src/context-collection.ts exposes RPC-typed sessions that agents consume via methods like session = await gatekeeper.getSession(). This pattern ensures that the admin API, agent code, and frontend UI all communicate through consistent, type-safe stubs defined in the abstract DurableObject base class (see packages/workshop-backend/worker-configuration.d.ts).
Scalable State Sharing with Facets
For fine-grained concurrency within a single Durable Object, the platform implements facets—named sub-objects that function as micro-services sharing the same DO context. This pattern allows different logical components to maintain isolated state while benefiting from the DO's durability guarantees.
The scheduler gatekeeper demonstrates this approach in packages/gatekeeper-scheduler/src/schedule-driver.ts. The ScheduleDriver class registers persistent callbacks for workspace scheduling, while individual facets provide per-schedule isolation. This design enables complex, concurrent workflows without spawning separate DO instances.
Implementation Examples from the Source Code
The following patterns illustrate how Durable Objects are defined and consumed throughout the cloudflare/cloudflare-os repository.
Defining a Basic Durable Object
The ExampleDurableObject class in packages/gatekeeper-example/src/example-do.ts shows the minimal implementation required to extend the base DurableObject class and interact with persistent storage:
export class ExampleDurableObject extends DurableObject<Cloudflare.Env> {
async fetch(request: Request) {
const value = await this.ctx.storage.get<string>("greeting");
return new Response(value ?? "Hello, world!");
}
async setGreeting(greeting: string) {
await this.ctx.storage.put("greeting", greeting);
}
}
This class uses the ctx.storage API to read and write values that persist across requests and worker restarts.
Accessing a DO from a Worker
Workers obtain RPC stubs to Durable Objects through the namespace's get method. The UserDurableObject implementation demonstrates this pattern:
export class UserDurableObject extends DurableObject<Cloudflare.Env> {
// ...implementation...
}
// Somewhere in a request handler
const userDO = env.UserDurableObject.get(env.UserDurableObject.idFromName(userId));
const stub = await userDO; // RPC stub
await stub.setPreference({ theme: "dark" }); // Calls a DO method
The idFromName method generates a deterministic identifier from a string, ensuring the same DO instance is retrieved across different worker invocations.
Fetching Gatekeeper Sessions
Agents consume Durable Objects through session stubs provided by gatekeeper implementations. The frontend session management code illustrates this interaction:
import { getGatekeeperClassFor } from "packages/workshop-backend/src/user.ts";
async function obtainSession(env) {
const GatekeeperClass = await getGatekeeperClassFor("GATEKEEPER_CONTEXT");
const gatekeeper = env.GATEKEEPER_CONTEXT.get(env.GATEKEEPER_CONTEXT.idFromName("default"));
const session = await gatekeeper.getSession(); // Returns a typed session stub
return session;
}
This pattern enforces the "ambient capability" model, where access to DO functionality is granted through typed sessions rather than direct storage manipulation.
Implementing Facets for Concurrency
The ScheduleDriver class demonstrates advanced facet usage for isolating schedule-specific logic:
export class ScheduleDriver extends DurableObject<Cloudflare.Env> {
async schedule(name: string, cron: string) {
const facet = this.ctx.facets.get("ScheduleFacet", () => ({
id: undefined,
class: ScheduleFacet,
}));
await facet.add(name, cron);
}
}
Facets allow the scheduler to maintain multiple isolated callback registries within a single Durable Object, optimizing resource usage while preserving logical separation.
Key Architectural Files
The following files define the core Durable Object infrastructure in Cloudflare OS Gadgets:
-
packages/workshop-backend/worker-configuration.d.ts– Defines TypeScript typings forDurableObject,DurableObjectNamespace, and RPC helpers. -
packages/workshop-backend/src/user.ts– ImplementsUserDurableObject, the central example of per-user persistent state management. -
packages/workshop-backend/src/overseer.ts– Contains theOverseerDurableObjectthat orchestrates gadget execution and maintains global workspace state. -
packages/workshop-backend/src/admin-settings.ts– Stores deployment-wide admin configuration behind a durable capability check. -
packages/gatekeeper-context/src/context-collection.ts– Provides the DO-backed storage layer for the Context gatekeeper's collections. -
packages/gatekeeper-context/src/user-library.ts– Manages per-user private collections via dedicated DO instances. -
packages/gatekeeper-context/src/registry-do.ts– Registers public collections across deployments as read-only Durable Objects. -
packages/gatekeeper-scheduler/src/schedule-driver.ts– Implements persistent scheduling callbacks using DO facets.
Summary
- Durable Objects provide the fundamental stateful compute layer for Cloudflare OS Gadgets, handling everything from user authentication to administrative configuration.
- Each DO maintains isolated storage through unique
DurableObjectIdinstances andDurableObjectStorageAPIs that survive worker restarts. - Capability-based security enforces boundaries between users and admin functions, with sensitive operations protected behind explicit capability grants in files like
admin-settings.ts. - Type-safe RPC communication enables cross-worker interaction through Cap'n Web RPC stubs, exposed via methods like
getSession()in gatekeeper implementations. - Facets enable fine-grained concurrency within single DO instances, as demonstrated by the
ScheduleDriver's per-schedule isolation pattern.
Frequently Asked Questions
What is the relationship between Durable Objects and gatekeepers in Cloudflare OS?
Gatekeepers are logical security boundaries or service interfaces that are implemented as Durable Objects. Each gatekeeper defines one or more DO classes—such as ContextCollectionDurableObject or ScheduleDriver—that model specific entities. The Durable Object provides the persistent state and RPC interface that the gatekeeper exposes to agents and the frontend UI.
How does data persistence work across worker restarts?
Durable Objects in Cloudflare OS use the DurableObjectStorage API, which automatically replicates state across Cloudflare's edge datacenters. When a worker restarts or crashes, the DO's storage remains intact because it is maintained separately from the worker process. Files like packages/gatekeeper-google/src/google.ts demonstrate how OAuth credentials persist through these events.
What role do facets play in Durable Object architecture?
Facets are named sub-objects within a single Durable Object that act like independent micro-services. They allow the system to isolate different logical contexts—such as individual schedules in packages/gatekeeper-scheduler/src/schedule-driver.ts—while sharing the same DO's durability and resource pool. This pattern reduces overhead compared to spawning separate DO instances for each isolated context.
How is security enforced between different Durable Objects?
Security relies on capability-based isolation where each DO has a unique identifier and storage namespace. Data cannot be accessed without possessing the specific stub or capability reference. The AuthenticatedApi.getAdminApi() method in packages/workshop-backend/src/admin-settings.ts exemplifies this by requiring explicit capability acquisition before allowing access to deployment-wide settings.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →