Cloudflare OS's runtime stacks Durable Objects, Dynamic Workers, and Facets into a layered architecture: each workspace is a Durable Object, each Gadget runs in a Dynamic Worker Facet, and Gatekeepers inject facets to mediate access to external services. The system uses Workers KV and R2 for persistent storage (blueprints, avatars, collections), injects bindings dynamically in dev and prod, and requires global_fetch_strictly_public in production to prevent SSRF via global fetch. A Durable Object provides a single-instance, stateful execution context with strongly consistent storage, guaranteeing that only one instance runs at a time and that reads always reflect the latest committed writes.
Cloudflare OS is built on Cloudflare Workers using Durable Objects, Dynamic Workers, and Facets: every workspace is its own Durable Object, every Gadget runs in a Dynamic Worker Facet, and Gatekeepers install facets into each workspace to manage access to remote services.[1]
Gatekeeper service bindings and the Workers AI binding are not declared in the base wrangler.jsonc; they are dynamically injected by run-dev-server.js for dev and generate-wrangler-prod.js for production.[2] All Durable Object classes (UserDurableObject, OverseerDurableObject, AdminSettings, PendingLogin) are reached via ctx.exports and require no explicit durable_objects binding in wrangler.jsonc.[2] Two KV namespace bindings are declared for workshop-backend: BLUEPRINTS (blueprint metadata, preview ID gadgets-blueprint-metadata) and AVATARS (user avatar images, preview ID gadgets-avatars).[2] Blueprint code snapshots are stored in an R2 bucket bound as BLUEPRINT_CONTENT (bucket name gadgets-blueprint-content).[2]
capnweb-validate was introduced to provide runtime RPC type validation for both the backend and gatekeepers; packages/workshop-backend/src/server.ts applies the @validateRpc() decorator to AuthenticatedApiImpl, enabling this validation for all authenticated API calls.[3][4] Structured logging for Workers Logs was introduced to replace unstructured log output from the backend.[5]
workshop-backend enables observability with full head sampling (head_sampling_rate: 1) for logs but disables invocation logs. Traces are enabled at 50% head sampling (head_sampling_rate: 0.5); a comment notes spans will bill against the Logs quota from 2026-10-01, so the sampling rate should be revisited before then.[2] The gatekeeper-context worker enables observability with full head sampling (head_sampling_rate: 1) but disables invocation logs; no trace sampling is configured for it.[6]
The global_fetch_strictly_public compatibility flag is required in wrangler.jsonc to prevent SSRF: without it, the webFetch agent tool (and global fetch()) can reach RFC1918/private-network addresses in production.[2] This flag only takes effect in production or when running workerd standalone; wrangler dev intentionally reconfigures its global outbound to permit fetching from any address (including localhost), making the flag a no-op during local development.[2]
The gatekeeper-context worker's main entry point is a generated file at .wrangler/validate/src/index.ts, produced by the build command pnpm exec capnweb-validate build --out .wrangler/validate, with src as the watch directory.[6] Four Durable Object classes are registered as new SQLite classes under migration tag v0 in packages/gatekeeper-context/wrangler.jsonc: ContextCollectionDurableObject, UserLibraryDurableObject, LibraryRegistryDurableObject, and ContextGatekeeper.[6] As with workshop-backend, Durable Object classes in gatekeeper-context are reached via ctx.exports, so no durable_objects binding entry is needed in that config.[6] gatekeeper-context binds a KV namespace named CONTEXT_COLLECTIONS (preview ID gadgets-context-collections) for public-collections snapshots.[6]
Product analytics is optional: deployments can bind PRODUCT_ANALYTICS to a Cloudflare Pipelines stream typed as Pipeline<ProductAnalyticsRecord>; local and dev configs omit it, and analytics no-op when the binding is absent.[7] Frontend error reporting requires both FRONTEND_ERROR_REPORTER (a Service<ErrorReporter> binding) and FRONTEND_ERROR_RATE_LIMITER (a RateLimit binding) to be present before error reports are dispatched.[7] The error-reporting package and workshop-frontend's errorReporting.ts attach the current route and authenticated user to every client-side error report sent to the backend; the useAuth hook exposes the signed-in user in the form required by the error reporter.
Cloudflare Access authentication is enabled by setting CF_ACCESS_AUD (audience) and CF_ACCESS_ISS (team URL, e.g. https://<team>.cloudflareaccess.com) in the environment.[7] The integration-test harness omits CF_ACCESS_AUD so /api takes the unauthenticated code path and password signup is available during tests.[8] AUTH_GATEKEEPERS=cloudflare,google,github allowlists which connected gatekeepers may be used for sign-in, showing a "Continue with …" button for each alongside username/password. Each auth gatekeeper OAuth app must be registered with a redirect URI of the form ${PUBLIC_BASE_URL}/gatekeeper/{github|google|cloudflare}/oauth.[9] DISABLE_PASSWORD_AUTH=true hides username/password login and signup, leaving gatekeeper sign-in only; this setting is ignored unless AUTH_GATEKEEPERS is non-empty, to avoid locking everyone out.[9][7]
ENABLE_CLOUDFLARE_LIMITS=true enables the free daily AI usage limit plus the Cloudflare-credits top-up flow; billing reads a token from the connected Cloudflare gatekeeper.[9] DAILY_LLM_CALL_LIMIT sets the per-user daily free-tier LLM-call limit as a string (defaulting to DEFAULT_DAILY_LLM_CALL_LIMIT), and MINIMUM_CLOUDFLARE_BALANCE sets the minimum connected-account balance in USD required to proceed via BYOK (defaulting to the MINIMUM_CLOUDFLARE_BALANCE constant).[7]
packages/workshop-backend/src/server.ts uses a module-level formatBlueprintInstallStarted flag to ensure the bundled format blueprints are only requested to install once per Worker instance; the AdminSettings Durable Object holds the authoritative answer.[4] SERVICE_SALT in packages/workshop-shared/src/api.ts is a fixed 16-byte Uint8Array used as part of the argon2id password hashing salt on the client side.[10] Durable Object resets are absorbed at the Worker layer in user.ts, server.ts, and do-telemetry.ts so that transient DO evictions do not propagate to the client as fatal failures; overseer.ts applies the same recovery to Overseer session capabilities. The mcp-shared package modules connection.ts, session.ts, client.ts, fetch.ts, and account.ts include hardened error handling and state management covering MCP connection establishment, session tracking, and request fetching. mcp-shared/__tests__/ contains test coverage for the hardened MCP lifecycle modules in mcp-shared. The reconnect probe handshake contract — used to test session liveness after device sleep or browser tab suspension — is implemented in packages/workshop-backend/src/server.ts and packages/workshop-shared/src/api.ts. packages/workshop-frontend detects tab-wake events in routes/__root.tsx and injects a reconnect probe into GadgetEditor.tsx and main.tsx; on visibility restore the probe tests WebSocket liveness and forces a clean reconnection when the session is stale. Crash-recovery logic in overseer.ts and packages/workshop-backend/src/agent.ts handles git-storage edge cases that could leave the system in an inconsistent state after a crash or interrupted write, allowing the Workshop to resume correctly after partial writes without manual intervention. Related crash-recovery and transactionality fixes also appear in workshop-shared/src/api.ts and workshop-shared/src/code-change.ts. The step-transactionality.md design document records the transactionality and crash-recovery approach; it is the reference for engineers working on storage durability or debugging state corruption in overseer.ts.
Sources
README.mdpackages/workshop-backend/wrangler.jsoncgithub.com/cloudflare/cloudflare-os/commit/1b92ab5packages/workshop-backend/src/server.tsgithub.com/cloudflare/cloudflare-os/commit/4b53133packages/gatekeeper-context/wrangler.jsoncpackages/workshop-backend/src/env.d.tspackages/integration-tests/src/harness.tsdocs/public-server.mdpackages/workshop-shared/src/api.ts