# How the Cap'n Web RPC Interface Ensures Runtime Type Validation in Cloudflare OS

> Discover how Cloudflare OS leverages Cap'n Web RPC for robust runtime type validation, ensuring data integrity across network boundaries with the capnweb-validate transformer.

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

---

**Cloudflare OS uses the `capnweb-validate` build-time transformer to inject runtime type-checking code into every Cap'n Web RPC method, ensuring that only well-formed data crosses the network boundary.**

Cloudflare OS leverages the **Cap'n Web** RPC framework to handle communication between front-end and back-end services. To prevent type mismatches that could corrupt application state or create security vulnerabilities, the `cloudflare/cloudflare-os` repository implements a rigorous validation pipeline that bridges static TypeScript types with runtime enforcement. This article examines how the system guarantees **runtime type validation** through compile-time transformations and consumer-side enforcement mechanisms.

## The TypeScript-to-Runtime Validation Pipeline

The validation strategy combines static TypeScript definitions with generated runtime checks. This pipeline ensures that type safety extends beyond compilation to actual execution across network boundaries.

### Typed RPC Interface Definitions

All public RPC interfaces are defined as TypeScript interfaces extending `RpcTarget`. These declarations specify exact argument types and return shapes, creating a strict contract between client and server. In [`packages/workshop-shared/src/api.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-shared/src/api.ts) (lines 60-69), the `PublicApi` interface declares methods like `startGatekeeperLogin` with precise signatures:

```typescript
export interface PublicApi extends RpcTarget {
  startGatekeeperLogin(vendorId: string): Promise<{
    url: string;
    attempt: RpcStub<LoginAttempt>;
  }>;
}

```

### The @validateRpc() Decorator

Server implementations apply the **`@validateRpc()`** decorator imported from the `capnweb-validate` package. This decorator functions as a compile-time marker that signals the build tool to generate validation logic. In [`packages/workshop-backend/src/server.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-backend/src/server.ts) (lines 2-75), the decorator is applied to classes implementing RPC interfaces:

```typescript
import { validateRpc } from "capnweb-validate";

@validateRpc()
export class PublicApiImpl implements PublicApi {
  async startGatekeeperLogin(vendorId: string) {
    // Implementation logic here
    const { url, attempt } = await this._gatekeeper.startLogin(vendorId);
    return { url, attempt };
  }
}

```

The decorator itself is a no-op at runtime; its sole purpose is to trigger the transformation process during the build phase.

### Build-Time Code Generation

During the worker build process, the **`capnweb-validate build`** command parses TypeScript declarations and emits wrapper code. This transformer examines every method signature in the RPC interface and generates corresponding validation logic that checks nested objects, arrays, and enums. The build script in [`packages/workshop-backend/package.json`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-backend/package.json) executes:

```bash
pnpm exec capnweb-validate build --out .wrangler/validate

```

The transformer outputs a deterministic validation bundle to `.wrangler/validate`, which contains wrappers that verify incoming arguments and return values against the declared schemas before and after method execution.

## Runtime Enforcement and Bundle Loading

Once built, the validation bundle integrates automatically into the worker runtime, creating an enforcement layer that protects application logic.

### Automatic Validation Bundle Integration

The Vite configuration in [`packages/workshop-backend/vite.config.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-backend/vite.config.ts) (lines 49-56) ensures the generated validation bundle loads automatically when the worker starts. This configuration imports the `.wrangler/validate` directory, making the runtime checks available without manual intervention.

When a client invokes an RPC method, the generated wrapper executes first. If any argument fails type validation—including complex nested structures—the wrapper throws a descriptive `Error` before application logic runs. This guarantees that **only well-formed data reaches the server** and that **responses respect the declared contract**.

## Selective Validation Control

For performance-critical paths where callers are fully trusted, developers can bypass validation while maintaining the decorator infrastructure.

### Bypassing Checks with skipRpcValidation()

The `capnweb-validate` package exports **`skipRpcValidation()`**, a decorator that disables runtime checks for specific methods. This pattern appears in gatekeeper implementations like [`packages/gatekeeper-zoominfo/src/zoominfo.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/gatekeeper-zoominfo/src/zoominfo.ts) (lines 2-4), where trusted internal calls require maximum performance:

```typescript
import { skipRpcValidation } from "capnweb-validate";

export class ZoomInfoGatekeeper {
  @skipRpcValidation()
  async fastInternalCall() {
    // Validation bypassed for trusted callers
    return this.processData();
  }
}

```

## Consumer-Side Validation Design

The architecture intentionally places validation logic in the consumer rather than the provider. According to the design documentation in [`plans/gatekeeper-kit.md`](https://github.com/cloudflare/cloudflare-os/blob/main/plans/gatekeeper-kit.md) (lines 2123-2146), this choice ensures that interface changes are caught immediately at call sites rather than propagating through the system. The `@validateRpc()` decorator stays with the consumer code, and `capnweb-validate` validates every RPC call against the generated schema before transmission, preventing invalid data from crossing network boundaries.

## Summary

- **TypeScript interfaces** in [`packages/workshop-shared/src/api.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-shared/src/api.ts) define strict RPC contracts extending `RpcTarget`, specifying exact argument and return types.
- The **`@validateRpc()` decorator** triggers build-time generation of validation code via the `capnweb-validate` package.
- The **build process** emits a validation bundle to `.wrangler/validate` through the `capnweb-validate build` command, integrated via Vite configuration.
- **Runtime wrappers** enforce type checking on arguments and return values before executing application logic, throwing descriptive errors on mismatch.
- **`skipRpcValidation()`** allows selective disabling of checks for trusted, performance-critical methods in gatekeeper implementations.
- **Consumer-side placement** ensures type mismatches are caught at the call site, maintaining strong contracts across the network boundary.

## Frequently Asked Questions

### What happens if an RPC call fails type validation at runtime?

The generated wrapper throws a descriptive `Error` immediately, aborting the call before any application logic executes. This prevents malformed data from reaching server-side handlers or returning to clients, ensuring that only valid data crosses the network boundary.

### Why is the validation logic placed in the consumer rather than the provider?

Placing validation in the consumer ensures that any changes to the RPC interface are caught at the call site immediately. As documented in [`plans/gatekeeper-kit.md`](https://github.com/cloudflare/cloudflare-os/blob/main/plans/gatekeeper-kit.md), this design prevents silent failures and ensures that the calling code respects the declared contract before data transmission occurs.

### Can I disable validation for specific methods while keeping it for others?

Yes. Import `skipRpcValidation` from `capnweb-validate` and apply it as a decorator to specific methods. This pattern is used in gatekeeper implementations like [`packages/gatekeeper-zoominfo/src/zoominfo.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/gatekeeper-zoominfo/src/zoominfo.ts) to bypass checks for trusted internal calls while maintaining validation across the rest of the API surface.

### How does the build process know which classes need validation?

The `@validateRpc()` decorator serves as a compile-time marker. When `capnweb-validate build` runs, it scans for these decorators and generates corresponding validation wrappers for the decorated classes and their methods, as implemented in [`packages/workshop-backend/src/server.ts`](https://github.com/cloudflare/cloudflare-os/blob/main/packages/workshop-backend/src/server.ts).