Sandbox Environments

A sandbox session runs an environment in its own container and connects a remote client to it, so an environment with its own dependencies never has to share your process.

A sandbox session runs an environment in its own container and connects a remote client to it, so an environment with its own dependencies never has to share your process. A sandbox env is a remote env: it inherits the same reset / step / render / close loop and the same contract, and it also owns the container, which it starts on construction and stops on close.

Sandbox helpers are experimental. Pin versions for any real run; see Compatibility. For runnable files, see Sandbox Examples.

from rlmesh.numpy import SandboxEnv, SandboxBuild

with SandboxEnv(
    "CartPole-v1",
    build=SandboxBuild(packages=["gymnasium==1.3.0"], imports=["gymnasium"]),
    render_mode="rgb_array",
) as env:
    obs, info = env.reset(seed=42)
    action = env.action_space.sample()
    obs, reward, terminated, truncated, info = env.step(action)

The first argument is the source. Everything else splits into three places: build= configures how an image is built from source, runtime= configures how a prebuilt container is run, and any remaining keyword is forwarded to the environment’s constructor. render_mode above is one such construction param, passed through to gym.make.

Sources

The source string both names the environment and selects how RLMesh gets it. A gym id or a gym:// / hf:// scheme is built into an image from source; a Docker reference is run as-is. RLMesh always logs which kind it resolved, so the choice is never silent.

A Gymnasium id is the simplest source:

SandboxEnv("CartPole-v1", build=SandboxBuild(packages=["gymnasium==1.3.0"]))

gym://CartPole-v1 makes the scheme explicit. The plain id and the gym:// form resolve to the same kind of source.

A hf:// source points at a Hugging Face EnvHub repository, which exposes an environment factory as described in the Hugging Face EnvHub docs. The LeRobot CartPole demo returns suite cartpole_suite, task 0, so the selector is explicit:

from rlmesh.numpy import SandboxEnv, SandboxBuild

with SandboxEnv(
    "hf://lerobot/cartpole-env:cartpole_suite/0",
    build=SandboxBuild(trust_remote_code=True, allow_unpinned_hf=True),
) as env:
    observation, info = env.reset(seed=0)
    action = env.action_space.sample()
    observation, reward, terminated, truncated, info = env.step(action)

A Docker reference (docker://img, image://img, or a bare img:tag) runs a prebuilt rlmesh-serving image directly, with no build step and no rlmesh pin. Construction params are injected into the running container as the environment’s make binding.

Use SandboxVectorEnv when the selected source serves more than one environment. It takes num_envs (which must be at least two) and otherwise mirrors SandboxEnv:

from rlmesh.numpy import SandboxVectorEnv

with SandboxVectorEnv("CartPole-v1", num_envs=2) as envs:
    observations, infos = envs.reset(seed=0)

Where each option goes

Three groups of configuration sit on a sandbox session, and they apply at different moments. Passing a build or runtime field directly as a keyword is an error that points you at the right group, so a setting can never silently fall through into the construction binding.

Pass it via Configures Applies to
build=SandboxBuild(...) how an image is built from source gym:// / hf:// / bare gym id
runtime=SandboxRuntime(...) docker run flags when the container starts prebuilt image sources
**params the env’s make/load construction binding every source
connect_timeout_seconds how long to wait for the container to serve every source

Build options

SandboxBuild describes how a from-source image is assembled. It is meaningless for a prebuilt image, which is already built; setting it there is ignored with a warning.

Field Purpose
packages Python packages installed in the sandbox image
imports import names checked before the sandbox is considered ready
base_image Docker base image override for environments with native dependencies
rlmesh_package the rlmesh package, wheel path, or "local" wheel installed in the image
trust_remote_code allow a remote source to execute repository Python code
allow_unpinned_hf allow unpinned Hugging Face sources; keep it off for reproducibility
build_memory memory ceiling for the image build

Runtime options

SandboxRuntime carries docker run flags and applies only to prebuilt image sources; a from-source build has no run step and rejects these. Simulation environments that render through a GPU need them.

Field Purpose
gpus docker run --gpus: "all", a count, or a selector like "device=0,1" (CUDA compute only)
devices docker run --device entries, such as ["nvidia.com/gpu=all"] for SAPIEN/Vulkan via a CDI ref
volumes docker run -v mounts for bind-mounting large assets
user docker run --user: an int uid or a "uid[:gid]" string

For a writable mount, set user= to the uid that owns the host directory (typically os.getuid()). The container runs with --cap-drop ALL, so even a root process inside it obeys the mount’s permission bits; without a matching uid, writes into a bind-mounted volume fail with permission errors.

Match the RLMesh version

The sandbox image and your Python process should use the same released RLMesh version. Set SandboxBuild(rlmesh_package="rlmesh==0.1.0"), replacing 0.1.0 with the version you installed locally. See Compatibility for details. The SandboxBuild API documents other package sources for development builds.

Startup timing

A sandbox client retries while the container boots. The server inside the container binds its port only after the environment factory’s make() runs, so an environment that loads large simulations or assets, such as a LIBERO task suite, needs headroom. connect_timeout_seconds (default 30) sets how long the client waits before giving up; if the container exits or never becomes ready, the failure includes its recent logs instead of a bare transport error.

Pinning and safety

The HF demo above is unpinned for convenience. For real evaluations, pin the repository to a full commit SHA:

hf://lerobot/cartpole-env@<full-commit-sha>:cartpole_suite/0