CapnWeb RPC Wire Contract: How Cloudflare Computer Connects Durable Objects to `computerd`

The capnWeb RPC wire contract is a Cap'n Proto-based protocol that defines filesystem, sync, and lifecycle operations between a Cloudflare Durable Object (control plane) and the computerd process (data plane), using capability-based security and zero-copy serialization.

This article explains the complete wire contract implemented in the cloudflare/computer repository. You'll learn how the protocol is structured, what methods are available, and how capabilities enable secure, low-latency communication between distributed components.

What Is capnWeb and Why Cap'n Proto?

Cloudflare Computer uses capnWeb as its core RPC layer. Cap'n Proto provides three critical advantages for this architecture:

  • Zero-copy serialization — Large file payloads skip unnecessary memory copies
  • Capability-based security — Fine-grained access control without authentication overhead
  • Streaming-friendly framing — Efficient over WebSocket and HTTP connections

The protocol separates concerns: Durable Objects maintain authority and state, while computerd instances perform I/O operations using delegated capabilities.

Core Concepts of the Wire Contract

Capabilities as Security Primitives

Every resource exposed over RPC is a capability. The Durable Object creates capabilities (file handles, directory watchers, sync endpoints) and passes them to computerd. The recipient can invoke methods on a capability but cannot forge or escalate privileges.

Capabilities are reference-counted. When computerd drops a reference, the Durable Object automatically revokes access.

Message Structure

Each RPC call follows Cap'n Proto framing:

Field Purpose
Method ID 16-bit identifier from the schema
Request payload Cap'n Proto struct with parameters
Response payload Return value or exception
Capability table References to exported/imported capabilities

Schema-Driven Code Generation

The contract in packages/rpc/src/schema.capnp generates TypeScript bindings. Method signatures are thin wrappers around binary messages, ensuring type safety across version changes.

Top-Level ComputerRpc Interface

The root capability exposed by every Durable Object implements these methods:

Method Parameters Returns Description
open path: Text, flags: UInt32 FileCapability Open file or directory for I/O operations
read file: FileCapability, offset: UInt64, length: UInt32 Data Read bytes from an open file
write file: FileCapability, offset: UInt64, data: Data void Write bytes to an open file
stat path: Text StatResult Retrieve metadata (size, mtime, mode)
readdir dir: FileCapability DirectoryListing List entries with basic metadata
mkdir path: Text, mode: UInt32 void Create new directory
unlink path: Text void Remove file or empty directory
close file: FileCapability void Release capability, free resources
sync — SyncResult Trigger VFS synchronization
watch path: Text WatchCapability Subscribe to change notifications

All paths are absolute within the Computer instance namespace. Flags follow POSIX conventions (O_RDONLY, O_WRONLY, O_CREAT, etc.).

Connection Lifecycle

1. Handshake and Capability Exchange

When computerd starts, it establishes a WebSocket to its host Durable Object. Per the protocol in docs/08_capnweb_interface.md:

// Simplified handshake from packages/rpc/src/client.ts
import { createRpcClient } from '@cloudflare/computer-rpc';

const rpc = await createRpcClient(DURABLE_OBJECT_URL);

// First message negotiates protocol version
// Durable Object returns root ComputerRpc capability

The handshake verifies version compatibility and exchanges the initial capability set.

2. Operation Phase

computerd requests capabilities as needed. Each operation validates that the caller holds the required capability:

// Example: Read operations require explicit file capability
const fileCap = await rpc.open('/etc/config.json', 0x0000); // O_RDONLY

const data = await rpc.read(fileCap, 0n, 4096);
const text = new TextDecoder().decode(data);

await rpc.close(fileCap); // Revokes capability

3. Error Handling

Errors encode as Cap'n Proto exceptions with structured codes:

Exception Type code value Trigger
FileNotFound ENOENT Path does not exist
PermissionDenied EACCES Missing capability for operation
IsDirectory EISDIR File operation on directory
NotDirectory ENOTDIR Directory operation on file
Timeout ETIMEDOUT Sync operation deadline exceeded

The client library in packages/rpc/src/client.ts translates these to JavaScript Error objects:

try {
  await rpc.open('/restricted/file', 0x0000);
} catch (err) {
  if (err.code === 'EACCES') {
    // Handle permission denial
  }
}

Directory Watching and Events

The watch method returns a long-lived capability for reactive filesystem monitoring:

const watchCap = await rpc.watch('/var/log');

