# snowglobe
> snowglobe is a Go CLI that turns a Docker image into a static website that runs it in the
> browser: real Linux on a 64-bit ARM CPU emulated in WebAssembly (armless), restored from a
> snapshot taken once the app was ready. The output is a folder of static files (GitHub Pages
> works); no server runs the program. Guests must be arm64 Alpine images (FROM alpine, built for
> arm64, or FROM arm64v8/alpine). Docker is the only requirement.
Live demos: https://filipecabaco.github.io/snowglobe/ (Elixir IEx, PostgreSQL, Elixir + Postgres,
Python + Rich, TypeScript + Zod, Go + Bubble Tea, Rust + clap, Java + Gson, curl + jq, Supabase).
Source and README: https://github.com/filipecabaco/snowglobe
## Commands
- `snowglobe build
[flags]` builds the site into `--out` (default `dist`).
- `snowglobe serve [dir] [--port N]` previews a built site on localhost.
- `snowglobe pool ` makes every site under a directory share one content-addressed blob store.
- `snowglobe pwa [--name N] [--short-name N]` makes a built site an installable Progressive
Web App: manifest, icons, and a service worker that keeps it working offline once it has run.
Run it after `pool`. Every demo on the live site is installable.
- `snowglobe pack [-o site.tar.gz]` writes a site as one self-contained tarball (.tar.gz or
.tar.zst, pooled blobs included) to publish on a CDN.
- `snowglobe run [site] [--name N] [--detach]` restores a built site headlessly in a sandboxed
container (unprivileged, read-only, memory-capped, offline unless built with a network). In a
terminal it is an interactive session (ctrl-] quits); with stdin piped, each line is typed in as a
command and the output printed as plain text. The site can be a directory, a tarball, a tarball
URL, or a deployed site's URL (e.g. https://filipecabaco.github.io/snowglobe/python/): it is
fetched into a local cache first and revalidated on later runs. `-p HOST:GUEST` (repeatable)
forwards a host port to a guest port, bound to 127.0.0.1 unless given as IP:HOST:GUEST; it needs
a site built with `--network fetch`.
- `snowglobe exec ` types one command into a detached instance and prints only what
it printed (plain text when piped): the way for scripts and agents to drive a REPL.
- `snowglobe attach ` joins a detached instance (ctrl-] detaches); `snowglobe ps` lists them;
`snowglobe stop ` throws one away.
- `snowglobe version`
## Build flags (and the image labels they override)
- `--cmd CMD`: the program to run (default: the image's ENTRYPOINT + CMD). Drops the image's ready and exercise labels.
- `--ready TEXT` / `snowglobe.ready`: snapshot once this text appears on the console (default: after 5 s of quiet).
- `--title TEXT` / `snowglobe.title`: the page title.
- `--memory MB` / `snowglobe.memory`: guest RAM (default 512).
- `--exercise CMD` (repeatable) / `snowglobe.exercise` (JSON array): commands typed after the snapshot; the files they read are preloaded.
- `--warm REGEX` / `snowglobe.warm`: extra guest paths to preload.
- `--network fetch` / `snowglobe.network`: give the guest a network (default `none`).
- `--cpus N` / `snowglobe.cpus`: guest CPUs, 1 to 8 (default 1); each runs in a Web Worker.
Example Dockerfile labels:
LABEL snowglobe.title="My app" snowglobe.ready="iex(1)> " snowglobe.memory="640" \
snowglobe.network="fetch" snowglobe.exercise='["h Enum.map"]'
## Networking (`--network fetch`)
Every request leaves as the visitor's own browser request; there is no proxy server.
- HTTP (port 80): each request becomes a fetch().
- HTTPS (port 443): the machine terminates TLS 1.3 itself (armless's web relay) with a per-build CA
that only the guest trusts, then the request is replayed with fetch("https://...").
- WebSockets (ws://, wss://): the guest's upgrade opens a browser WebSocket; messages pass through.
- In `snowglobe run`, the same requests leave from the container (through HTTPS_PROXY/HTTP_PROXY if
set); other TCP connections are made directly; private addresses are refused.
- Limits: CORS applies (servers without it answer 502), no raw TCP or UDP.
## What a built site contains
- `index.html`: the page that restores the snapshot (`?embed` hides the status strip).
- `system/state.bin.zst`: the snapshot; `system/filesystem.json` + blobs: the root filesystem over 9p.
- `system/warm.pack`: files preloaded in one request; `warm-report.json` explains what went in.
- `run.json`: measured facts of the build; `transcript.json`: the exercise commands and their output.