quixand — Local & Pluggable Sandboxes for AI Code Execution
Quixand is a local-first sandbox and code interpreter library. It mirrors the developer-facing API of E2B’s Sandbox and AsyncSandbox while running locally by default via Docker/Podman, with a clean adapter interface to plug in other backends (your infra, remote HTTP, etc.).
Why quixand?
- Familiar: mirrors E2B’s concepts and method names where practical
- Local-first: default execution in local containers (Docker/Podman)
- Pluggable: unified
Adapterprotocol for custom backends - Async & streaming ready; simple, secure, fast
- CLI parity for everyday workflows
Requirements
- Python 3.10+
- Docker or Podman installed and in PATH
Install
Editable install with Docker extras:
python3 -m venv .venv
. .venv/bin/activate
pip install -U pip
pip install -e '.[docker]'
Configuration
Quixand works out of the box, but you can configure via environment variables or code.
Environment variables:
QS_ADAPTER:local-docker(default) |chutes|remote-httpQS_TIMEOUT_DEFAULT: default timeout seconds (default 300)QS_IMAGE: default container image (defaultpython:3.11-slim)QS_RUNTIME:docker|podman(auto-detects if unset)QS_ROOT: host directory for state/template cache (default~/.quixand)QS_METADATA: JSON string to tag sandboxes
Programmatic config:
import quixand as qs
cfg = qs.Config(timeout=600, image="python:3.11-slim")
Python API
Top-level imports:
from quixand import Sandbox, AsyncSandbox, connect, Templates
Sync Sandbox:
import quixand as qs
sbx = qs.Sandbox(template="python:3.11-slim", timeout=600, metadata={"user":"alice"})
sbx.files.write("hello.txt", "hi!")
print([f.path for f in sbx.files.ls(".")])
res = sbx.run(["python","-c","print(2+2)"])
print(res.text) # "4\n"
execn = sbx.run_code("x=1\nx+=1\nprint(x)")
print(execn.text.strip()) # "2"
sbx.shutdown()
Async Sandbox:
import asyncio, quixand as qs
async def main():
sbx = await qs.AsyncSandbox.create(template="python:3.11-slim")
await sbx.files.put("data.csv", "/workspace/data.csv")
res = await sbx.run(["python","-c","print(42)"])
print(res.text)
await sbx.shutdown()
asyncio.run(main())
Connect to running sandbox:
sbx1 = qs.Sandbox(template="python:3.11-slim")
sid = sbx1.id
sbx2 = qs.connect(sid)
print(sbx2.status())
Filesystem API:
sbx.files.write("/workspace/notes.txt", "hello") # text
sbx.files.write("/workspace/blob.bin", b"\x00\x01", mode="binary")
print(sbx.files.read("/workspace/notes.txt")) # returns str
print(sbx.files.read("/workspace/blob.bin", mode="binary")) # returns bytes
sbx.files.mkdir("/workspace/data", parents=True)
sbx.files.put("./local.txt", "/workspace/local.txt")
sbx.files.get("/workspace/local.txt", "./local_copy.txt")
sbx.files.rm("/workspace/local.txt")
paths = sbx.files.glob("/workspace/*.txt")
Process execution:
res = sbx.run("echo $USER && uname -a")
print(res.exit_code, res.text)
Python code convenience:
sbx.install_pkg("pandas==2.2.2")
execn = sbx.run_code("import pandas as pd; print(pd.__version__)")
print(execn.ok, execn.text)
Networking and ports (adapter-dependent):
binding = sbx.expose(port=8000, host_port=18000, proto="tcp")
print(binding)
Timeouts and lifecycle:
st = sbx.status() # created_at, last_active_at, timeout_at
sbx.refresh_timeout(900) # extend timeout
sbx.shutdown() # stop container and clean state
CLI
The qs CLI mirrors E2B’s verbs.
qs --help
# Sandbox lifecycle
qs sandbox create --template python:3.11-slim --timeout 900 --env FOO=bar --env BAZ=qux
qs sandbox ls
qs sandbox connect <id>
qs sandbox exec <id> -- echo hello
qs sandbox run-code <id> --code "x=1; print(x+1)"
qs sandbox refresh-timeout <id> --seconds 600
qs sandbox kill <id>
# Files
qs files ls <id> /workspace
qs files put <id> ./local.txt /workspace/local.txt
qs files get <id> /workspace/local.txt ./local_copy.txt
qs files mkdir <id> /workspace/data --parents
qs files rm <id> /workspace/local.txt --recursive
# Templates
qs templates build ./path --name py311-tools
qs templates ls
qs templates rm py311-tools
Templates
Quixand supports building images from e2b.Dockerfile or Dockerfile and caching references locally under ~/.quixand/templates.
qs templates build ./examples --name py311-tools
qs templates ls
In code:
from quixand import Templates, Sandbox
img = Templates.build("./examples", name="py311-tools")
sbx = Sandbox(template=img)
Adapters
Adapters implement a common protocol in quixand.adapters.base.Adapter. Included:
LocalDockerAdapter(default) — local Docker/Podman containersChutesAdapter(skeleton) — wire to your infraRemoteHTTPAdapter(skeleton) — call a remote service
Select adapter via QS_ADAPTER or passing an instance to Sandbox(adapter=...).
State and Timeout Enforcement
Quixand keeps a local registry at ~/.quixand/state.json. A lightweight watchdog process enforces idle timeouts and cleans up containers and state when deadlines are exceeded. Default timeout is 300s; refresh with refresh_timeout() or CLI.
Security Defaults
- Non-root user inside container (image-dependent)
- No privileged flags
- Bridge network by default (configurable);
nonesupported - CPU/memory/pids limits supported by the adapter
- Ephemeral writable layer
Troubleshooting
- Docker/Podman not found: ensure one is installed and in PATH
- Permission errors: add your user to the
dockergroup (Linux) or run Docker Desktop - Hanging exec or timeouts: increase
timeoutinrun()orQS_TIMEOUT_DEFAULT - Unicode output:
CommandResult.textdecodes UTF-8 with replacement; usestdoutbytes for raw data
Examples
See examples/:
minimal.pyasync_minimal.pytemplate_build.pystreaming.py(PTY placeholder; streaming to be implemented)
Roadmap
- Full PTY streaming
- Richer RemoteHTTP/Chutes adapters
- Test suite and CI
License
MIT
Metadata
Release files for quixand 0.1.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| quixand-0.1.5.tar.gz | 144.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| quixand-0.1.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 183.2 kB
Release files / quixand-0.1.5.tar.gz
| Download URL | quixand-0.1.5.tar.gz |
|---|---|
| Size | 144.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c1be96f0e53248ee00f87d104686fa494f52344c4d9501a44ebf58fa0c13aaf0
|
|
BLAKE2b-256 checksum How to use checksums |
870026b196de258c1f93496391746d6263c448a8d0223cbe3cf3c0296c865776
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.10.15
|
Release files / quixand-0.1.5-py3-none-any.whl
| Download URL | quixand-0.1.5-py3-none-any.whl |
|---|---|
| Size | 38.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b0a325b86b4773361fff7ba3353a1b8f5e26f19f6205a3bd451f9de192e45933
|
|
BLAKE2b-256 checksum How to use checksums |
5d1b06200803e7dcfe13b49ff6d3c91849c0b0134fb9d6761e8e96835f5bbbbf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.10.15
|