Yurt

A Linux sandbox that runs inside this browser tab: a shell, Python 3.14, NumPy, Jupyter. Nothing leaves the page.

Terminal

          
          
          
          
          
          online
        
$

BusyBox ash on a real PTY, on the Yurt kernel compiled to WebAssembly. Loads about 90 MB once, then boots here in the tab; the cell below starts a Jupyter kernel on the same machine. The sandbox has no network: pip install and wget fail by design (the desktop app below has both).

Run it on your machine

The same playground with the sandbox running natively: network access, and a boot measured in seconds. Or the sandbox from a shell, the way you would use docker.

Desktop app
Command line

The playground as a Mac app

Download for Apple Silicon, open the disk image and drag Yurt Playground to Applications.

It is not notarized, so macOS blocks the first open: allow it under System Settings → Privacy & Security → Open Anyway (or run xattr -dr com.apple.quarantine "/Applications/Yurt Playground.app"), then open it again.

The playground as a Linux app

Download the package for x86_64 or arm64 (Debian, Ubuntu), then:

sudo apt install ./Yurt-Playground-*.deb
yurt-playground

It opens in your browser; Ctrl-C stops it.

yurt on macOS

run, exec, ps, logs, cp, stop, kill, restart, commit, inspect, stats, images, pull — with ports, environment, a working directory and an interactive terminal; commit turns a sandbox you set up into an image to run again. Download for Apple Silicon, then:

tar -xzf yurt-*.tar.gz && sudo yurt/install.sh
yurt pull playground                       # the image (about 90 MB), no account needed
yurt run -it playground                    # a shell in the sandbox
yurt run -d --name web -p 8080:8000 playground python3 -m http.server 8000
yurt logs -f web                           # Ctrl-C leaves it running
yurt stats --no-stream web                 # CPU % and memory, docker-stats style
yurt cp ./notes.txt web:/home/user/        # curl http://127.0.0.1:8080/notes.txt
yurt commit web my-image                   # yurt run my-image … starts from that disk
yurt stop web

Everything the command needs is in the package; the daemon starts itself. yurt --help lists the verbs.

yurt on Linux

run, exec, ps, logs, cp, stop, kill, restart, commit, inspect, stats, images, pull — with ports, environment, a working directory and an interactive terminal; commit turns a sandbox you set up into an image to run again. Download the package for x86_64 or arm64 (Debian, Ubuntu), then:

sudo apt install ./yurt-*.deb
yurt pull playground                       # the image (about 90 MB), no account needed
yurt run -it playground                    # a shell in the sandbox
yurt run -d --name web -p 8080:8000 playground python3 -m http.server 8000
yurt logs -f web                           # Ctrl-C leaves it running
yurt stats --no-stream web                 # CPU % and memory, docker-stats style
yurt cp ./notes.txt web:/home/user/        # curl http://127.0.0.1:8080/notes.txt
yurt commit web my-image                   # yurt run my-image … starts from that disk
yurt stop web

Everything the command needs is in the package; the daemon starts itself. yurt --help lists the verbs.

Is this really running in your browser?

Four checks you can do yourself

No badge can prove it, so here is what a server-backed fake could not survive. Each takes under a minute, in Chrome, Edge, Firefox or Safari.

Go offline, keep computing

Start the sandbox and wait for the cell to say ready. Then turn off Wi-Fi (or, in Chrome, Edge and Firefox, pick Offline in the developer tools' Network panel). Run another cell.

It still answers. The terminal's bar shows offline while you do it.

Watch the tab work

Watch this tab in the browser's task manager: in Chrome and Edge, Shift+Esc; in Firefox, about:processes; for Safari, macOS's Activity Monitor, which lists each tab's web content process. Its memory climbs by hundreds of MB as the sandbox boots, and its CPU spikes when a cell runs.

A thin client stays flat.

See the workers

While the sandbox runs, list the page's workers: in Chrome, visit chrome://inspect/#workers (in Edge, edge://inspect/#workers); in Firefox, about:debugging#/runtime/this-firefox; in Safari, the Web Inspector's Sources tab. The coordinator and the guest workers are listed with their script URLs, and you can attach a debugger.

In the developer tools' Network panel, nothing moves while a cell runs. The page's security policy forbids talking to any other origin.

Check the bytes

The site publishes the SHA-256 of every file the sandbox runs on, the 87 MB guest filesystem included. This fetches them again and hashes them here, in your browser; the kernel and the image are also checked against the hash their release was published with.

Suspend and resume

A notebook cell continues where it stopped

A sandbox can be sealed while it runs — the kernel's memory, each program's memory and its unwound call stack — and brought back later exactly where it was: no boot, no image to stage, no program restarted. The notebook suspends and resumes a running cell; the terminal demo seals every two seconds.

In a notebook

Open the suspend/resume notebook. Its kernel is one CPython 3.14 process (the Python 3 (Yurt, suspend/resume) kernelspec), built so the sandbox can be sealed. Run the cell that prints primes, press Suspend in the panel at the bottom right: about 50 MB lands in IndexedDB in a quarter of a second and the sandbox is gone. Press Resume: the cell carries on at the next prime, in the same output, with every variable intact. Interrupt works as usual, and primes is still there afterwards.

What makes a cell suspendable is a syscall to seal at: printing progress is enough; a loop that never leaves the interpreter has to wait for its next print.

In a terminal

Open the demo. A prime finder prints one prime every quarter second, testing each candidate against the primes it has already found — its position is on the call stack, its list is on the heap, and both come back. The bar counts the seals as they land in your browser's IndexedDB (about 14 MB, under 50 ms each).

Close the tab. Open the page again: it says which seal it restored from, draws a marker, and the next prime is the one after the last sealed one. Forget the snapshot drops the image so the next load boots fresh.

What is saved, and when

Nothing happens at close: the browser gives a closing tab no chance to seal. Every seal overwrites the one before, so a restore is at most one interval behind, and the interrupted syscall runs exactly once, after the restore.

The image stays on this origin, in this browser, and is tied to the kernel build that sealed it: after an update it is dropped and the page boots fresh.

Use it from code

On the JS host, KernelHostInterface.sealSandbox() returns the image and lets the sandbox continue; KernelHostInterface.restore(kernelWasm, image) brings it back in a fresh host with the processes ready to run. A program opts in by being built with Asyncify on yurt.syscall (it exports yurt_asyncify_syscall); a seal of anything else is refused rather than guessed at.

Today: one thread per program, terminals and timers carried; sockets, in-flight host I/O and the stock image's shell are not yet. See src/snapshot_worker.ts for the whole loop in a page, and src/notebook_kernel_worker.ts for the notebook kernel around a sealable CPython (scripts/build-python-seal.sh builds it).