Skip to content
sandbox-workers
Esc
navigateopen⌘Jpreview
On this page

Overview

Run a code interpreter on your own Cloudflare account in stateless or stateful mode, in the shape of the Sandbox SDK 1.0 preview.

sandbox-workers deploys JavaScript, Python, Perl, and Ruby interpreters as separate Wasm Workers on your own Cloudflare account. You can call a runtime Worker directly for one-shot execution (stateless mode), or host a Sandbox Durable Object in your own Worker that mirrors the Sandbox SDK 1.0 preview’s model: getSandbox(env.Sandbox, id), sandbox.interpreter.*, and a shared /workspace (stateful mode). Every code context and every stateless call is bound to a runtime Worker by the name of a Service Binding — there is no language option — so one sandbox can run several languages at once, all sharing the same files.

There is no process execution, terminals, ports, or backups here: this project runs one thing, a code interpreter, on Wasm rather than containers. See Concepts for what that trades away and the design document for the full model.

Two modes

Stateless mode Stateful mode
What runs on your side Nothing extra — you call the runtime Worker directly A Sandbox Durable Object, hosted in your own Worker
State between calls None — a fresh Wasm instance every call Persists in a code context: top-level variables, functions, imports
Files None A shared /workspace, readable and writable by every code context
Languages JavaScript, Python, Perl, and Ruby JavaScript, Python, and Perl (Ruby is stateless-only)
Setup A services entry only services + durable_objects binding + migration + export { Sandbox }
Client call runCode(env.PYTHON, code) getSandbox(env.Sandbox, id).interpreter.runCode(code, { context })
  • Choose stateless when you need a one-shot, isolated execution and don’t need state or files to survive between calls.
  • Choose stateful when you need variables, functions, or imports to persist across calls, or you need a shared /workspace across one or more languages.

You can mix both against the same runtime Worker and the same binding names — a Worker deployed once serves either call shape. A stateless-only runtime Worker (Ruby, or one scaffolded with the CLI’s --stateless flag) only supports stateless mode: it works normally with the free runCode, but createCodeContext({ binding }) against it fails, and sandbox.interpreter.runCode(code, { binding }) (no context) falls back to running statelessly.

Examples

Stateless mode: one call, no sandbox

import { runCode } from "@sandbox-workers/core";

const result = await runCode(env.PYTHON, "1 + 1"); // env.PYTHON: a Service Binding to the runtime Worker

For a one-shot call with no code context and no files, call the free runCode function directly against a runtime Worker’s Service Binding — no Sandbox Durable Object involved.

Stateful mode

Execute code in a context

import { getSandbox } from "@sandbox-workers/core";

export { Sandbox } from "@sandbox-workers/core";

export default {
  async fetch(request, env) {
    const sandbox = getSandbox(env.Sandbox, "user-42");
    const ctx = await sandbox.interpreter.createCodeContext({ binding: "PYTHON" });
    const result = await sandbox.interpreter.runCode(
      "print('Hello!')\nimport os\nint(os.environ['X']) ** 2",
      { context: ctx, envVars: { X: "12" } },
    );
    return Response.json(result);
  },
};

runCode always resolves to an ExecutionResult: no error, results: [{ text: "144" }], and the captured greeting in logs.stdout.

Multiple languages, one workspace

const sandbox = getSandbox(env.Sandbox, "user-42");

const py = await sandbox.interpreter.createCodeContext({ binding: "PYTHON" });
const js = await sandbox.interpreter.createCodeContext({ binding: "JAVASCRIPT" });

await sandbox.interpreter.runCode("open('/workspace/a.txt','w').write('hi')", { context: py });
await sandbox.interpreter.runCode("fs.readFileSync('/workspace/a.txt','utf8')", { context: js });

A code context is a durable REPL bound to one runtime Worker: top-level variables, functions, classes, and imported modules from one execution are visible to the next execution in the same context. /workspace belongs to the sandbox, not to any one context, so a JavaScript context can read a file a Python context wrote.

Files

const sandbox = getSandbox(env.Sandbox, "user-42");

await sandbox.mkdir("/workspace/project", { recursive: true });
await sandbox.writeFile("/workspace/project/app.py", "print('hi')");
const file = await sandbox.readFile("/workspace/project/app.py");

Every sandbox owns a writable /workspace directory, reachable from guest code and from the caller through a files API, and shared by every code context in that sandbox regardless of language.

Choose an engine

Runtime Engine Env vars accessed as
JavaScript SpiderMonkey 147 / goccy spidermonkey-wasm v0.2.6 process.env.NAME
Python CPython 3.14.6 os.environ["NAME"]
Perl Perl 5.42.2 $ENV{NAME}
Ruby CRuby 4.0.0 ENV["NAME"]

Every run creates a fresh Wasm instance unless it runs in a code context. Fuel, memory, and output bounds limit guest execution. Standard libraries depend on the selected engine; host networking, host files, and package installation are unavailable. Ruby is stateless-only — its runtime Worker always answers contexts: false, so it works in stateless mode only.

Start here

Explore

  • Stateless mode: one-shot execution with no sandbox.
  • Stateful mode: code contexts and a shared /workspace.
  • API reference: request fields, responses, and error handling.
  • Concepts: architecture, sandbox lifecycle, code contexts, runtime engines, and security model.
  • Configuration: the caller’s and runtime Worker’s wrangler.jsonc, bindings, and environment variables.
  • Runtimes: per-language details for JavaScript, Python, Perl, and Ruby.
  • Platform: limits, licenses, and troubleshooting.

The npm packages are currently previews and have not been published. Source-based deployment templates work independently of npm publication once this repository and the template directories are public. Read runtime licenses and limits before use.