Tunnelling & proxies
Reaching an in-VM port from outside the tab — the WebSocket tunnel and relay, and the CORS proxy for outbound requests.
Previews reach an in-VM port from the same tab. Two other mechanisms cross a real network boundary: a tunnel (expose an in-VM port to the outside), and a CORS proxy (let the VM reach hosts a tab can't).
The WebSocket tunnel
WebSocketTunnel connects a box's port to a relay over a WebSocket, so something
outside the tab — a phone running Expo Go, a teammate's browser — can reach a dev
server inside the VM. The relay (apps/tunnel-server) accepts connections and
forwards HTTP and WebSocket traffic in both directions.
- Framing — tunnel frames are
Buffer-based (thewsreceiver reads binary frames), carrying request/response and WebSocket-upgrade traffic. - Path-based multiplexing — run the relay without a fixed port and it routes
by a path prefix (
`/<port>/…`), stripping the prefix before handing the request to the box, so tunnelled URLs stay clean. - Headers preserved — the relay forwards headers (including
Host), which matters for dev servers that build URLs fromreq.headers.host.
A typical native-device setup: run the relay on your machine, start a tunnel from the VM, and point the dev server's public URL at the relay:
# on your machine
node apps/tunnel-server/server.js --port 8081
# inside the VM
tunnel --server ws://localhost:3005 &The tunnel, forward, and ports commands manage this from
the shell.
The CORS proxy
Outbound requests are the mirror image. A browser tab can't fetch arbitrary
cross-origin hosts, so requests to known non-CORS hosts (e.g. api.expo.dev,
exp.host, u.expo.dev) are routed through a same-origin CORS proxy —
`/_cors?url=…` — which fetches server-side and returns the response with
permissive CORS headers.
The Node layer wires this in transparently: the
fetch a module receives (and require("fetch-nodeshim")) proxies those hosts
automatically, so packages that call them work inside a tab without any code
change. Configure it with LIFO_CORS_PROXY (the proxy base) and
LIFO_CORS_PROXY_HOSTS.
Note
The relay is per-session and manual today. A hosted tunnel/proxy — so exposing a port doesn't require running your own relay — is on the roadmap, along with hardening the relay (auth, reconnect, multiplexing).