Skip to content

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 a Date; [["Alice","Bob"]] is a literal array). Human-readable; you can tail -f a 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 RpcTarget are 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 successful authenticate() 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 workerd test 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. Any await inside 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

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)

Last updated · 766 distilled / 2,225 read