Skip to content

Ports & routing

Loopback curl, the port preview, and the default app preview all go through the supervisor’s port registry. The registry is a map from port to owning pid and the facet stub that serves it; a facet’s listen(port) is an RPC that writes this map, and ports are torn down with their pid. Loopback exists only inside the session: the DNS shim resolves localhost and nothing else, and a connection to 127.0.0.1:<port> is a lookup in this registry, not a network hop.

No registry entry returns 502: no process is listening. A registered port whose stub isn’t attached returns 501. That state exists during the boot window between registration and a serving isolate. A dead port and a booting port return different codes, and neither is masked by a retry.

A routed request crosses the isolate boundary as a native Request over Workers RPC — not a structured-clone copy — so binary bodies pass byte-for-byte in both directions, and the request body itself streams into the facet.

Responses stream end to end. The relay pipes the facet’s body through an identity stream and keeps the routing call open until the body finishes; closing it early would kill the stream, and materializing it first — what an earlier version did — turned every SSE feed into one batch at stream-close. Live probes check each SSE tick in the session shell and through the preview path at ~300 ms intervals. In-session curl writes each chunk as it arrives, and Ctrl+C cancels the read. Abandoned readers also cancel their relay pipes.

Node’s HTTP bridge and the Python and Ruby virtual-socket shims are separate adapters today, but both end at this registry. The target is one shared virtual socket layer for Node, Python, Ruby, and WASI. sock_accept is the one unimplemented preview1 function, so WASM servers can’t take /port traffic yet. That work is on the research page.