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.