Skip to main content

Run code in the sandbox

This page shows the two ways code reaches the sandbox: the run_python tool your agents already have (the normal path), and the raw sandbox API (for operators with cluster access). It also documents every request field, response field, and error.

Before you begin

  • To use run_python, you need a deployed agent. See Invoke an agent.
  • To call the API directly, you need kubectl access to the cluster. The sandbox service (sandboxd) has no public endpoint and no authentication — it is reachable only inside the cluster, on purpose. If you only use the web console and CLI, you never need direct access; your agents reach it automatically.

Run code from an agent (the normal path)

Every agent gets the run_python tool. When the model decides code is needed, the tool sends the snippet to the sandbox and returns the output as part of the agent's reply.

  1. Invoke an agent with a request that needs computation:
platformctl invoke research-buddy "Please compute 2**32 in python."

The reply contains 4294967296, and the tool-call list shows run_python. That number came from a real Python process in a single-use pod, not from the model guessing.

  1. If you write custom agent code, you can hand the tool to your agent explicitly. It is a plain Python function:
from crusoe_adk.tools import run_python

The tool takes one argument, code (a string of Python source), and returns a formatted string with the sandbox's stdout, stderr, and exit code. When called through run_python, the timeout is fixed at 20 seconds.

Call the sandbox API directly

Operators only

Direct API access requires kubectl and is meant for platform operators and debugging. The service has no authentication — never expose it outside the cluster.

  1. Port-forward the service to your machine:
kubectl -n cai-system port-forward svc/sandboxd 8080:8080 &
export SANDBOX="http://localhost:8080"
  1. Run a snippet:
curl -s -X POST "$SANDBOX/v1/execute" \
-H "Content-Type: application/json" \
-d '{"code":"print(2**32)","timeout_s":10}'

You should see:

{"sandbox_id":"sandbox-x7k2p","stdout":"4294967296\n","stderr":"","exit_code":0,"duration_ms":38}
  1. Check the warm pool:
curl -s "$SANDBOX/v1/pool"

You should see:

{"warm":3,"target":3,"in_flight":0,"created_total":18}

created_total goes up by one for every execution — proof that each run got a fresh pod.

POST /v1/execute — run a Python snippet

Runs one snippet in a warm pod, then destroys the pod. Every response carries an X-Request-Id header. Errors are JSON: {"error": "<message>"}.

Request fields

FieldTypeRequiredDefaultNotes
codestringyesPython source to run.
languagestringno"python"Only Python is supported. Anything else returns 400 with unsupported language "<x>".
timeout_sintegerno20Wall-clock limit in seconds. Maximum 60. 0 or absent means "use the default". Out of range returns 400 with timeout_s must be between 1 and 60.
filesarraynoFiles to write into the working directory before the code runs. Each entry is {"path": "...", "content_b64": "..."} with base64-encoded content.

Response fields (200)

FieldTypeNotes
sandbox_idstringThe single-use pod that ran your code (e.g. sandbox-x7k2p). Different on every call.
stdoutstringEverything the code printed.
stderrstringErrors and warnings. On timeout: timed out after <N>s.
exit_codeinteger0 on success. -1 when the run was killed at the timeout.
duration_msintegerHow long the code ran, in milliseconds.

Send input files

Files are written into the working directory before your code starts. Paths must stay inside the working directory.

B64=$(printf 'name,qty\nwidget,2\n' | base64)
curl -s -X POST "$SANDBOX/v1/execute" \
-H "Content-Type: application/json" \
-d "{\"code\":\"print(open('data/input.csv').read())\",\"files\":[{\"path\":\"data/input.csv\",\"content_b64\":\"$B64\"}]}"

You should see:

{"sandbox_id":"sandbox-7fq3n","stdout":"name,qty\nwidget,2\n\n","stderr":"","exit_code":0,"duration_ms":44}

A path that tries to escape the working directory (for example ../etc/passwd) is rejected inside the pod with file path escapes working directory: '<path>', surfaced to you as a 502.

Timeouts kill, cleanly

curl -s -X POST "$SANDBOX/v1/execute" \
-H "Content-Type: application/json" \
-d '{"code":"while True: pass","timeout_s":5}'

You should see:

{"sandbox_id":"sandbox-9d4qf","stdout":"","stderr":"timed out after 5s","exit_code":-1,"duration_ms":5003}

The whole process group is killed at the deadline. An infinite loop cannot hold a pod hostage.

Error responses

StatusError messageMeaning
400invalid JSON bodyThe request body did not parse.
400unsupported language "<x>"Only "python" is accepted.
400timeout_s must be between 1 and 60Timeout out of range.
405method not allowedOnly POST works on /v1/execute.
502sandbox execution failed: <err>The pod could not run the code.
502sandbox pod returned HTTP <code>: <detail>The in-pod agent rejected the request (includes file-path escapes).
502invalid response from sandbox podThe pod answered with something unparseable.
503no warm sandbox pod available, try again shortlyThe pool was empty for 30 seconds (the maximum wait). Retry.

GET /v1/pool — pool status

Returns 200:

{"warm": 3, "target": 3, "in_flight": 0, "created_total": 17}
FieldMeaning
warmPods ready right now.
targetThe pool size the platform maintains (default 3).
in_flightExecutions running at this moment.
created_totalPods created since the service started — a lifetime counter.

POST /v1/call — sandboxed tool calls

This is the second mode: running one of your own tool functions in a one-use pod built from the agent's own image, with none of the agent's credentials. Agents use this automatically when tool sandboxing is on; you rarely call it yourself. Request bodies are capped at 1 MiB.

{
"image": "localhost:30500/agents/my-agent:abc123",
"module": "agent",
"function": "get_weather",
"args": {"city": "Reykjavik"},
"timeout_s": 30
}
FieldTypeRequiredDefaultNotes
imagestringyesThe agent's container image. Missing returns 400 with image and function are required.
functionstringyesThe tool function to call.
modulestringno"agent"The Python module that defines the function.
argsobjectnoKeyword arguments for the function.
timeout_sintegerno30Maximum 120. Out of range returns 400 with timeout_s must be between 1 and 120.

Success (200):

{"sandbox_id": "tool-xyz99", "result": {"temp_c": 4}, "duration_ms": 8123}

Both result and error are omitted when empty, so a successful call carries no error key at all. When the tool raises, error appears and carries the exception text.

Tool-call errors:

StatusError messageMeaning
503could not start a sandbox for this tool call: <err>The pod could not be created.
503the tool sandbox did not start: the tool sandbox exited before it was ready (phase Failed); the agent image must contain crusoe_adk.toolrunnerThe image lacks the tool runner.
503timed out waiting for the tool sandbox to become readyThe pod did not become ready within 110 seconds.
502tool execution failed: <err>The call itself failed.
502invalid response from the tool sandboxThe runner's reply was unusable (replies are capped at 8 MiB).

Unlike /v1/execute, tool calls have no warm pool: every call pays pod scheduling plus an image pull, so expect several seconds, not one.

Next steps