The sandbox is created with two settings that do the work:

  • api.scopes: ["infer:run"] lets code inside it call the node's model at http://127.0.0.1:7700/v1/infer, and spend_cap_usd: 1 stops those calls once they have cost a dollar, whatever the model-written code does. The create sets OPENAI_BASE_URL, a placeholder OPENAI_API_KEY and B3OS_MODEL in its environment, so a stock OpenAI client (or plain HTTP, as here) works with no key inside the sandbox.
  • egress: {mode: "deny"} leaves it no other way out. The recipe checks that: curl https://example.com from the sandbox fails (internet: blocked), while the model calls succeed.
Check

Verified 2026-11-06: this recipe's last run passed against https://beta-api.b3os.org, label live-recorded (recorded from production), from commit 9fd4273e. The recipe harness runs it again, and a failed run takes this page out of the cookbook.

Run it

Each tab uploads the three files from this recipe's folder, checks that the sandbox cannot reach the internet, runs agent.py and prints the solution it kept.

bash
API=https://beta-api.b3os.orgAUTH="Authorization: Bearer $B3OS_API_KEY"post() { curl -s "$API$1" -H "$AUTH" -H "Content-Type: application/json" -d @-; }SBX=$(echo '{"template": "base", "timeout_s": 300, "egress": {"mode": "deny"}, "api": {"scopes": ["infer:run"], "spend_cap_usd": 1}}' | post /v1/sandboxes | jq -r .id)for f in agent.py task.md test_solution.py; do curl -s -X PUT "$API/v1/sandboxes/$SBX/files?path=/workspace/$f" -H "$AUTH" \ -H "Content-Type: application/octet-stream" --data-binary @"$f" > /dev/nulldoneecho '{"cmd": "curl -s -m 5 -o /dev/null https://example.com && echo reached || echo blocked"}' \ | post "/v1/sandboxes/$SBX/exec" | jq -r .stdoutecho '{"cmd": ["python3", "/workspace/agent.py"], "timeout_ms": 58000}' \ | post "/v1/sandboxes/$SBX/exec" | jq -r '.stdout | split("\n") | map(select(. != "")) | last'curl -s "$API/v1/sandboxes/$SBX/files?path=/workspace/solution.py" -H "$AUTH"curl -s -X DELETE "$API/v1/sandboxes/$SBX" -H "$AUTH" > /dev/null

Inside the sandbox, agent.py (standard library only) sends task.md to the model, writes the answer's code block to solution.py, runs python3 -m unittest test_solution and, when a test fails, sends the failure back and tries again, at most three times. The task is parse_duration("1h30m") == 5400 with seven tests, including the inputs it must refuse ("30m1h", "1.5h", "10").

The outputs are the run's own measurements: model_ms is the time spent in model calls and tests_ms the last test run, both measured inside the sandbox; create_ms is the create's server-side total from its timing; exec_ms is the agent's exec as the client saw it. The sandbox is deleted at the end, whatever happened.

Files: run.mjs (the client), agent.py, task.md and test_solution.py (uploaded to the sandbox).

Owner: Sandboxes. Products: Sandboxes, Infer.

Output of the last green run

json
{ "model": "b3os/qwen3.5-9b", "internet": "blocked", "attempts": 1, "all_passed": true, "tests_total": 7, "tests_passed": 7, "model_ms": 2722.6, "completion_tokens": 624, "tests_ms": 92.7, "create_ms": 1.821, "exec_ms": 2887.1, "solution_lines": 72, "solution_head": "def parse_duration(text: str) -> int:\n import re\n \n text = text.strip()\n if not text:\n raise ValueError(\"Empty string\")\n \n # Pattern to match integer followed by a unit\n # We need to ensure the whole string is consumed by these parts\n pattern = r'^(\\d+)([hms])'\n \n parts = []\n remaining = text\n \n while remaining:\n match = re.match(pattern, remain"}

The outputs this recipe publishes (the publish.outputs list in its recipe.json), with workspace identifiers redacted. The run's stdout, stderr and other outputs are not published.

Captured 2026-11-06 from commit 9fd4273e , label live-recorded (recorded from production). Case recipes/sandbox-model-writes-tested-code .

Ask a question... ⌘I