# How Cloudflare OS Enforces Access Control for External Services via Gatekeepers

> Cloudflare OS secures external services with Gatekeepers, enforcing access control via capability-based adapters, policy-driven provisioning, and runtime authorization for every operation.

- Repository: [Cloudflare/cloudflare-os](https://github.com/cloudflare/cloudflare-os)
- Tags: how-to-guide
- Published: 2026-09-05

---

**Cloudflare OS enforces fine-grained access control to external services through capability-based Gatekeeper adapters that implement strict RPC interfaces, with policy-driven provisioning and runtime authorization checks for every read and write operation.**

Cloudflare OS manages access to third-party APIs and external resources through a sophisticated Gatekeeper architecture that treats service bindings as capabilities. This system ensures that only authorized workers can invoke external service methods, with enforcement occurring at provisioning, account creation, and runtime. Understanding how **Cloudflare OS access control external services Gatekeepers** function reveals a security model built on immutable capabilities and audit-ready authorization flows.

## The Three-Stage Gatekeeper Enforcement Flow

The enforcement architecture operates through three distinct stages, each implemented in specific source files within the `cloudflare/cloudflare-os` repository.

### Stage 1: Vendor Discovery and Provisioning Policy

The Workshop backend begins by enumerating all bound Gatekeeper vendors and filtering them against a deployment-wide **provisioning policy**. In [`packages/workshop-backend/src/user.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-backend/src/user.ts) (lines 1198‑1214), the system retrieves ambient vendors and evaluates the `ambientGatekeeperMode` setting, which can be `enabled`, `optional`, or `disabled`.

The policy also checks the `shouldAutoProvisionAccount` flag alongside each vendor's `VendorDescription.autoProvisionsAccount` property (defined in [`packages/workshop-shared/src/gatekeeper.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-shared/src/gatekeeper.ts), lines 73‑80). Only vendors explicitly marked with `autoProvisionsAccount: true` are considered for automatic account creation. This logic is enforced in [`user.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/user.ts) (lines 1249‑1256), ensuring that administrators maintain strict control over which external services users may access.

### Stage 2: Account Creation and Capability Sealing

When the policy permits auto-provisioning, the backend calls the optional `GatekeeperVendor.createAccount()` method. This method is exposed only on vendors that explicitly declare `autoProvisionsAccount` in their description.

The returned **GatekeeperUser** capability is immediately stored in the user’s Durable Object and becomes the sole authority for that external service account. As implemented in [`user.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/user.ts) (lines 1267‑1282), this capability is *immutable*: the gatekeeper cannot look up or return other accounts, guaranteeing that a caller can only act on the account it just created. No OAuth flow is required at this stage, and raw credentials remain isolated within the Gatekeeper’s Durable Object, never exposed to the Workshop backend.

### Stage 3: Runtime Enforcement of Reads and Writes

