The Command Line
Code Sandboxes ships a Typer CLI that does two things: it RUNS code in any
variant — exec for one snippet, repl for a prompt — and it MANAGES
sandboxes across variants, with the same four verbs everywhere. Running comes
first below, managing after it.
exec and repl create a sandbox, use it, and terminate it. Nothing is left
running behind you — to keep one, see create.
For canonical variant setup details (requirements, credentials, and parameters), see Providers. For package installation and extras, see Installation.
code-sandboxes exec --variant eval 'print(40 + 2)'
code-sandboxes repl --variant jupyter-server
code-sandbox is available as an alias for the same command.
Running Code​
exec — one snippet​
The code comes from an argument, from --file, or from standard input:
code-sandboxes exec -v eval 'x = 40; x + 2'
code-sandboxes exec -v eval --file analysis.py
echo 'print("hello")' | code-sandboxes exec -v eval
The exit status is the code's own — 0 when it ran cleanly, 1 when it
raised or the sandbox could not run it — so this composes in a shell. With
--quiet nothing is printed but what the code produced:
if code-sandboxes exec -v eval -q 'import numpy' ; then
echo "numpy is there"
fi
repl — a prompt​
# Explicit variant
code-sandboxes repl --variant monty
# Or omit it and choose interactively
code-sandboxes repl
# Datalayer with overrides
code-sandboxes repl --variant datalayer --token "$DATALAYER_API_KEY" \
--run-url "https://prod1.datalayer.run"
State is kept between lines, and the value of an expression is shown as a REPL shows it. The prompt names the sandbox the line will run in, which matters as soon as two are open side by side:
sandbox(daytona:tan-law-5384)>>> x = 40
sandbox(daytona:tan-law-5384)>>> x + 2
42
Use any of :exit, :quit, exit, quit or Ctrl+D to leave, and :help
for a reminder. On exit, the sandbox is terminated.
Examples At The Prompt​
A sandbox can carry snippets worth running in it, and the prompt offers them:
sandbox(daytona:tan-law-5384)>>> :examples
# 1. What the GPU is, straight from the driver
import subprocess
print(subprocess.run(["nvidia-smi"], capture_output=True, text=True).stdout)
# 2. A workload that actually uses it: a matmul, timed on the device
import time, torch
...
:examples:N runs one of them — 1 to 5.
Two ways to use one. Copy it into the prompt, or ask for it by number and let
the prompt run it — :examples:2 prints the snippet and then executes it, so
the answer arrives with its cause above it:
sandbox(daytona:tan-law-5384)>>> :examples:2
>>> code:
import time, torch
...
'312.4 TFLOP/s'
They are declared once, where the sandbox is made, because what is worth running depends on what was created — a sandbox with an H100 in it wants device discovery, one that runs in this very process wants neither:
from code_sandboxes import Sandbox, run_repl
examples = [
("What the GPU is", "import torch\ntorch.cuda.get_device_name(0)"),
("How much memory it has", "import torch\ntorch.cuda.mem_get_info()"),
]
with Sandbox.create(variant="daytona", gpu="H100", examples=examples) as sandbox:
run_repl(sandbox) # reads them off the sandbox
run_repl(sandbox, examples=[...]) still overrides them for one prompt, and
:help lists both forms whenever a sandbox has any.
A snippet asked for by number runs as ONE execution, so it may contain a
for or a def. A snippet meant to be PASTED cannot: the prompt reads a line
at a time, and a block would arrive without its body. The examples that ship
with this package are written to be safe either way.
Every REPL example under examples/repl ships its own set; see
Examples.
Variant Selection​
Supported variants:
cloudflarecoreweavedatalayerdaytonadockere2bevalgoogle-colabjupyter-serverkagglemodalmonty
exec and repl take the same options: --variant, --timeout,
--environment, --gpu, --spot, and the connection settings a variant
needs.
Variant-specific Behavior​
cloudflare: talks to a deployed sandbox bridge Worker, named byCLOUDFLARE_SANDBOX_API_URLwith theCLOUDFLARE_SANDBOX_API_KEYit was deployed with. Each snippet — and each line of the REPL — runs in a process of its own, so a definition on one line is gone by the next.coreweave: starts a CoreWeave sandbox and holds a session process for it, so lines share a namespace.--gputakes a CoreWeave GPU kind; CoreWeave places a sandbox on runners of one kind only, so if several are given comma-separated the first one is used rather than walked as a fallback list.daytona: starts a Daytona sandbox;--gputakes Daytona's own flavors (H100,H200,RTX-4090, ...), several of them comma-separated to fall back along, and--spotruns on preemptible capacity.e2b: starts an E2B microVM from the interpreter's own template,code-interpreter-v1. Another is named from Python withtemplate=, and has to be built on top of it — only a template carrying a Jupyter kernel can serve this variant. State persists between lines.google-colab: prompts for runtime URL, kernel ID, and proxy token.jupyter-server: starts a managed local Jupyter server on a random port.kaggle: supports either interactive runtime settings or credential-based batch execution.modal: starts a Modal sandbox container.monty: starts a Monty REPL-backed sandbox.
The Same Machinery, From Python​
The CLI has nothing of its own: exec and repl are show_and_run and
run_repl, which the package exports and every example uses.
from code_sandboxes import Sandbox, run_repl, show_and_run
with Sandbox.create(variant="eval") as sandbox:
show_and_run(sandbox, "x = 40")
result = show_and_run(sandbox, "x + 2") # prints the code, then "42"
assert result.text == "42"
with Sandbox.create(variant="eval") as sandbox:
run_repl(sandbox) # the prompt, on your own sandbox
show_code, show_result and repl_prompt are exported too, for a program
that wants the pieces rather than the whole.
Creating and Managing Sandboxes​
Beyond running code, sandboxes can be created, listed, inspected and deleted — from the CLI or from Python. Every variant answers the same four verbs:
| Verb | CLI | Python |
|---|---|---|
| Create | code-sandboxes create -v <variant> | get_manager(variant).create(**kwargs) |
| Read (all) | code-sandboxes list [-v <variant>] | get_manager(variant).list() |
| Read (one) | code-sandboxes get <id> -v <variant> | get_manager(variant).get(id) |
| Update | code-sandboxes update <id> -v <variant> [...] | get_manager(variant).update(id, **changes) |
| Delete | code-sandboxes delete <id> -v <variant> | get_manager(variant).delete(id) |
CLI​
# Every variant that answers, in one table
code-sandboxes list
# One variant
code-sandboxes list -v kaggle
code-sandboxes list -v modal
# One sandbox, with its live status
code-sandboxes get sb-XXXX -v modal
code-sandboxes get my-kernel-slug -v kaggle # user/slug or bare slug
# Update — what changes depends on the variant
code-sandboxes update <container> -v docker --name proud-noether
code-sandboxes update sb-XXXX -v modal --tag team=ai --tag env=dev
code-sandboxes update <runtime-uid> -v datalayer --capability gpu
code-sandboxes update my-kernel -v kaggle --code "print('v2')"
# Delete (asks for confirmation without --yes)
code-sandboxes delete sb-XXXX -v modal --yes
# Create a sandbox and leave it running, detached from this process
code-sandboxes create -v modal
code-sandboxes create -v docker
code-sandboxes create -v jupyter-server --server-url http://localhost:8888
# The environments sandboxes can be created in
code-sandboxes environments
Results are rendered as tables (via rich). When
list is asked for every variant, a variant that cannot answer — no
credentials, no daemon, no runtime URL — is skipped with the reason printed
below the table instead of hiding the ones that answered.
Python​
from code_sandboxes import get_manager, manageable_variants
print(manageable_variants())
# ['cloudflare', 'coreweave', 'datalayer', 'daytona', 'docker', 'e2b', 'eval', 'google-colab', 'jupyter-server', 'kaggle', 'modal', 'monty']
manager = get_manager("modal")
for info in manager.list():
print(info.id, info.status)
info = manager.create() # detached: keeps running after this process
manager.delete(info.id)
Connection settings go to get_manager and stay out of the verbs:
get_manager("jupyter-server", server_url="http://localhost:8888", token="...")
get_manager("google-colab", server_url="https://...", proxy_token="...")
get_manager("modal", app_name="code-sandboxes")
get_manager("daytona", api_key="dtn_...", target="eu")
get_manager("e2b", api_key="e2b_...", domain="e2b.example.com")
get_manager("coreweave", api_key="...", base_url="https://api.cwsandbox.com")
get_manager("cloudflare", api_url="https://...workers.dev", api_key="...")
get_manager("kaggle", username="...")
get_manager("datalayer", token="...", run_url="https://...")
What each variant maps to​
| Variant | A sandbox is | Update changes | Delete removes |
|---|---|---|---|
cloudflare | a container of a deployed sandbox bridge | — nothing in place | the Cloudflare sandbox |
coreweave | a sandbox of the CoreWeave organization | — nothing in place | the CoreWeave sandbox |
datalayer | a runtime of the Datalayer platform | the capabilities | the runtime |
daytona | a sandbox of the Daytona organization | the labels | the Daytona sandbox |
docker | a container labelled code-sandboxes | the name | the container (forced) |
e2b | a sandbox of the E2B account | — nothing in place | the E2B sandbox |
eval, monty | an object inside the creating process | — not supported | — not supported |
google-colab | a kernel of the Colab runtime | — nothing in place | the kernel |
jupyter-server | a kernel of the Jupyter Server | — nothing in place | the kernel |
kaggle | a kernel on kaggle.com (batch mode creates one per run) | the code (a new version) | the kernel |
modal | a Modal sandbox of the code-sandboxes app | the tags | the Modal sandbox |
Not every provider can honour every verb. A manager states what it supports
through manager.capabilities and raises SandboxManagementError with the
reason for the rest — an eval sandbox lives and dies inside the process that
made it, so list truthfully answers [] and delete explains itself rather
than silently doing nothing.
Cloudflare notes: create, get and delete are answered; list is not.
The sandbox bridge exposes a sandbox by its id and has no endpoint that
enumerates them, so code-sandboxes list -v cloudflare raises with that
reason instead of answering with an empty list — "none" and "cannot know" are
different facts, and a management tool that confuses them loses sandboxes.
Kaggle notes: list enumerates your kernels (mine=True), get adds the
live run status, and create pushes a batch kernel with the given --code —
creation on Kaggle is a code push. Both user/slug and a bare slug of
the authenticated user are accepted as ids.