Skip to content

Getting started

Sandbox is the one class you construct. You give it a backend name (local or k8s), or inject a configured Backend, and call run(argv, ...). The same call is identical for every backend.

Run a program

from resoluto.sandbox import Sandbox

result = Sandbox(backend="local").run(
    ["python", "-c", "print('hello from the sandbox')"]
)
print(result.output)   # hello from the sandbox
print(result.ok)       # True → exit_code == 0
print(result.exit_code)

The RunResult captures the merged stdout/stderr (output), the exit_code, any files you asked to collect (artifacts), and a parsed result.json if the program wrote one (result). stdin is not supported — pass inputs via argv, env, or workspace files.

Passing inputs and collecting outputs

result = Sandbox(backend="local").run(
    ["python", "run_task.py", "--mode", "fast"],
    workspace="/path/to/project",         # staged into the sandbox as cwd
    env={"GREETING": "hello"},            # overlaid on the guest environment
    output_paths=["out/*.json"],          # globs collected into result.artifacts
)

workspace is staged into the conduit and becomes the program's working directory. output_paths globs are matched in the workspace after the program exits and returned in RunResult.artifacts.

The local backend image

backend="local" runs in a Kata microVM launched via nerdctl against a dedicated, standalone containerd (not your regular Docker daemon). It needs a sandbox image present in that containerd. When image= is omitted it uses default_local_image() — the base substrate tagged to the currently installed resoluto-sandbox version (never a floating :dev/:latest tag):

from resoluto.sandbox.client import default_local_image

Sandbox(backend="local", image=default_local_image())   # explicit; the default

Build it from Dockerfile.base (or resoluto-sandbox image build) and load it into the local containerd. Run argv with the guest's python and paths relative to workspace.

The k8s backend

backend="k8s" runs each program in a short-lived Kata microVM pod, rendezvousing through an S3/minio Conduit. For anything beyond the defaults, inject a configured SubstrateBackend:

import os
from resoluto.sandbox import Sandbox
from resoluto.sandbox.backends.substrate import SubstrateBackend, store_env_for_pod
from resoluto.sandbox.conduit.factory import store_from_env
from resoluto.sandbox.runtime.k8s import K8sSandboxRuntime

runtime = K8sSandboxRuntime(
    namespace="resoluto-sandboxes",
    context=os.environ.get("RESOLUTO_SANDBOX_KUBECONTEXT"),
)
sb = Sandbox(backend=SubstrateBackend(
    runtime=runtime,
    conduit=store_from_env(),
    image="<registry>/resoluto-sandbox-base:0.1.0",
    store_env=store_env_for_pod(os.environ),
))

Requirements: a Kubernetes cluster with Kata Containers, RESOLUTO_STORE_KIND (plus the matching store env vars), and a pinned RESOLUTO_SANDBOX_KUBECONTEXT (fails closed otherwise).

See Concepts for the run lifecycle, Backends for the substrate details, and Networking for per-run egress control.