// Stream of change events
for await (const event of watchCap.events()) {
  console.log(`${event.path}: ${event.type}`); // 'create', 'modify', 'delete'
}

// Explicit cleanup or drop reference
await rpc.close(watchCap);

Watch capabilities are reference-counted like file handles. The Durable Object batches events and pushes them over the existing WebSocket connection.

Synchronization Protocol

The sync method triggers a three-way merge between local computerd state and the Durable Object's authoritative view:

const result = await rpc.sync();

if (!result.success) {
  console.error('Conflict in:', result.conflictPaths);
  // Application logic resolves conflicts
}

Per packages/computer/src/computerd.ts, sync operations:

  1. Compute content-addressed hashes of modified files
  2. Exchange hash sets with the Durable Object
  3. Transfer missing chunks using read/write operations
  4. Update capability references to reflect new inode mappings

Server-Side Implementation

The computerd process implements the ComputerRpc interface in packages/computer/src/computerd.ts. Key responsibilities:

  • Capability lifecycle — Track reference counts, validate method invocations
  • Local VFS cache — Maintain consistent view between sync operations
  • Event routing — Forward watch notifications from kernel inotify/kqueue to RPC events

The implementation uses a capability table indexed by 32-bit handles. Each handle maps to a kernel file descriptor or synthetic resource.

Client Library Architecture

packages/rpc/src/client.ts provides the ergonomic JavaScript API. It handles:

  • Capability proxying — Transparent method dispatch on remote objects
  • Message pipelining — Parallel requests over single WebSocket
  • Promise integration — Async/await for RPC round-trips

Generated types from schema.capnp ensure compile-time safety:

// Generated interface (simplified)
interface FileCapability {
  read(offset: bigint, length: number): Promise<Uint8Array>;
  write(offset: bigint, data: Uint8Array): Promise<void>;
  stat(): Promise<StatResult>;
  close(): Promise<void>;
}

Security Model: Capability-Based Access Control

The capnWeb contract eliminates ambient authority. computerd cannot access any resource without an explicit capability. This design:

  • Prevents lateral movement — Compromised computerd instances remain confined to delegated capabilities
  • Enables audit logging — All operations trace to specific capability grants
  • Supports revocation — Durable Objects invalidate capabilities instantly without network round-trips

Capabilities are cryptographically unforgeable. The wire format includes integrity checks on capability references.

Testing and Verification

The contract includes comprehensive tests in packages/rpc/tests/wire.test.ts:

Test Category Coverage
Serialization round-trips All request/response types
Capability transit Import/export, reference counting
Error propagation Exception encoding, stack preservation
Concurrent operations Message ordering, pipelining correctness

Tests use in-memory transports to verify behavior without network dependencies.

Key Files in the Repository

Path Purpose
docs/08_capnweb_interface.md Human-readable protocol specification
packages/rpc/src/schema.capnp Canonical Cap'n Proto schema
packages/rpc/src/client.ts Client-side RPC implementation
packages/computer/src/computerd.ts Server-side capability implementation
packages/rpc/tests/wire.test.ts Wire format and behavior verification

Summary

  • capnWeb is the Cap'n Proto RPC contract connecting Cloudflare Durable Objects to computerd processes
  • Capabilities provide fine-grained, revocable access to files, directories, and sync endpoints
  • Zero-copy serialization enables efficient large-file I/O over WebSocket connections
  • Schema-defined methods (open, read, write, stat, sync, watch) implement a complete POSIX-like filesystem API
  • Security model eliminates ambient authority—every operation requires an explicit, unforgeable capability

Frequently Asked Questions

What transport does capnWeb use under the hood?

capnWeb runs over WebSocket connections between the Durable Object and computerd. The Cap'n Proto messages are framed as binary WebSocket frames. This provides NAT traversal through Cloudflare's edge network and enables long-lived capability references without connection re-establishment.

How does capability revocation work if computerd crashes?

The Durable Object maintains a reference count for each issued capability. When the WebSocket disconnects, all capabilities held by that computerd instance are automatically released. The next connection from a new computerd process receives fresh capabilities through a new handshake.

Can I extend the capnWeb protocol with custom methods?

Yes. The schema in packages/rpc/src/schema.capnp can be extended with new interfaces and methods. Backward compatibility is preserved by Cap'n Proto's field numbering and interface versioning. New computerd implementations can ignore unknown methods, while old clients receive unimplemented exceptions for new operations.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →