Skip to main content

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_python tool; 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.

note

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. language defaults to python, and anything else is refused with 400 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, and duration_ms.

API reference

The three routes are documented in full, with request and response fields, in Run code:

RouteWhat it does
POST /v1/executeRun one Python snippet in a warm sandbox. Backs run_python.
POST /v1/callRun one tool call in a sandbox built from the agent's own image. Backs TOOL_SANDBOX.
GET /v1/poolPool 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.

LimitSnippet (run_python, /v1/execute)Tool call (TOOL_SANDBOX, /v1/call)
LanguagePython 3.12Python 3.12, from your agent's image
Default timeout20 s30 s
Maximum timeout60 s120 s
Memory512 MiB1 GiB
CPU1 vCPU2 vCPUs
Response size8 MiB
Networknone - not even DNSpublic internet; private ranges blocked
Environment reusenevernever

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

SymptomWhat it means
stderr reads timed out after <N>s, exit_code: -1The 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/executeThe 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 directoryA files entry tried to write outside the working directory.
A network call inside run_python fails with a name-resolution errorSnippet 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 errorA 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_python sandbox 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=true the container stays for the conversation, so variables and files carry over between calls until it goes idle.
  • Authenticated per project. /v1/execute and /v1/call are gated on your project's own internal token; a run inherits no broader authority.
  • A tool call runs YOUR image. /v1/call pins the sandbox image to the calling agent's own, so one project cannot ask the sandbox to run another's code.

Where next