Protocols
The wire protocols that connect the VM to the outside — the service-worker bridge, the tunnel frames, and I/O streaming.
Lifo is mostly in-process, but a few seams cross a boundary: the page ↔ service worker, the service worker ↔ VM, the VM ↔ relay, and the shell ↔ terminal. Each has a small, deliberate protocol.
VirtualRequest / VirtualResponse
The common currency is an HTTP-like message pair defined by the
kernel: a VirtualRequest (method, url, headers, body) and a
VirtualResponse (status, headers, body). Everything that wants to talk to an
in-VM server produces a VirtualRequest and gets a VirtualResponse back — the
service worker, a tunnel, and in-VM clients all speak it.
Service worker ↔ page bridge
The ServiceWorkerBridge connects the page's registered `/sw.js` to the
box. The worker:
- Intercepts
fetchfor`/_sw/<boxId>/<port>/…`and forwards aVirtualRequestinto the VM handler for<port>, streaming the response back. - Routes a client's later requests by client id (a
clientPortsmap), so only the entry URL carries the`/_sw/<boxId>/<port>`prefix. - Injects two shims into VM-served HTML: a path shim (strips the
`/_sw/<boxId>/<port>`prefix viahistory.replaceStatebefore app code runs, and restores it on reload viasessionStorage) and a WebSocket shim (so in-app sockets route through the worker).
Because a service worker update swaps control, a client needs one reload after
sw.js changes for the new worker to claim the page.
Tunnel frames
A WebSocketTunnel multiplexes traffic between an in-VM port
and the relay over a single WebSocket. Frames are Buffer-based (the ws
receiver reads them as binary), carrying both HTTP request/response bodies and
WebSocket-upgrade traffic. When the relay runs path-based, it prefixes
`/<port>` on the way in and strips it before the box sees the request, so
public URLs stay clean while the box still serves at its own paths.
HMR over WebSocket
Dev-server hot updates (Vite's /hot, Metro's socket) are WebSocket upgrades.
They ride the same bridge/tunnel: the upgrade is proxied to the in-VM server's
socket, and frames are relayed as Buffers in both directions, so Fast Refresh
and HMR work inside a preview or over a tunnel.
I/O streaming
Inside a box, a command's stdout/stderr are streamed incrementally (the
shell threads per-execution stream defaults so concurrent runs
don't clobber each other). At the edge, sandbox.commands.run() surfaces this as
onStdout / onStderr callbacks, and stdin is fed either as a string or, on an
interactive terminal, keystroke-by-keystroke — including raw mode for
prompt-driven CLIs. See The Node runtime for how a script's
process.stdin/stdout bind to this.