Every Gatekeeper session must call back into the Workshop’s authorization layer before returning data. For read operations, the system invokes `ObservationAuthorizer.authorizeObservation` (defined in [`packages/workshop-shared/src/gatekeeper.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-shared/src/gatekeeper.ts), lines 995‑1004), ensuring each data retrieval is audited and allowed by the user’s grant.

Write-side actions are submitted to an `ApprovalQueue` (lines 985‑990), which inherits the same authorization checks and may require human-in-the-loop approval via `submitAction`, `applyAction`, or `rejectAction` methods (lines 1015‑1024). Because the `GatekeeperUser` capability serves as the only entry point, the Workshop guarantees that all RPC calls route through this enforcement layer, with the service binding itself acting as a capability that only the bound worker can invoke.

## Core RPC Interfaces and Capability Contracts

The Gatekeeper system relies on well-defined TypeScript interfaces that establish the security contract between the Workshop backend and external service adapters.

The `GatekeeperVendor` interface exposes the optional `createAccount` method (lines 74‑80), while the `ObservationAuthorizer` interface (lines 995‑1004) defines the `authorizeObservation` signature that Gatekeepers must call before returning sensitive data. The `ApprovalQueue` interface extends `ObservationAuthorizer` (lines 985‑990) to provide the submission and approval methods required for mutating operations.

Because each Gatekeeper runs as a separate Cloudflare Worker, the **service binding** itself functions as a capability. The Workshop never holds raw OAuth tokens or secrets; these remain sealed within the Gatekeeper’s Durable Object, accessible only through the capability-based RPC interface.

## Implementing Gatekeeper Access Control in Practice

### Listing Optional Gatekeepers for User Selection

To present users with Gatekeepers they may manually add, the backend filters for vendors where `ambientGatekeeperMode === "optional"`:

```typescript
// packages/workshop-backend/src/user.ts (conceptual usage)
const user = new UserDO(ctx);
const addable = await user.listAddableGatekeepers(); 
// Returns vendors where mode is "optional" and no account exists yet
// Implements logic from lines 1198-1227

```

This method leverages the ambient vendor discovery logic to respect deployment policy while excluding already-provisioned services.

### Auto-Provisioning an Enabled Gatekeeper

When the policy allows automatic account creation, the provisioning flow executes:

```typescript
// Auto-provisioning for a specific vendor
const vendorId = "context"; // A Gatekeeper marked with autoProvisionsAccount
await user.provisionAmbientAccount(vendorId);
// Internally checks ambientGatekeeperMode === "enabled" (lines 1249-1256)
// Then invokes vendor.createAccount() if defined (gatekeeper.ts lines 74-80)

```

This sequence corresponds to the implementation in [`user.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/user.ts) (lines 1267‑1282), ensuring the capability is sealed immediately upon creation.

### Enforcing Authorization in a Gatekeeper Session

Gatekeeper implementations must authorize observations before returning data:

```typescript
// In a concrete Gatekeeper implementation
async startSession(approvalQueue) {
  const authorizer = await approvalQueue; // RPC stub from Workshop
  
  // Mandatory check before any data exposure
  await authorizer.authorizeObservation({
    resourceId: "user-data-123",
    operation: "read",
    scope: ["profile", "settings"]
  });
  
  // Only after authorization succeeds can data be returned to the Gadget
  return this.fetchSecureData();
}

```

This pattern matches the `authorizeObservation` signature defined in [`gatekeeper.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/gatekeeper.ts) (lines 995‑1004), ensuring runtime enforcement of user grants.

## Summary

- **Policy-driven provisioning**: The `ambientGatekeeperMode` setting in [`user.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/user.ts) (lines 1198‑1227) controls whether Gatekeepers are enabled, optional, or disabled per deployment, while `autoProvisionsAccount` determines eligibility for automatic account creation.
- **Immutable capabilities**: The `GatekeeperUser` capability created via `createAccount()` (lines 1267‑1282) is the sole authority for an external service account, preventing privilege escalation or cross-account access.
- **Runtime mediation**: Every read operation requires `authorizeObservation` approval (lines 995‑1004), while writes flow through the `ApprovalQueue` (lines 985‑990), ensuring audit trails and explicit consent.
- **Worker isolation**: Service bindings act as capabilities, keeping OAuth tokens and secrets confined to individual Gatekeeper Durable Objects rather than the Workshop backend.

## Frequently Asked Questions

### What is a Gatekeeper in Cloudflare OS?

A **Gatekeeper** is a Cloudflare Worker that acts as an adapter between the Workshop backend and external services (such as OAuth providers or APIs). It implements RPC interfaces defined in [`packages/workshop-shared/src/gatekeeper.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-shared/src/gatekeeper.ts), including optional account provisioning (`createAccount`) and mandatory authorization callbacks (`authorizeObservation`). Each Gatekeeper runs in isolation, with service bindings serving as the capability mechanism that restricts access to authorized callers only.

### How does the provisioning policy determine which Gatekeepers are available?

The provisioning policy evaluates the `ambientGatekeeperMode` (enabled/optional/disabled) and `shouldAutoProvisionAccount` settings against each vendor's `autoProvisionsAccount` flag. In [`packages/workshop-backend/src/user.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-backend/src/user.ts) (lines 1198‑1227), the backend filters the list of bound vendors, showing only those that match the deployment policy and the user's current account state. Vendors marked as `enabled` are auto-provisioned, `optional` ones appear in addable lists, and `disabled` ones are hidden entirely.

### What makes Gatekeeper capabilities immutable and secure?

Once created via `GatekeeperVendor.createAccount()` (lines 74‑80), the resulting `GatekeeperUser` capability is stored in the user’s Durable Object and cannot be modified or used to access other accounts. The Gatekeeper interface contract prohibits lookup operations for existing accounts, ensuring the returned capability is the only valid reference. This immutability, combined with Worker isolation that keeps secrets out of the Workshop backend, prevents lateral movement or privilege escalation between external service accounts.

### How does runtime authorization prevent unauthorized data access?

Before returning any data, Gatekeepers must call `ObservationAuthorizer.authorizeObservation` (lines 995‑1004) with a description of the requested resource and operation. The Workshop backend evaluates this against the user’s grants and audit policies. For write operations, the `ApprovalQueue` interface (lines 985‑990) submits actions for review, potentially requiring human approval before `applyAction` executes the mutation. This design ensures that every data access—read or write—is explicitly authorized and logged.