Skip to main content
Any time an agent runs a shell command or executes Python, the interesting question is where. Your laptop is fine for prototyping and wrong for almost everything else: a stray rm -rf, a runaway pip install, or a model that decides to curl a dubious URL reaches straight into your machine. A sandbox is the boundary that prevents that. Sandbox is an abstract execution environment. You create one, run commands in it, move files in and out, and close it when you are done. Because the interface is a clean abstract class, a sandbox can be backed by anything: a local Docker container, a remote machine, a microVM, a serverless runtime, your own in-house isolation technology. Motus ships two reference backends so you do not have to start from scratch:
  • DockerSandbox runs work inside a local Docker container. Great for local development and self-hosted deployments.
  • CloudSandbox talks to a remote sandbox over a REST API. Used automatically when your agent runs on Motus Cloud; see Cloud Sandbox for the cloud-specific behavior.
The same get_sandbox(...) call works in both places. You get a DockerSandbox on your laptop and a CloudSandbox when the deploy target is Motus Cloud. If neither fits, implement the Sandbox interface yourself and callers of get_sandbox() keep working unchanged.

Quick start

That’s the whole loop. get_sandbox() picks the right backend, the with block tears it down on exit, and each exec/sh call returns the command’s output as a string.

Local and cloud, one call

Your code makes the same get_sandbox(...) call in both environments. Locally it returns a DockerSandbox. When the same agent runs on Motus Cloud, it returns a CloudSandbox that talks to a sandbox the cloud side manages for you. The switch happens automatically. Motus Cloud currently runs your agent inside a fixed, Motus-provided sandbox image. See Cloud Sandbox for the cloud-specific lifecycle, limits, and console UI.

Creating a sandbox

get_sandbox() is the recommended entry point. It manages a global provider behind the scenes, so repeated calls in the same process do not spin up redundant infrastructure.
For fine-grained control, the DockerSandbox class is a direct factory with the same options plus an async variant:

Parameter reference

What you can do in a sandbox

Every sandbox exposes the same small surface, built on exec().
A non-zero exit code does not raise; the command output is returned as a string so the caller can inspect it. This matches the way a terminal behaves and lets the agent handle a failure the same way it handles any other tool output.

Handing a sandbox to an agent

A Sandbox is both a Python object you can drive yourself and a tool collection an agent can call. Two common patterns:

Pass the sandbox directly

Motus extracts the sandbox’s python and sh methods and exposes them as tools (Sandbox is declared with @tools(allowlist={"python", "sh"})).

Use the full builtin_tools suite

builtin_tools(sandbox=sb) wraps the sandbox in a richer developer toolkit: bash, read_file, write_file, edit_file, glob_search, grep_search, a todo list, and (when skills_dir= is passed) load_skill. See Tools for how these fit together.
Call builtin_tools() with no argument and it binds to a LocalShell, which runs commands directly on the host. Convenient for prototyping; not suitable once the model is running code you did not write yourself.

Lifecycle

Context managers are the recommended path. Motus supports both sync and async:
For long-lived sandboxes driven from tests or a custom orchestrator, you can also close manually:

Ownership

Sandboxes created with create() / acreate() own the underlying container: closing them attempts to stop and remove it. Sandboxes obtained with connect(name) do not own the container and will leave it running when closed. This is what you want when your sandbox is a shared dev environment rather than a disposable workspace.

Backends

The default on your laptop and on self-hosted deploys. Requires a running Docker daemon. Supports images, Dockerfiles, bind mounts, port mapping, and attach-to-existing.The first time Motus brings up the DockerToolProvider in a process, it checks for a ghcr.io/lithos-ai/sandbox image (a Python base with common utilities) and builds it locally from the bundled Dockerfile if missing. This check only runs once per provider. Your get_sandbox(image=...) call is separate: it spins up whatever image you name.

Where to go next

Tools

How the @tool decorator, builtin_tools, and sandbox fit together.

Cloud Sandbox

Per-session containers on Motus Cloud: lifecycle, limits, and console UI.

MCP integration

Running MCP servers inside a sandbox when you want their side effects contained.

Human in the Loop

Require approval before the agent runs commands that write to disk or reach the network.