CONCEPT Cited by 1 source
Bidirectional RPC¶
What it is¶
Bidirectional RPC is an RPC model in which the protocol is symmetric — there is no privileged "client" and "server" at the protocol layer. Either peer can invoke methods on the other's exported interfaces; passing a function or object over RPC gives the recipient a stub whose invocations route back to the peer that created it.
Common shapes:
- A client passes a callback to a server method; the server later invokes the callback to stream updates back.
- A server returns an object reference; the client invokes methods on that object, and each invocation reaches back to the server where the object lives.
- Two browser windows exchange
postMessage()stubs and call each other's APIs as equals.
This is a direct consequence of object-capability RPC treating functions and objects as first- class wire values — once references go both ways, calls do too.
Why it matters¶
- Callbacks become a natural primitive. In a classical RPC (gRPC, JSON-RPC, Thrift), streaming updates from server to client requires a dedicated streaming call type; in bidirectional RPC, the client simply passes a callback function and the server calls it. No protocol extension required.
- WebSocket friendliness. WebSockets are inherently bidirectional message streams, but their APIs don't support headers / cookies after the initial handshake. A symmetric RPC protocol fits them natively — auth can be done in-band via a capability-returning method and subsequent messages flow both ways. See the WebSocket auth note in patterns/capability-returning-authenticate.
- Works across any bidirectional transport. Cap'n Web ships
HTTP batch (request-scoped), WebSocket (long-lived), and
postMessage()(in-process, browser) out of the box — and the same protocol semantics apply to all three because they all provide a bidirectional message stream. - Enables peer-to-peer patterns. Two collaborating services can expose capabilities to each other without one being nominally "the server" — useful for symmetric microservices meshes and for browser-to-browser (via a relay) scenarios.
Canonical mechanics (Cap'n Web)¶
At connection start, both peers populate their export tables with a single entry at ID 0 representing their "main" interface. Typically the server exports its public API surface as ID 0 and the client exports an empty interface — but either side can export whatever it wants. Subsequent exports are added in two ways:
- When Peer A sends a message containing a function or object reference, Peer A adds the target to its export table at a negative ID (starting at -1, counting down).
- When Peer A sends a
pushmessage asking Peer B to evaluate an expression, the result lands in Peer B's export table at a positive ID (starting at 1, counting up) — enabling promise pipelining.
A callback passed from a client to a server is an instance of (1):
the client exports the callback at a negative ID; the server
receives a stub; invoking the stub sends a push message back
the other way, and the "call" flows client ← server → client
the same way any other call flows.
Trade-offs¶
- Transport must be bidirectional. Naïve request/reply transports (a single HTTP POST with no streaming) cannot support post-hoc callbacks. Cap'n Web's HTTP batch mode explicitly breaks all references once the batch completes for this reason — you can pipeline within the batch, but you cannot retain a stub across batches.
- Connection-oriented. Each connection has its own export tables; scaling horizontally requires stickiness to the connection's originating server (for WebSockets) or an explicit redistribution mechanism. Contrast stateless gRPC unary calls.
- Resource management is tricky. A stub held by a remote peer pins the underlying object. Long-lived connections with many callbacks need a disposal discipline (Cap'n Web IDs are never reused within a connection).
- Firewall / proxy awareness. Pure RPC-over-HTTP deployments that expect outbound-only flow from clients will not see server-initiated calls. Transports that carry both directions (WebSocket, HTTP/2 server push, postMessage, etc.) are required.
Seen in¶
- sources/2025-09-22-cloudflare-capn-web-rpc-for-browsers-and-web-servers — Cap'n Web is explicitly symmetric at the protocol layer; the post walks through callback-by-reference and object-by-reference. "Every kind of interaction can happen in either direction."
Related¶
- concepts/object-capability-rpc — bidirectional RPC is a consequence of making function/object references first-class.
- concepts/promise-pipelining — companion capability-RPC feature; both ship together in Cap'n Proto + Cap'n Web.
- systems/capnweb / systems/capnproto
- systems/web-streams-api — a related bidirectional-ish JavaScript primitive, but at the byte-stream level rather than the method-call level.