Cap'n Web: a new RPC system for browsers and web servers
Relationship to Cap’n Proto and design goals
- Cap’n Web is presented as a simplification of Cap’n Proto RPC, with a much smaller and clearer state machine.
- Some commenters hope the new design will feed back into Cap’n Proto (for C++, Rust, etc.), but the author notes this would be a large “running-in-place” rewrite.
- The protocol is schemaless at the wire level but heavily inspired by Cap’n Proto’s object-capability model and promise pipelining.
Object capabilities, promise pipelining, and arrays
- Core features: pass-by-reference objects and callbacks, bidirectional calls, and promise pipelining so chains of calls incur one network round trip.
- Over WebSockets, multiple calls are sent without waiting for replies; with HTTP batch, calls are concatenated into one request.
- Arrays are handled via a special
.map()onRpcPromisethat “records” the callback once with placeholder promises, then replays on the server as a tiny DSL.- This enables server-side fan-out (e.g., per-item lookups) without multiple RTTs.
- Conditionals, computation, and side effects inside the mapper are largely disallowed or dangerous; several people see this as powerful but “magical” and footgun-prone.
Schemas, typing, and “schemaless” concerns
- “Schemaless” means the protocol doesn’t know types; schema responsibility is pushed to the application.
- Many want strong schemas at the RPC boundary; suggestions include TypeScript-only schemas with generated runtime checks, or Zod/Arktype-style validators.
- Some dislike duplicating definitions (TS + Zod), others note Zod can be the single source of truth.
Comparison with other systems
- Compared with JSON-RPC: Cap’n Web adds object references, lifecycle management, and pipelining at the cost of more protocol complexity.
- Compared with GraphQL:
- Similar “nesting”/shape selection via promise chains and
.map(), but lacks built-in solutions for N+1/database batching, query planning, and federation. - Several argue GraphQL’s dataloader, query-costing, and federated gateways remain advantages.
- Similar “nesting”/shape selection via promise chains and
- Compared with OCapN: Cap’n Web lacks sturdyrefs and third-party handoff; positioned more as client–server SaaS than general distributed capability routing.
- Compared with REST/gRPC: seen as a more natural fit to function-call mental models and object capabilities, but critics warn about repeating CORBA-style “remote looks local” pitfalls.
State, sessions, scaling, and reconnection
- Each RPC session has import/export tables for capabilities; state is per-connection:
- WebSocket: lasts for the socket lifetime.
- HTTP batch: lasts for a single request.
- Reconnects invalidate old stubs; apps must reconstruct object graphs and subscriptions. Patterns described include re-fetching from a root stub in React.
- Some worry about server affinity, load balancing with long-lived sockets, and easy DoS via slow/unresponsive clients. Others argue these issues are generic to WebSocket-heavy systems and belong in load balancers/infra.
Security and safety
- Concerns about untyped inputs, callback stubs, and accidental invocation of remote functions (e.g., via
toString/toJSON) are raised. - The protocol blocks overriding dangerous prototype methods and recommends runtime type checking; more automated TS-based validation is a stated goal.
.map()requires side-effect-free callbacks; misuses (branching, coercion of promises to booleans/strings) can silently behave oddly, so linters or different method names (rpcMap) are suggested.
Language support and portability
- Today it’s TypeScript/JavaScript only; the design depends heavily on JS dynamism and small bundle size.
- Dynamic languages like Python are seen as plausible future targets; static languages would likely be better served by Cap’n Proto plus a Cap’n Web–to–Cap’n Proto proxy.
- Some view TS focus as a strength (no separate IDL); others see lack of cross-language support as a dealbreaker for “real” RPC.
Use cases, enthusiasm, and skepticism
- Enthusiasts like the uniform model across browser, workers, iframes, and web workers, plus the ability to pass rich capabilities instead of just data.
- Potential uses include internal APIs, worker–worker communication, and inter-context messaging; some are wary of using it for public APIs without more tooling (dataloaders, rate limiting, query planning).
- Skeptics worry about:
- Hiding network boundaries and latency behind “local-looking” calls.
- The complexity and subtle semantics of pipelining and
.map(). - Tight coupling to TS/JS and difficulty of porting to other stacks.
- Others argue these abstractions are acceptable when used by teams that understand the distributed semantics and add explicit limits, logging, and patterns on top.