Playground: sharness-ai in Pyodide and in Linux
Two runtimes, one page, nothing installed on your machine. On the left, Pyodide: Python compiled to WebAssembly, where pip install sharness-ai installs the CLI and you can hash, check and translate Process Behaviour Policies. Switch to Linux: Alpine with a Landlock kernel, booted by v86 in this tab, where every sharness-ai run is enforced by a real kernel. On the right, one policy, hashed by three implementations.
run and hello refuse there with exit 126: there is no child to confine, and the CLI says so rather than pretend. In Linux every policy is applied by the shell's sharness-ai to its child: a Boundary for the child, a Setting for the shell, which could have skipped it. The kernel is real; the CPU is emulated, so a command takes seconds. The wheel is the CLI's dev branch, versioned +dev.<commit>; PyPI carries only releases. Built and tested as of 25 Oct 2026. The milestone behind it: Linux in a tab.Things to try
- Pyodide:
pip install sharness-ai, thensharness-ai policy translate policies/p1-agent-session.json --to claude-code: the same policy as Claude Code sandbox settings, followed by what that sandbox cannot enforce. Thensharness-ai hello, and read why it refuses. - Linux:
sharness-ai hello(six probes under a deny-all policy, 6 of 6 when the kernel holds), then the chips: a write refused under P2, port 80 refused under P1, 200 MiB refused under P0's 64 MiB cap, a wrong--expect-hashthat runs nothing. - Your own policy: edit the JSON on the right, press Hash it in three places, then Use it, and run something under
policies/my-policy.jsonin Linux. Remove/libfromexecand watch every dynamic binary fail to start: the ELF interpreter needs the execute right too.
How it is put together
| Pyodide tab | Linux tab | |
|---|---|---|
| Runtime | Pyodide 314.0.7: CPython 3.14 in WebAssembly, from jsDelivr | v86 0.5.472 (x86 in WebAssembly) running Alpine 3.23 x86, linux-virt 6.18.58, Python 3.12 |
| Install | micropip installs sharness_ai-…+dev.<commit>-py3-none-any.whl from this site | baked into the initramfs by build_guest.py |
| Terminal | a line-oriented terminal that calls sharness_ai.cli.main and captures its output | the guest's /bin/sh on the serial line (sharness.mode=shell on the kernel command line) |
| Enforces | nothing: no kernel | Landlock files, exec and TCP ports, an empty network namespace, rlimits and cgroups |
| Getting a file in | the Pyodide filesystem, directly | base64 over the serial line, in shell-sized chunks |
The code is one ES module, playground.js, in the CLI repository's experiments/browser/, with a headless test that drives both tabs (test_playground.mjs: Pyodide loads and installs, four commands give the expected output, the guest boots to a shell, hello holds 6 of 6, a write is refused, the three hashes agree). The scripted run of the same guest, without a shell, is the E09 page; what it shows and does not is on Linux in the browser.