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:
- Compute content-addressed hashes of modified files
- Exchange hash sets with the Durable Object
- Transfer missing chunks using
read/writeoperations - 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/kqueueto 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
computerdinstances 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
computerdprocesses - 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →