Networking
The virtual network stack — interfaces, sockets, DNS, ports — and how in-VM HTTP servers work.
Each box has a NetworkStack — a virtual network with interfaces, sockets,
routes, and a DNS resolver — plus a portRegistry on the kernel
that maps a port number to a request handler. Together they let programs inside
the VM listen, connect, and serve as if they were on a real machine, without any
real sockets.
Listening and serving
When a program creates an HTTP server through the http shim
and calls listen(port), the server registers a VirtualRequestHandler for that
port on the kernel. Anything that can produce a VirtualRequest — the
service worker, the
SW-free preview, a tunnel,
sandbox.fetch(), a
host port forward, or an in-VM client — reaches the server by
looking up its port.
// inside the VM
const http = require("http");
http.createServer((req, res) => res.end("hi")).listen(3000);Now port 3000 is in the registry, and a browser preview at `/_sw/<boxId>/3000/`
(or a tunnel) can hit it. From the code that owns the box, call it directly:
// on the host
const res = await sandbox.fetch("http://localhost:3000/");
await res.text(); // "hi"Handlers are called synchronously but may finish asynchronously — Express and Vite
return immediately and write the response later. Every transport goes through one
shared dispatcher that waits for that completion, so don't invoke a handler by hand;
use sandbox.fetch() or dispatchRequest.
Sockets, DNS, and routes
The NetworkStack models the lower layers too:
- Interfaces & routes — a loopback and virtual interfaces, inspectable with
the
ifconfig,ip,route, andnetstatcommands. - DNS — a resolver seeded from
/etc/hosts(loaded at boot).hostqueries it; the network layer uses it to resolve names. - Sockets —
net/tlssockets for programs that speak raw TCP rather than HTTP.
The ports command lists what's currently listening, and forward / unforward
manage port forwards.
Reaching the outside world
A browser tab can't make arbitrary cross-origin requests, so outbound HTTP from
inside the VM is routed through a CORS proxy for hosts that don't send CORS
headers (for example api.expo.dev, which create-expo-app calls). The
Node layer injects a proxying fetch into each
module and serves require("fetch-nodeshim") from the same stack, so a package's
networking is transparently proxied. See Tunnelling for the
proxy and relay.
Git over CORS
Git hosts (github.com, gitlab.com, …) don't send CORS headers either, so
git clone, git fetch, and git push in the browser route through a separate
git CORS proxy — a clone's pack data can be tens of MB, so it's kept off the
same-origin proxy used for small API calls. Set LIFO_GIT_CORS_PROXY to point it
at a proxy (the playground defaults to the public cors.isomorphic-git.org; self-host
one — e.g. a Cloudflare Worker running @isomorphic-git/cors-proxy — for production).
In Node there's no CORS wall, so git talks to remotes directly and the setting is
ignored.
The curl command
curl is built in and speaks to both in-VM servers and (via the proxy) the
outside world — useful for testing a dev server from the shell:
curl http://localhost:3000
curl -H "Accept-Encoding: identity" http://localhost:8081/index.bundleNote
The virtual network is per-box. Two boxes don't share ports or sockets — which is exactly what you want when running many isolated sandboxes in one process.