Skip to main content

external-sandbox-quicksand

A Python execution server for coding agents that runs commands inside a Quicksand QEMU virtual machine. It implements the shared External Sandbox protocol, alongside external-sandbox-v86 and external-sandbox-container: the same buffered and streaming shell APIs, Codex endpoint, browser terminal, host mounts, and allow-listed HTTP(S) proxy, with a full virtual machine as the sandbox.

Start

Requirements: uv and a host supported by Quicksand. This release is tested on Apple silicon macOS with HVF acceleration; Quicksand publishes no QEMU wheel for Intel Macs. QEMU and the Alpine image arrive as Python wheels; no Homebrew, Docker or separate VM tooling is needed.

uvx external-sandbox-quicksand --preserve-paths \
  --mount-rw project=/absolute/path/to/project \
  --mount-ro reference=/absolute/path/to/reference

uvx downloads and runs the latest release from PyPI, including about 100 MB of QEMU and Alpine image wheels on first use. To install the command permanently, use uv tool install external-sandbox-quicksand. From a source checkout, use uv run external-sandbox-quicksand instead.

The server listens on 127.0.0.1:8688, so it can run beside the v86 (8686) and container (8687) servers. Mounts appear at /mnt/project and /mnt/reference; --preserve-paths also binds them at their original absolute host paths for clients that send host paths. The first mount is the default working directory; without mounts it is /workspace. Stop the server with Ctrl-C to shut down and discard the VM. Files in writable mounts persist on the host; all other guest changes are disposable.

The VM gets two CPUs and 1 GiB of memory by default (--cpus, --memory). --accel tcg forces software emulation when hardware acceleration is unavailable. Run uvx external-sandbox-quicksand --help for every option.

Guest images

Alpine (default). Quicksand's Alpine 3.23 image lacks Bash and Python, which the protocol and agent need. On first use, the server boots that image once, installs bash, python3, git and ripgrep from https://dl-cdn.alpinelinux.org through its own allow-listed proxy (only GET/HEAD to that origin), and saves the result as a Quicksand save under ~/.cache/external-sandbox-quicksand/images/. On an Apple silicon Mac this took about seven seconds and produced a 65 MB save; later starts boot it directly and serve requests in about a second.

uvx external-sandbox-quicksand --prepare-image                  # prepare now, then exit
uvx external-sandbox-quicksand --packages git,ripgrep,nodejs,npm,uv,jq

--packages replaces the optional package list (bash and python3 are always included) and prepares a separately cached image. The cache key covers the Quicksand Alpine build, architecture and package list, so a Quicksand upgrade prepares a fresh image. Each prepared image records its installed package versions in external-sandbox-image.json. Alpine package versions come from the 3.23 branch at preparation time. Set --cache-dir or XDG_CACHE_HOME to move the cache.

Ubuntu 24.04 (source checkout only). Quicksand's Ubuntu image already includes Bash and Python 3.12. Its image wheel (about 300 MB) comes from Microsoft's Quicksand package index rather than PyPI, so it is not part of the published package. From a source checkout, the ubuntu dependency group installs it:

uv run --group ubuntu external-sandbox-quicksand --image ubuntu

--image also accepts a Quicksand save directory, provided it has /bin/bash and python3.

How it works

host: external-sandbox-quicksand (aiohttp)                    guest: Quicksand VM
  /exec, /exec/ws, /codex ── virtio-serial port ───────────▶ guest_agent.py (python3)
  9p fsdev (readonly=on for ro mounts) ─────────────────────▶ /mnt/<name>
  host proxy ◀── bridge ◀── QEMU guestfwd stream ◀── guest-mux ◀── 127.0.0.1:3128 ◀── HTTP(S)_PROXY

Quicksand boots the VM and its own guest agent installs this server's agent from the Python package. The private agent speaks length-prefixed JSON over a dedicated virtio-serial port, separate from Quicksand's agent channel and unreachable through guest networking. It runs each command as /bin/bash -c in its own process group, streams binary output, and performs Codex filesystem operations inside the guest. These modules, the HTTP/WebSocket/Codex adapters, and the proxy engine are shared with external-sandbox-container; see PROVENANCE.md.

Host directories use QEMU virtio-9p mounts. Read-only mounts set QEMU's readonly=on, so the host rejects writes regardless of guest privileges. The VM starts with Quicksand's NetworkMode.NONE, which removes its default NIC, host agent port forward and SMB relay.

Browser terminal

Open http://127.0.0.1:8688/ to run commands through the streaming WebSocket API, with stdin, EOF, cancel, history and Copy session. Each run starts a new Bash process; files persist for the VM's lifetime. The terminal uses pipes, not a PTY. The HTML, CSS and JavaScript are a pinned bundle of external-sandbox-ui 0.3.0; update it with that repository's scripts/sync.py, as described in its README.

Controlled network access

Without --proxy-config the VM has no network interface. With a policy file, it gets exactly one: a QEMU user-mode NIC with restrict=on and a single guestfwd rule, 10.0.2.101:3128, connected to a host stream. Save this starter policy as proxy.json; it allows only GET and HEAD requests to https://example.com:

{
  "allow": [
    {"origin": "https://example.com", "methods": ["GET", "HEAD"]}
  ]
}
uvx external-sandbox-quicksand --proxy-config proxy.json

