Serving the page
Two headers, and only if you want the guest to have network:SharedArrayBuffer, and without that the guest
cannot receive bytes while it is running. The sandbox still boots and still runs
commands. It just has no network.
That is the whole setup for a page that loads the SDK as plain ES modules. It
finds its own worker and wasm, and Sandbox.create() needs no arguments.
If you build with a bundler
Import the browser entry by name, so the bundler cannot pick the Node build:dist/ into whatever your app serves
statically, usually public/, and name the three files the SDK fetches at
runtime. A bundler cannot emit them for you, and the address the SDK would
otherwise work out for itself does not survive the build:
Wire the copy to a
prebuild script. Done by hand, the next upgrade leaves an old
engine behind a new API.What the network can reach
sandbox.network reports the backend in use and what it implies:
In a browser every request the guest makes leaves as a
fetch from your page, so
the browser’s rules apply to it. A host that sends no access-control-allow-origin
comes back to the guest as a 502, which is correct behaviour rather than a bug
to engineer around.
uv pip install works because PyPI is CORS-open, and apk add does
not because Alpine’s mirrors are not. Anything you control can be made reachable
by sending the header. For everything else, there is corsProxy.
Reaching hosts that do not send CORS headers
Most of the web sends noaccess-control-allow-origin, which puts curl targets,
git clone and Alpine’s mirrors out of reach. Pointing the guest’s traffic at a
relay you run brings them back:
Caching
Two separate caches make the second visit fast, and neither needs configuration. The snapshot lands in origin-private storage on first boot, so later visits skip the download entirely. See Snapshots for inspecting and clearing it. The 25 MB engine is compiled once and kept in the browser’s own code cache, keyed to the URL it was fetched from, so a returning visitor reuses the compiled module instead of recompiling it. Measured in Chrome against a production build, fetching and compiling the engine goes from 527ms on a cold profile to 64ms on a later visit. Nothing needs configuring for that.Cache-Control: public, max-age=0 with an
ETag, which is what most static file servers already send, is enough: the
conditional request comes back 304 and the compiled module stays attached to
the cache entry.
Origin-private storage is site data, not the HTTP cache. Clearing cached images
and files in the browser’s settings does nothing to it, which trips up anyone
trying to force a fresh download by hand.
Limits
These follow from the browser, not from vpod:- No streaming.
commands.run()resolves once the command has finished and returns all of its output. There is no byte stream to pipe into a terminal, so anything needing a live tty (vim,top, a barepythonREPL) will sit there until you stop it. You no longer have to wait out its timeout to do that: see Interrupting a command. - No raw sockets. SSH, databases on custom ports, and anything not speaking HTTP over 80 or 443 are out of reach.
- CORS decides which hosts answer. Covered above, along with the
corsProxythat gets around it.