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.

A browser reaches a port on a hostname of its own, <port>--<id>.<suffix>, built from the deployment’s NIMBUS_PREVIEW_HOST_SUFFIX binding. The router resolves that host to the same /port/<n>/ lookup with the app mounted at the host’s root, so root-absolute paths resolve without rewriting. Only the canonical form parses. A leading zero in the port, a second dot in the label, or a session id that is not a DNS label all fail to match. Deployments with no suffix bound reach the registry at /s/<id>/port/<n>/ instead. The preview host is also a trust boundary (security covers what is stripped at it).

A port with no registry entry returns 502, because no process is listening. A registered port whose stub is not attached returns 501. That state exists during the boot window between registration and a serving isolate. No retry masks either code.

A routed request crosses the isolate boundary as a native Request over Workers RPC, not a structured-clone copy. Binary bodies pass byte-for-byte in both directions, and the request body 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 kills the stream, and materializing the body first collapses an SSE feed into one batch at stream-close. SSE ticks arrive one by one 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. That work is on the research page.