SYSTEM Cited by 3 sources
Cap'n Web¶
Cap'n Web is a pure-TypeScript
object-capability RPC library from Cloudflare (GitHub,
MIT, announced 2025-09-22).
Minify+gzip is under 10 kB with no dependencies; runs in every major
browser, Workers, Node.js, and other modern JS
runtimes; ships transports for HTTP batch, WebSocket, and
postMessage() out of the box, and is extensible to others.
What it is¶
A JS-native RPC protocol that inherits the capability + promise-pipelining design of Cap'n Proto but drops the schema language and binary wire format. Instead:
- No schemas, no codegen. You define RPC servers by extending
RpcTarget; you call remote methods on a client stub as if it were a local object. TypeScript interfaces can give you end-to-end type safety at compile time. - Wire format is JSON post-processed for escape-array encoding of
non-JSON types (
["date", 1758...]is aDate;[["Alice","Bob"]]is a literal array). Human-readable; you cantail -fa session and understand it. - Symmetric protocol. No client/server distinction at the protocol level — both peers maintain import + export tables and can invoke each other's exported objects.
The four-message protocol¶
| Message | Meaning |
|---|---|
push |
Evaluate an expression; store result in the peer's export table at a predictable positive ID. |
pull |
Please serialize and send back the value at this ID (issued only if the caller actually awaits). |
resolve |
Here's the value (in response to a pull). |
reject |
Call threw / rejected. |
The caller predicts the positive export ID of each push — that's
what enables promise pipelining without
waiting for the resolve. See the
protocol spec
for details.
Capability features (vs typical RPC)¶
- Pass-by-reference for functions. Passing a function over RPC gives the recipient a stub; invoking the stub makes an RPC back to the creator → bidirectional RPC with callbacks as first-class arguments.
- Pass-by-reference for objects. Classes extending
RpcTargetare passed by reference; method calls route back to the origin. - Promise pipelining. The Promise
returned by a call is a JS
Proxy— methods invoked on it are sent immediately as speculative dependent calls. - Capability-based security.
authenticate()returning a session stub is the idiomatic auth pattern. The session object is unforgeable at the protocol layer — clients can only obtain one via a successfulauthenticate()call.
The .map() trick¶
Cap'n Web supports promise.map(callback) for pipelined
transformations over a promised array — this is the feature that
makes it a serious alternative to
GraphQL for the canonical "N+1 query" case. The callback is run
once client-side against a Proxy placeholder (must be synchronous);
the interceptor records the pipelined calls as a DSL expression that
is just the RPC protocol itself, and ships it to the server to
replay per array element. No round trips, no schema.
(record-replay-dsl)
Setup¶
Client (WebSocket):
import { newWebSocketRpcSession } from "capnweb";
const api = newWebSocketRpcSession("wss://example.com/api");
const result = await api.hello("World");
Client (HTTP batch — once you await, the batch is done):
const batch = newHttpBatchRpcSession("https://example.com/api");
const [a, b] = await Promise.all([batch.hello("Alice"), batch.hello("Bob")]);
Server (Cloudflare Worker):
import { RpcTarget, newWorkersRpcResponse } from "capnweb";
class MyApiServer extends RpcTarget {
hello(name) { return `Hello, ${name}!` }
}
export default {
fetch(req) {
if (new URL(req.url).pathname === "/api")
return newWorkersRpcResponse(req, new MyApiServer());
return new Response("Not found", {status: 404});
}
}
Production use¶
- Wrangler remote bindings (GA
2025-09-16)
— local
workerdtest instances speak Cap'n Web RPC to production services. First shipped consumer, announced ~6 days before the launch post. - Cloudflare states internal experiments in frontend apps.
Trade-offs¶
- Experimental. Cloudflare explicitly flags "willingness to live on the cutting edge may be required."
- No runtime type checking. TypeScript is compile-time only; malicious clients can still send wrong-shaped args — pair with runtime validators like Zod at handlers.
.map()lambda must be synchronous. Anyawaitinside the callback breaks the DSL capture. Users will hit this and have to restructure.- Capability lifetime / GC. A stub held by a remote peer pins the underlying object. The post notes IDs are never reused but doesn't walk through the disposal discipline — a real consideration for long-lived WebSocket sessions.
Generable from OpenAPI via Forge (2026-09-28)¶
Cloudflare's open-source generation pipeline Forge can take an
OpenAPI spec and generate Cap'n Web directly. Because Workers bindings are implemented as
Workers that expose RPC methods, this "opens the door to generating
bindings from Workers to other
APIs." In Cloudflare's own Forge chain, the Cap'n Web specifications are generated from the
TypeScript SDK (which is itself generated from OpenAPI): OpenAPI → TypeScript SDK → (cf CLI +
Cap'n Web specs). This makes Cap'n Web a first-class output target of a codegen pipeline, not
just a hand-authored RPC layer. (Source:
sources/2026-09-28-cloudflare-introducing-forge-the-open-source-pipeline-for-generating-sd)
Seen in¶
- sources/2025-09-22-cloudflare-capn-web-rpc-for-browsers-and-web-servers — announcement post; full design walkthrough + protocol spec. Canonical wiki instance of object-capability-rpc, promise-pipelining, and record-replay-dsl.
- sources/2026-09-28-cloudflare-introducing-forge-the-open-source-pipeline-for-generating-sd — Cap'n Web as a codegen output target: Forge generates Cap'n Web directly from OpenAPI, opening the path to generating Workers bindings.
Related¶
- systems/capnproto — schema-based ancestor, same model, binary wire
- systems/grpc — contrast: schema-first, HTTP/2, no capability/pipelining
- systems/github-graphql-api — the GraphQL solution to the same waterfall problem Cap'n Web solves with pipelining
- systems/cloudflare-forge — pipeline that can generate Cap'n Web from OpenAPI
As a TypeScript-as-schema precedent¶
Cap'n Web's "define RPC in TypeScript types, not a schema DSL"
posture is the direct precedent for Cloudflare's 2026-04-13
decision to make TypeScript the source of truth for generating
the cf CLI, Workers bindings, SDKs, Terraform
provider, and MCP Code Mode server from one
unified interface schema.
See typescript-as-codegen-source (Source:
sources/2026-04-13-cloudflare-building-a-cli-for-all-of-cloudflare).
Cloudflare OS app invocation (2026-08-05)¶
Cloudflare OS uses Cap'n Web between a generated app's sandboxed browser client and its Dynamic Worker server. Browser code calls a server method as an ordinary JavaScript function, and the workspace agent can invoke the same method surface. This makes an agent-authored app reusable as a live tool rather than only an exported document. The source does not disclose transport selection, connection lifetimes, serialization rules, or how object-capability references are scoped across shared apps. (Source: sources/2026-08-05-cloudflare-os-an-open-platform-for-agents-apps-and-work)