How Cloudflare OS Handles Security with Gatekeepers: A Capability-Based Defense Model
Cloudflare OS isolates every external service integration in dedicated Gatekeeper workers that enforce capability-based security, admin-controlled enablement, and strict OAuth secret handling to ensure the Workshop backend never trusts raw data from third-party services.
Cloudflare OS uses Gatekeepers to securely broker access to external APIs like GitHub or Gmail according to the cloudflare/cloudflare-os source code. Each Gatekeeper is a standalone Cloudflare Worker bound to the Workshop backend via service bindings (e.g., GATEKEEPER_GITHUB), creating a defense-in-depth model where capabilities—not raw credentials—flow across the RPC boundary.
The Gatekeeper Security Architecture
A Gatekeeper is a standalone Cloudflare Worker dedicated to a single external service integration. The Workshop backend communicates with Gatekeepers through service bindings defined at deployment time. This architecture ensures that external service logic never runs inside the Workshop's privilege context; instead, the Workshop receives only typed capability handles that represent specific, revocable actions on remote resources.
Three Pillars of Gatekeeper Security
Capability-Based Access Control
The security model centers on fine-grained capabilities—typed RPC objects that represent exact actions a Gadget or the Workshop may perform. The root RPC interface is defined in packages/workshop-shared/src/gatekeeper.ts, where every method returns an RpcStub that callers use to interact with resources.
Capabilities are object-oriented and bounded: each capability ties to a concrete resource URL and can be revoked by the Gatekeeper at any time. This design ensures that even if a capability handle leaks, it grants only the specific permissions it was minted for, and only on the specific resource it targets.
Admin-Controlled Enablement
The Workshop's administrative configuration controls which Gatekeepers are available and whether they auto-provision. Admin settings live in packages/workshop-backend/src/admin-config.ts.
Before exposing a Gatekeeper class, the system validates the request through User.getGatekeeperClassFor() in packages/workshop-backend/src/user.ts. This function checks the deployment-wide AdminConfig and refuses access if the Gatekeeper is disabled or if the user lacks required OAuth scopes. This prevents instantiation of disabled Gatekeepers even if a client knows the correct URL.
OAuth and Secret Management
Gatekeepers requiring third-party OAuth obtain credentials from deployment-time environment variables (e.g., GITHUB_CLIENT_ID). The dev server in scripts/run-dev-server.ts injects these secrets into each Gatekeeper's wrangler.dev.jsonc configuration.
Critically, secrets never travel over the RPC channel. The Workshop receives only a capability handle after the OAuth flow completes within the isolated Gatekeeper worker.
Security Enforcement Points
Cloudflare OS implements multiple enforcement layers to prevent abuse:
- Binding Expansion Protection: The router only routes
/gatekeeper/<slug>/*to bindings that actually exist, preventing attackers from guessing non-existent Gatekeeper URLs. This logic resides inscripts/run-dev-server.ts. - Admin-Level Gating: The
User.getGatekeeperClassForfunction inpackages/workshop-backend/src/user.tschecksAdminConfigbefore returning a Gatekeeper class, guaranteeing that disabled Gatekeepers cannot be instantiated. - Capability Caps: The Workshop clamps catalog or data returned by Gatekeepers using functions like
boundAgentCataloginpackages/workshop-shared/src/gatekeeper.ts. This prevents malicious Gatekeepers from sending arbitrarily large payloads that could affect LLM prompts. - OAuth Scope Verification: Each Gatekeeper validates its OAuth token before granting capabilities. For example,
packages/gatekeeper-github/src/github.tsensures clients cannot obtain capabilities for resources they have not authorized. - Resource URL Pattern Checks: The system enforces per-account restrictions through
AccountDescription.grantedResourceUrlPatternsinpackages/workshop-shared/src/gatekeeper.ts, which lists the specific URL patterns a user may access.
Implementing Secure Gatekeeper Access
The following patterns demonstrate how Cloudflare OS maintains security when interacting with Gatekeepers.
Fetching a Gatekeeper class with admin verification:
// packages/workshop-backend/src/user.ts
const { class: GatekeeperCls, resource } =
await account.account.getGatekeeperClassFor(url);
// AdminConfig is consulted inside getGatekeeperClassFor; if the
// Gatekeeper is disabled an error is thrown.
Using capabilities with automatic resource cleanup:
// packages/workshop-shared/src/gatekeeper.ts
export interface Cursor<T> {
// Must be disposed when done to avoid resource leaks.
next(): Promise<T[] | null>;
}
// Example – reading a paginated list of Gmail messages
const cursor: Cursor<GmailMessage> = await gmailSession.listMessages();
while (true) {
const batch = await cursor.next();
if (!batch) break; // `null` signals exhaustion
// …process batch…
}
cursor[Symbol.dispose](); // clean up server‑side resources
Bounding Gatekeeper-provided catalogs:
import { boundAgentCatalog } from "packages/workshop-shared/src/gatekeeper";
const rawEntries = await gatekeeper.getAgentCatalog();
const safeCatalog = boundAgentCatalog(rawEntries.entries);
// `safeCatalog` is guaranteed to respect size caps before being injected
// into the LLM prompt.
Key Source Files
packages/workshop-shared/src/gatekeeper.ts: Core capability-based RPC definitions and catalog bounding logic.packages/workshop-backend/src/admin-config.ts: Central admin configuration for enabling or disabling Gatekeepers.packages/workshop-backend/src/user.ts: ImplementsgetGatekeeperClassForwith admin checks.packages/workshop-backend/src/overseer.ts: Orchestrates session creation and capability minting.scripts/run-dev-server.ts: Dev-time binding generation and OAuth secret injection.
Summary
- Capability-based isolation: Gatekeepers expose only typed
RpcStubobjects that bind to specific resources and can be revoked at any time. - Administrative gating:
User.getGatekeeperClassForinpackages/workshop-backend/src/user.tsenforcesAdminConfigchecks before allowing Gatekeeper instantiation. - Secret isolation: OAuth credentials reside in deployment-time environment variables injected by
scripts/run-dev-server.ts, never crossing the RPC boundary. - Defense in depth: Multiple enforcement points—including capability caps in
packages/workshop-shared/src/gatekeeper.tsand resource URL pattern checks—prevent payload attacks and unauthorized access. - Resource cleanup: The
Cursorpattern with[Symbol.dispose]()ensures server-side resources are released after capability use.
Frequently Asked Questions
What is a Gatekeeper in Cloudflare OS?
A Gatekeeper is a standalone Cloudflare Worker that isolates an external service integration (such as GitHub or Gmail) from the Workshop backend. Each Gatekeeper binds to the Workshop via service bindings like GATEKEEPER_GITHUB and exposes only fine-grained capabilities rather than raw API access.
How does Cloudflare OS prevent unauthorized Gatekeeper instantiation?
The system uses User.getGatekeeperClassFor() in packages/workshop-backend/src/user.ts to validate requests against the AdminConfig defined in packages/workshop-backend/src/admin-config.ts. If a Gatekeeper is disabled or the user lacks required OAuth scopes, the function throws an error before returning the Gatekeeper class.
Where are OAuth secrets stored in Cloudflare OS?
OAuth secrets such as GITHUB_CLIENT_ID are stored as deployment-time environment variables. The dev server in scripts/run-dev-server.ts injects these into each Gatekeeper's wrangler.dev.jsonc configuration. Secrets remain isolated within the Gatekeeper worker and never traverse the RPC channel to the Workshop.
How does the capability model prevent resource exhaustion attacks?
The Workshop applies capability caps using functions like boundAgentCatalog() in packages/workshop-shared/src/gatekeeper.ts to clamp the size of data returned by Gatekeepers. Additionally, the Cursor<T> interface requires explicit disposal via [Symbol.dispose]() to ensure server-side resources are released, preventing leaks even if the client misbehaves.
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 →