QEMU's guestfwd TCP backend is one persistent byte stream, so a small static Go helper (guest-mux, from the Quicksand proxy prototype) listens on guest 127.0.0.1:3128 and multiplexes each connection over that stream. The host bridge opens one connection per channel to the embedded host proxy and nowhere else. Commands receive standard proxy variables, NODE_USE_ENV_PROXY=1, and a CA bundle containing the proxy's public certificate.

The proxy, policy format, management page and API are the same as the container server's and follow the protocol's proxy profile: exact-origin allow rules with optional methods, paths and CIDRs; public addresses only by default; credential placeholders replaced on the host; secret redaction; and live policy edits at http://127.0.0.1:8688/proxy that close existing connections. examples/proxy.json demonstrates credential substitution and needs OPENAI_API_KEY in the server's environment. Literal redaction cannot prevent an allowed upstream from transforming and echoing a credential, so use credential rules only with trusted services and narrow their methods and paths. Removing proxy variables cannot open another route: QEMU drops everything except the guestfwd stream. If the guestfwd stream fails, proxy access fails closed until restart; /health reports proxyTransport.

Agents

Agent integrations are maintained in external-sandbox:

  • Claude Code: set EXTERNAL_SANDBOX_API_URL=http://127.0.0.1:8688 and start this server with --preserve-paths --mount-rw project=$PWD.
  • Codex: use executor URL ws://127.0.0.1:8688/codex.
  • Pi: use --sandbox-url http://127.0.0.1:8688. The default Alpine image includes the Python and ripgrep its tools need.

Scope

  • Commands and Codex filesystem operations run in the VM as guest root. The VM is the isolation boundary; guest root cannot change QEMU's mount modes or network restrictions.
  • Mounts deliberately expose host directories. Read-write mounts change host files immediately; host changes are visible in the guest. Guest root can follow symlinks only within the guest view. A pre-existing host hardlink in a writable mount remains editable even if another name for the same file is in a read-only mount; avoid that alias if the file must stay unchanged. This backend does not reproduce v86's hardlink checks.
  • The guest agent and proxy helper are not trusted by the host. The host bounds frame sizes and validates messages; a compromised guest can disrupt its own commands or proxy access. The host does not provision real credentials to the VM, but an allowed upstream can transform and echo one in its response.
  • Quicksand's own guest agent remains in the VM and is used only by the host for setup.
  • Agent processes, model traffic and host-side tools remain on the host; routing shell commands into the VM does not constrain those tools.
  • The execution listener is for trusted local clients, with the same browser token, Origin, Fetch Metadata and Host checks as the other servers. It is not authentication between mutually untrusted host users or a remote multi-user service.

Test

uv run pytest -m 'not integration'
node --test tests/test_terminal_js.mjs
uv run ruff check .

Tests marked integration boot real VMs and are opt-in:

RUN_QUICKSAND_TESTS=1 uv run pytest -m integration
RUN_QUICKSAND_TESTS=1 SANDBOX_TEST_IMAGE=ubuntu uv run --group ubuntu pytest -m integration
RUN_QUICKSAND_TESTS=1 RUN_SANDBOX_TOOL_IMAGE_TESTS=1 uv run pytest tests/test_proxy_integration.py
EXTERNAL_SANDBOX_CLIENT=/absolute/path/to/external-sandbox/claude-code/external_sandbox_exec.py \
  RUN_QUICKSAND_TESTS=1 uv run pytest tests/test_legacy_client.py tests/test_real_clients.py

The tool-image tests prepare an Alpine image with Node, npm and uv and check they reach a synthetic HTTPS service only through the proxy. The mux bridge tests also compile guest-mux for the host when Go is installed. To rebuild the bundled helpers, run guest-mux/build.sh; Go 1.27.2 reproduces the committed binaries exactly.

See verification for recorded results, including the shared conformance suite.

Metadata

Release files for external-sandbox-quicksand 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for external-sandbox-quicksand 0.1.0
File Size Uploaded Provenance Actions
external_sandbox_quicksand-0.1.0.tar.gz 2.1 MB With provenance Details for external_sandbox_quicksand-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for external-sandbox-quicksand 0.1.0
File Interpreter ABI Platform Actions
external_sandbox_quicksand-0.1.0-py3-none-any.whl Python 3 none any Details for external_sandbox_quicksand-0.1.0-py3-none-any.whl

Total release size: 4.1 MB

Release files / external_sandbox_quicksand-0.1.0.tar.gz

Download URL external_sandbox_quicksand-0.1.0.tar.gz
Size 2.1 MB
Tags Source
SHA-256 checksum
How to use checksums
d68fc211ed2811b63a65d7a91a36729b18f17e2322519f700f86c1ede9e596da
BLAKE2b-256 checksum
How to use checksums
df9d90186a4e7493d466d15dac7318cfd447134c0d2c37432299b986513df455
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Release files / external_sandbox_quicksand-0.1.0-py3-none-any.whl

Download URL external_sandbox_quicksand-0.1.0-py3-none-any.whl
Size 2.0 MB
Tags Python 3
SHA-256 checksum
How to use checksums
fa8dfcb8b6ddb19ad1ef5dff77a432f087777217add1303658ac91b0de7dd51f
BLAKE2b-256 checksum
How to use checksums
182d9167955b4304f48fe0155d4f46d0069f055d1c44504537f8bee0dcc6172c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Security for external-sandbox-quicksand 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for external_sandbox_quicksand-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page