Code Sandbox guide
This is the end-to-end walkthrough for Code Sandbox, the platform's isolated, single-use execution environment. It assumes only an account and a project, and it takes you from zero to running untrusted code safely.
What Code Sandbox is
Code Sandbox runs untrusted Python inside a fresh, short-lived, isolated environment that is destroyed after the run. Nothing the code does can affect your project, other users, or the platform.
It is reached in exactly two ways, both from inside an agent:
- Code the model writes — the agent lists the
run_pythontool; when the model decides a calculation or a parser is needed, the snippet runs in a sandbox and the printed output comes back as the tool result. - Tool code you wrote — every tool function in your agent is relocated into a sandbox before the agent serves traffic, automatically, so a tool the model calls cannot read the agent's credentials or reach internal services.
There is no way to hand the sandbox a script from outside an agent (a workflow tool where users paste Python, a plugin runner). It is an agent capability, not a standalone service.
Quick start: your first sandboxed run
Code Sandbox is reached from inside a workload, not from your laptop. There is no public endpoint and no separate CLI group: the sandbox service is internal on purpose, because the whole point is that untrusted code runs somewhere your project cannot be reached from.
There are two ways to use it, and most people want the first.
1. Let an agent run code (run_python)
Give your agent the run_python tool. The model writes Python, the platform
runs it in a fresh sandbox, and the output comes back as the tool result.
# agent.py
from google.adk.agents import LlmAgent
from crusoe_adk.tools import run_python
root_agent = LlmAgent(
name="analyst",
tools=[run_python],
instruction="When a question needs arithmetic or parsing, write Python and run it.",
)
Deploy the agent, then ask it something that needs computing:
platformctl invoke analyst 'What is the 40th Fibonacci number?'
The sandbox is created for that one call and destroyed after it, unless the
agent has CODE_INTERPRETER_SESSION=true, in which case every call in one
conversation shares an interpreter (see the overview).
The deadline is CODE_INTERPRETER_TIMEOUT_S seconds (1–120, default 20), and
the model is told the number.
2. Your own tool code runs sandboxed
Any tool function you write is relocated into a sandbox before the agent serves
traffic. This is always on, and it is a property of every agent rather than a
setting: you do not call anything, your tool keeps its ordinary Python signature,
and the platform moves where it executes. TOOL_SANDBOX is a platform-owned
variable - the environment route refuses it - so there is no customer switch to
turn isolation off.
A tool that cannot be isolated fails startup under TOOL_SANDBOX=true
rather than quietly running unisolated. That is the intended behaviour: silently
downgrading the isolation of code you believed was sandboxed is worse than
refusing to start.
Calling the API directly from a workload
If you are writing your own harness, post to $SANDBOX_URL - an address the
platform injects into every workload:
import os, httpx
from crusoe_core.adapter import internal_caller_headers
r = httpx.post(
f"{os.environ['SANDBOX_URL'].rstrip('/')}/v1/execute",
json={"language": "python", "code": "print(6*7)", "timeout_s": 20},
headers=internal_caller_headers(), # the project's own internal token
timeout=30.0,
)
print(r.json()["stdout"]) # 42
The full request and response fields are in Run code.
SANDBOX_URL resolves only inside the platform, and /v1/execute is gated on
your project's internal token. A 401 here almost always means the agent
predates per-project tokens - redeploy it, so the control plane mints it a
current one.
Core concepts
- A run is one execution of one code snippet. It is single-use: starts fresh, runs, returns output, is destroyed.
- The language is Python 3.12.
languagedefaults topython, and anything else is refused with400 unsupported language "<x>". - A pool is a fleet of environments the platform keeps warm, so a snippet does not pay cold-start on every call. It is maintained automatically and there is no knob for it. Tool calls have no warm pool - each one waits for a sandbox to be placed and the agent's image pulled, so expect seconds rather than one.
- Network access differs by kind: a snippet sandbox has none at all (not even DNS), a tool-call sandbox reaches the public internet with private ranges blocked.
- A snippet brings its own dependencies in its code, not through a package list: a snippet sandbox has no internet and so cannot install from a registry. Code that needs a library belongs in a tool call, where your agent's own image already has it.
- Filesystem is ephemeral. Files written during a run are gone with the run. Use stdout, a returned blob, or write to an external store.
- Resource bounds — CPU, memory, wall time, output size — are enforced on every run, and a run that hits a bound is terminated.
- Tool calls are a first-class pattern: an agent passes a tool call (a name and arguments), the sandbox resolves the tool, executes it, and returns the structured result.
- Each run is isolated at the syscall level. Code cannot reach project's private resources, another project's network, or the host's filesystem.
- Results are returned as JSON with
stdout,stderr,exit_code, andduration_ms.
API reference
The three routes are documented in full, with request and response fields, in Run code:
| Route | What it does |
|---|---|
POST /v1/execute | Run one Python snippet in a warm sandbox. Backs run_python. |
POST /v1/call | Run one tool call in a sandbox built from the agent's own image. Backs TOOL_SANDBOX. |
GET /v1/pool | Pool status. Internal only. |
All three are served by the platform's internal sandbox service at $SANDBOX_URL.
None of them is routable from the public API - https://api.codyhill.dev/v1/execute
returns 404, by design.
Limits and quotas
The two sandbox kinds have different bounds, because they do different work: a snippet is short and untrusted, a tool call runs your own image and is allowed out to the internet.
| Limit | Snippet (run_python, /v1/execute) | Tool call (TOOL_SANDBOX, /v1/call) |
|---|---|---|
| Language | Python 3.12 | Python 3.12, from your agent's image |
| Default timeout | 20 s | 30 s |
| Maximum timeout | 60 s | 120 s |
| Memory | 512 MiB | 1 GiB |
| CPU | 1 vCPU | 2 vCPUs |
| Response size | — | 8 MiB |
| Network | none - not even DNS | public internet; private ranges blocked |
| Environment reuse | never | never |
A run that passes timeout_s outside 1-60 is refused with 400 and
timeout_s must be between 1 and 60, rather than being silently clamped.
Troubleshooting
| Symptom | What it means |
|---|---|
stderr reads timed out after <N>s, exit_code: -1 | The run passed its deadline and every process it started was killed. Through run_python the deadline is fixed at 20 s; calling /v1/execute yourself, raise timeout_s up to 60. |
401 from /v1/execute | The agent's internal token is missing or was minted for another project. Redeploy the agent - that is when the control plane mints a current one. |
400 unsupported language "<x>" | Only python is supported. |
502 file path escapes working directory | A files entry tried to write outside the working directory. |
A network call inside run_python fails with a name-resolution error | Snippet sandboxes have no network. If your code needs to reach an API, it belongs in a tool call, not a snippet. |
| The agent fails to start with a tool-isolation error | A tool could not be relocated into a sandbox under TOOL_SANDBOX=true. That is deliberate: it refuses rather than running your tool unisolated. Fix the tool, or set TOOL_SANDBOX=false to accept the risk knowingly. |
Security notes
- Isolation is the product. Every run is a single-use environment, destroyed afterwards, that cannot reach your project's networking, the host filesystem, or another tenant.
- Snippets have no network. A
run_pythonsandbox reaches nothing - not even DNS, which is a channel out if it answers. Tool-call sandboxes may reach the public internet, but cloud metadata (169.254.0.0/16) and private ranges (10/8,172.16/12,192.168/16) are blocked. - Files come back; the container does not stay. Anything the code writes
in its working directory is returned with the result and kept beside the
conversation; the container itself is destroyed. With
CODE_INTERPRETER_SESSION=truethe container stays for the conversation, so variables and files carry over between calls until it goes idle. - Authenticated per project.
/v1/executeand/v1/callare gated on your project's own internal token; a run inherits no broader authority. - A tool call runs YOUR image.
/v1/callpins the sandbox image to the calling agent's own, so one project cannot ask the sandbox to run another's code.
Where next
- Run code — full parameter reference and worked examples.
- Security and limits — the full isolation and resource contract.
- Overview — concepts in more depth.