Sandboxes

Where the agent's commands and files run, and the network policy of a sandbox.

Guides: Sandbox, Mistral sandbox, Local sandboxes and Network.

NameWhat it is
SandboxThe options every sandbox takes: network, secrets, hooks and the tools the model gets.
NoSandboxRun without a sandbox.
BashkitSandboxA sandbox that runs the model's shell commands in the agent's process, through the Bashkit bash interpreter, with no container and no Python.
DangerousLocalSandboxRuns commands directly in the process that runs the agent, with no isolation.
LocalDockerSandboxA sandbox in a Docker container on your machine, for development.
MistralSandboxA sandbox Mistral runs for each session, on Mistral Cloud.
SandboxExecutionResultOutput of a command run in the current session sandbox.
SandboxHookCode that runs when the sandbox is created and when the session closes.
pattern.sandbox_hook.GitIdentitySet the sandbox's global git user.name and user.email when it is created.
EgressPolicyThe IP addresses a MistralSandbox can open connections to, on top of its network.
NetworkOutbound network of a sandbox.
HeaderBindingAdd a secret to the HTTPS requests that a sandbox sends to one host.
GitHubBindingGive a sandbox Git and GitHub API access as the session user.
MistralApiBindingGive a sandbox Mistral API access as the session user.
WorkspaceHeaderBindingsRoute a Mistral sandbox through Sandbox Proxy, and apply the rules a workspace admin stored there.
Sandbox

Sandbox

class Sandbox(
    *,
    network: Network | None = None,
    header_bindings: Sequence[HeaderBinding | GitHubBinding | MistralApiBinding | WorkspaceHeaderBindings] = (),
    hooks: Sequence[SandboxHook] = (),
    access: Literal['none', 'files', 'full'] | None = None,
)

The options every sandbox takes: network, secrets, hooks and the tools the model gets.

access sets the sandbox tools the model gets: "files" gives the file tools, "full" adds bash, and "none" gives neither while tools, hooks and skills still use the sandbox. None gives every level the sandbox supports. A sandbox type declares in supported_access the widest level it serves, and its constructor refuses a wider one.

network sets the outbound connections the sandbox can open, and defaults to Network.none(). header_bindings lists the secrets the sandbox gets on those connections. A sandbox type declares in supported_network and supported_header_bindings what it can enforce, and its constructor refuses the rest.

ParameterTypeDefaultDescription
networkNetwork | NoneNoneThe outbound connections the sandbox can open. Defaults to Network.none().
header_bindingsSequence[HeaderBinding | GitHubBinding | MistralApiBinding | WorkspaceHeaderBindings]()The secrets the sandbox gets on those connections.
hooksSequence[SandboxHook]()Sandbox hooks, which run when the sandbox is created and when the session closes.
accessLiteral['none', 'files', 'full'] | NoneNoneThe sandbox tools the model gets: "full", "files" or "none". None gives every level the sandbox supports.
AttributeTypeDescription
supported_networktuple[Literal['none', 'all', 'allow'], ...]The network modes that the sandbox type can enforce, such as ("none", "all").
supported_header_bindingstuple[type[BaseModel], ...]The header_bindings types that the sandbox type serves.
supported_accessLiteral['none', 'files', 'full']The widest access level that the sandbox type serves.
delete_on_cleanupboolWhether the sandbox is deleted when the session closes.
supports_background_processesboolWhether the model can keep commands running in the background, through the built-in process tools.
NoSandbox

NoSandbox

class NoSandbox(*, hooks: Sequence[SandboxHook] = ())

Run without a sandbox.

Based on Sandbox.

ParameterTypeDefaultDescription
hooksSequence[SandboxHook]()Sandbox hooks, which run when the sandbox is created and when the session closes.
AttributeTypeDescription
supported_networktuple[Literal['none', 'all', 'allow'], ...]The network modes that the sandbox type can enforce: "none".
delete_on_cleanupboolWhether the sandbox is deleted when the session closes.
BashkitSandbox

BashkitSandbox

class BashkitSandbox(
    *,
    instance_id: str | None = None,
    network: Network | None = None,
    block_private_ips: bool = True,
    hooks: Sequence[SandboxHook] = (),
    access: Literal['none', 'files', 'full'] | None = None,
)

A sandbox that runs the model's shell commands in the agent's process, through the Bashkit bash interpreter, with no container and no Python. Its network is none by default.

A command without timeout_seconds gets 300 seconds.

Private and loopback addresses stay refused even when network allows them. Set block_private_ips=False to reach a service on your machine, for example in local tests.

instance_id names the sandbox's folders on your machine, under the system's temporary folder, together with each session's ID. A session reopened with the same instance_id, by this process or a new one, finds its folders again. Without instance_id, the folders are named after the session alone.

Based on Sandbox.

ParameterTypeDefaultDescription
instance_idstr | NoneNoneA name for the sandbox's folders, together with each session's ID.
networkNetwork | NoneNoneNetwork.none(), the default, Network.all() or Network.allow(hosts=...).
block_private_ipsboolTrueWhether private and loopback addresses stay refused whatever network allows.
hooksSequence[SandboxHook]()Sandbox hooks, which run when the sandbox is created and when the session closes.
accessLiteral['none', 'files', 'full'] | NoneNoneThe sandbox tools the model gets: "full", "files" or "none". None gives every level the sandbox supports.
AttributeTypeDescription
supported_networktuple[Literal['none', 'all', 'allow'], ...]The network modes that the sandbox type can enforce: "none", "all" and "allow".
DangerousLocalSandbox

DangerousLocalSandbox

class DangerousLocalSandbox(
    *,
    shell: Literal['bash', 'sh'] | None = None,
    default_exec_timeout_seconds: int | None = None,
    workspace: str | None = None,
    delete_on_cleanup: bool | None = None,
    hooks: Sequence[SandboxHook] = (),
    access: Literal['none', 'files', 'full'] | None = None,
)

Runs commands directly in the process that runs the agent, with no isolation.

There is no container: the model's commands run on your machine as the user of that process. Use it only for local development and CI, never in production.

Commands reach every network your machine reaches, so the sandbox's network is always Network.all().

workspace is the host folder where commands run and the agent reads and writes files. Without it, the sandbox creates a new folder under ~/.cache/mistral-vibe-harness-local-host/. A relative workspace is resolved against the working folder of the process that runs the agent.

delete_on_cleanup deletes the workspace when the session closes. It defaults to True without workspace and to False with one, so the sandbox never deletes your folder unless you ask. A workspace to delete must be absolute and must not contain your home folder or the process's working folder; the session fails at start otherwise.

Based on Sandbox.

ParameterTypeDefaultDescription
shellLiteral['bash', 'sh'] | NoneNoneThe shell that runs the agent's commands: "bash", the default, or "sh".
default_exec_timeout_secondsint | NoneNoneThe time limit of a command that does not set its own. Defaults to 600.
workspacestr | NoneNoneThe host folder where commands run. Defaults to a new folder under ~/.cache/mistral-vibe-harness-local-host/.
delete_on_cleanupbool | NoneNoneWhether the workspace is deleted when the session closes. Defaults to True without workspace and to False with one.
hooksSequence[SandboxHook]()Sandbox hooks, which run when the sandbox is created and when the session closes.
accessLiteral['none', 'files', 'full'] | NoneNoneThe sandbox tools the model gets: "full", "files" or "none". None gives every level the sandbox supports.
AttributeTypeDescription
supported_networktuple[Literal['none', 'all', 'allow'], ...]The network modes that the sandbox type can enforce: "all".
supports_background_processesboolWhether the model can keep commands running in the background, through the built-in process tools.
LocalDockerSandbox

LocalDockerSandbox

class LocalDockerSandbox(
    *,
    image: str | None = None,
    supports_background_processes: bool | None = None,
    shell: Literal['bash', 'sh'] | None = None,
    default_exec_timeout_seconds: int | None = None,
    setup_script: str | None = None,
    setup_script_timeout_seconds: int | None = None,
    network: Network | None = None,
    hooks: Sequence[SandboxHook] = (),
    access: Literal['none', 'files', 'full'] | None = None,
)

A sandbox in a Docker container on your machine, for development.

network defaults to Network.none(): the container has no network but loopback, and neither has the setup script. Network.all() gives it Docker's default network.

default_exec_timeout_seconds (600 by default) stops a command sent without a timeout of its own, such as a sandbox hook's ctx.sandbox.execute(...): the command and its children get SIGTERM, then SIGKILL 5 seconds later, and it ends with exit code 124. The image needs a setsid command (util-linux or BusyBox).

Based on Sandbox.

ParameterTypeDefaultDescription
imagestr | NoneNoneThe container image to run, which must provide shell. Defaults to python:3.12-bookworm.
supports_background_processesbool | NoneNoneWhether the model can keep commands running in the background. Defaults to True when you leave out image and to False when you pass it.
shellLiteral['bash', 'sh'] | NoneNoneThe shell that runs the agent's commands: "bash", the default, or "sh".
default_exec_timeout_secondsint | NoneNoneThe time limit of a command that does not set its own. Defaults to 600.
setup_scriptstr | NoneNoneA shell command run once after the container starts.
setup_script_timeout_secondsint | NoneNoneThe time limit of setup_script. Defaults to 300.
networkNetwork | NoneNoneNetwork.none(), the default, or Network.all().
hooksSequence[SandboxHook]()Sandbox hooks, which run when the sandbox is created and when the session closes.
accessLiteral['none', 'files', 'full'] | NoneNoneThe sandbox tools the model gets: "full", "files" or "none". None gives every level the sandbox supports.
AttributeTypeDescription
supported_networktuple[Literal['none', 'all', 'allow'], ...]The network modes that the sandbox type can enforce: "none" and "all".
MistralSandbox

MistralSandbox

class MistralSandbox(
    *,
    image: str | None = None,
    supports_background_processes: bool | None = None,
    instance_type: str | None = None,
    region: str | None = None,
    name_prefix: str | None = None,
    ttl_seconds: int | None = None,
    idle_timeout_seconds: int | None = None,
    default_exec_timeout_seconds: int | None = None,
    privileged: bool | None = None,
    docker_daemon_enabled: bool | None = None,
    docker_daemon_network_mode: str | None = None,
    enable_light_sleep: bool | None = None,
    setup_script: str | None = None,
    setup_script_timeout_seconds: int | None = None,
    readiness_max_retries: int | None = None,
    readiness_interval_seconds: float | None = None,
    readiness_probe_timeout_seconds: int | None = None,
    wait_ready_timeout_seconds: int | None = None,
    enable_mesh: bool | None = None,
    network: Network | None = None,
    egress: EgressPolicy | None = None,
    header_bindings: Sequence[HeaderBinding | GitHubBinding | MistralApiBinding | WorkspaceHeaderBindings] = (),
    pre_provisioned_sandbox_id: str | None = None,
    hooks: Sequence[SandboxHook] = (),
    access: Literal['none', 'files', 'full'] | None = None,
)

A sandbox Mistral runs for each session, on Mistral Cloud.

The API key's user needs the cloud_user role (Mistral Cloud User). Mistral Cloud filters outbound traffic by IP address, so network takes Network.none() or Network.all(). egress=EgressPolicy.custom(allow_cidrs=...) opens the listed addresses on top of Network.none(). Sandbox Proxy serves header_bindings.

An option you leave out, except supports_background_processes, network, egress, header_bindings, hooks and access, reads its default from the environment variable AGENTS_SDK_KOYEB_SANDBOX_<OPTION>, such as AGENTS_SDK_KOYEB_SANDBOX_INSTANCE_TYPE.

Based on Sandbox.

ParameterTypeDefaultDescription
imagestr | NoneNoneThe container image to run. Defaults to Mistral's public sandbox image.
supports_background_processesbool | NoneNoneWhether the model can keep commands running in the background. Defaults to True when you leave out image and to False when you pass it.
instance_typestr | NoneNoneThe instance size, such as "small", "medium", "large" or "2xlarge". Defaults to "medium".
regionstr | NoneNoneThe region the sandbox runs in. Defaults to "nl-north-1", the only region that can block traffic.
name_prefixstr | NoneNoneThe start of the sandbox's name. Defaults to "unknown".
ttl_secondsint | NoneNoneHow long after its creation Mistral Cloud deletes the sandbox, whatever happens. Defaults to 24 hours, the default session lifetime.
idle_timeout_secondsint | NoneNoneThe seconds idle before the sandbox sleeps. Defaults to 0, which never sleeps.
default_exec_timeout_secondsint | NoneNoneThe time limit of a command that does not set its own. Defaults to 30.
privilegedbool | NoneNoneWhether the container runs privileged. Defaults to False.
docker_daemon_enabledbool | NoneNoneWhether to start a Docker daemon in the sandbox, which also makes it privileged. Defaults to False.
docker_daemon_network_modestr | NoneNoneThe network mode passed to that Docker daemon. Defaults to "auto".
enable_light_sleepbool | NoneNoneWhether the sandbox uses light sleep, which wakes faster than deep sleep. Defaults to True.
setup_scriptstr | NoneNoneA shell command run once after the sandbox starts.
setup_script_timeout_secondsint | NoneNoneThe time limit of setup_script. Defaults to 300.
readiness_max_retriesint | NoneNoneHow many times the SDK probes the started sandbox before it gives up. Defaults to 10.
readiness_interval_secondsfloat | NoneNoneThe seconds between two readiness probes. Defaults to 2.0.
readiness_probe_timeout_secondsint | NoneNoneThe time limit of one readiness probe. Defaults to 5.
wait_ready_timeout_secondsint | NoneNoneHow long the SDK waits for the sandbox to be ready. Defaults to 300.
enable_meshbool | NoneNoneWhether the Mistral Cloud service mesh is on for the sandbox. Defaults to False.
networkNetwork | NoneNoneNetwork.none(), the default, or Network.all().
egressEgressPolicy | NoneNoneEgressPolicy.custom(allow_cidrs=...), the IP addresses to open on top of Network.none().
header_bindingsSequence[HeaderBinding | GitHubBinding | MistralApiBinding | WorkspaceHeaderBindings]()GitHubBinding, MistralApiBinding and WorkspaceHeaderBindings, which Sandbox Proxy serves.
pre_provisioned_sandbox_idstr | NoneNoneThe ID of a running Mistral Cloud sandbox to use instead of creating one.
hooksSequence[SandboxHook]()Sandbox hooks, which run when the sandbox is created and when the session closes.
accessLiteral['none', 'files', 'full'] | NoneNoneThe sandbox tools the model gets: "full", "files" or "none". None gives every level the sandbox supports.
AttributeTypeDescription
supported_networktuple[Literal['none', 'all', 'allow'], ...]The network modes that the sandbox type can enforce: "none" and "all".
supported_header_bindingstuple[type[BaseModel], ...]The header_bindings types that the sandbox type serves.
authored_settingsMapping[str, Any]The options you passed, before this process's environment fills in the rest.
egress_policyEgressPolicyThe IP-level policy that enforces network and egress on Mistral Cloud.
SandboxExecutionResult

SandboxExecutionResult

class SandboxExecutionResult(exit_code: int, stdout: str, stderr: str)

Output of a command run in the current session sandbox.

ParameterTypeDefaultDescription
exit_codeintrequiredThe command's exit code: 0 on success.
stdoutstrrequiredWhat the command wrote to standard output.
stderrstrrequiredWhat the command wrote to standard error.
Sandbox hooks

Sandbox hooks

SandboxHook

SandboxHook

class SandboxHook

Code that runs when the sandbox is created and when the session closes.

on_create and on_destroy may declare session inputs with Annotated[T, agents.Depends()] parameters, like tools and hooks.

On a deployed agent, on_create and on_destroy run in the agent loop: use ctx.sandbox for commands and files, and agents.now() and agents.sleep() for time.

The arguments a subclass is constructed with are recorded, so a deployed agent rebuilds the sandbox hook with the same arguments. They must be JSON values. A subclass that writes its own to_unified_state and from_unified_state replaces this contract.

SandboxHook.to_unified_state

SandboxHook.to_unified_state

def to_unified_state() -> dict[str, JsonValue]

Return the arguments the sandbox hook was constructed with, which a deployed agent rebuilds it from.

SandboxHook.from_unified_state

SandboxHook.from_unified_state

@classmethod
def from_unified_state(state: dict[str, JsonValue]) -> Self

Rebuild the sandbox hook by calling its constructor with the recorded arguments.

ParameterTypeDefaultDescription
statedict[str, JsonValue]requiredThe state that to_unified_state() returned.
SandboxHook.on_create

SandboxHook.on_create

async def on_create(ctx: SandboxCallbackContext) -> None

Runs once when the sandbox is ready, before the agent starts. The default does nothing.

ParameterTypeDefaultDescription
ctxSandboxCallbackContextrequiredAn agents.SandboxCallbackContext on the sandbox that was created.
SandboxHook.on_destroy

SandboxHook.on_destroy

async def on_destroy(ctx: SandboxCallbackContext) -> None

Runs once when the session closes. The default does nothing.

ParameterTypeDefaultDescription
ctxSandboxCallbackContextrequiredAn agents.SandboxCallbackContext on the session's sandbox, as the session closes.
pattern.sandbox_hook.GitIdentity

pattern.sandbox_hook.GitIdentity

class GitIdentity(*, name: str, email: str)

Set the sandbox's global git user.name and user.email when it is created:

agents.pattern.sandbox_hook.GitIdentity(name="Acme Bot", email="bot@acme.com")

DangerousLocalSandbox refuses it: on your machine, it would overwrite your ~/.gitconfig.

Based on SandboxHook.

ParameterTypeDefaultDescription
namestrrequiredThe git user.name.
emailstrrequiredThe git user.email.
Network

Network

EgressPolicy

EgressPolicy

class EgressPolicy(
    *,
    mode: Literal['block_all', 'allow_all', 'custom'] = 'block_all',
    allow_cidrs: tuple[str, ...] = (),
)

The IP addresses a MistralSandbox can open connections to, on top of its network.

Pass EgressPolicy.custom(allow_cidrs=...) as MistralSandbox(egress=...), which takes only custom(). block_all() and allow_all() describe MistralSandbox.egress_policy for Network.none() and Network.all().

ParameterTypeDefaultDescription
modeLiteral['block_all', 'allow_all', 'custom']'block_all'"custom" opens allow_cidrs; "block_all" and "allow_all" stand for Network.none() and Network.all().
allow_cidrstuple[str, ...]()The IP addresses or CIDR ranges to open, such as "203.0.113.0/24".
EgressPolicy.block_all

EgressPolicy.block_all

@classmethod
def block_all() -> EgressPolicy

Block every outbound connection: the egress_policy of a MistralSandbox with Network.none().

EgressPolicy.allow_all

EgressPolicy.allow_all

@classmethod
def allow_all() -> EgressPolicy

Allow every outbound connection: the egress_policy of a MistralSandbox with Network.all().

EgressPolicy.custom

EgressPolicy.custom

@classmethod
def custom(*, allow_cidrs: Sequence[str]) -> EgressPolicy

Open the listed addresses on top of Network.none().

ParameterTypeDefaultDescription
allow_cidrsSequence[str]requiredThe IP addresses or CIDR ranges to open, such as "203.0.113.0/24".
Network

Network

class Network(*, mode: Literal['none', 'all', 'allow'] = 'none', hosts: tuple[str, ...] = ())

Outbound network of a sandbox. Build it with none(), all() or allow(hosts=...).

ParameterTypeDefaultDescription
modeLiteral['none', 'all', 'allow']'none'"none" for no outbound connection, "all" for every one, "allow" for the listed hosts only.
hoststuple[str, ...]()The host or host:port entries an "allow" network reaches over HTTPS; empty otherwise.
Network.none

Network.none

@classmethod
def none() -> Self

No outbound connection.

Network.all

Network.all

@classmethod
def all() -> Self

Every outbound connection.

Network.allow

Network.allow

@classmethod
def allow(*, hosts: Sequence[str]) -> Self

Only HTTPS connections to the listed host or host:port entries. Wildcards are not accepted.

ParameterTypeDefaultDescription
hostsSequence[str]requiredThe host or host:port entries, at least one.
Network.reaches

Network.reaches

def reaches(host: str) -> bool

Whether this network opens connections to host, a host[:port] entry.

ParameterTypeDefaultDescription
hoststrrequiredThe host or host:port entry to check.
HeaderBinding

HeaderBinding

class HeaderBinding(
    *,
    host: str,
    header: str,
    secret_name: str,
    path_prefix: str = '/',
    prefix: str = '',
    suffix: str = '',
)

Add a secret to the HTTPS requests that a sandbox sends to one host.

secret_name picks a secret that Agent(secrets=...) declares. An experimental.CustomSandbox that lists HeaderBinding in supported_header_bindings takes it, and adds prefix + value + suffix as the header of each matching request. For a MistralSandbox, a workspace admin stores such rules in Sandbox Proxy: pass WorkspaceHeaderBindings.

ParameterTypeDefaultDescription
hoststrrequiredThe host or host:port whose requests get the header.
headerstrrequiredThe name of the header, such as "Authorization".
secret_namestrrequiredThe name of a secret that Agent(secrets=...) declares.
path_prefixstr'/'Only requests whose path starts with it get the header.
prefixstr''The text before the secret's value, such as "Bearer ".
suffixstr''The text after the secret's value.
AttributeTypeDescription
kindLiteral['header_binding']Always "header_binding".
GitHubBinding

GitHubBinding

class GitHubBinding(
    *,
    repos: tuple[str, ...] | Literal['all'],
    ttl_seconds: int = 3600,
    host: str = 'github.com',
)

Give a sandbox Git and GitHub API access as the session user.

Sandbox Proxy gets a short-lived token for the user's GitHub access, and the sandbox's git and gh use it without seeing it. It does more than a header binding: it also sets up git and gh, and checks each request against repos. It takes no secret_name because the token never comes from Agent(secrets=...). repos lists the owner/repository names the sandbox can reach, or is "all" for every repository the user can reach. The token is renewed before ttl_seconds elapse.

ParameterTypeDefaultDescription
repostuple[str, ...] | Literal['all']requiredThe owner/repository names the sandbox can reach, or "all" for every repository the user can reach.
ttl_secondsint3600How long one token lives; it is renewed before it expires.
hoststr'github.com'The GitHub host: "github.com".
AttributeTypeDescription
kindLiteral['github']Always "github".
MistralApiBinding

MistralApiBinding

class MistralApiBinding(*, ttl_seconds: int = 3600)

Give a sandbox Mistral API access as the session user.

Sandbox Proxy gives the sandbox a MISTRAL_API_KEY that only the proxy accepts, and the proxy URL as MISTRAL_API_HTTP_PROXY. Clients that send their Mistral API requests through that proxy, such as the agents CLI, act as the session user without seeing a real credential. The credential is renewed before ttl_seconds elapse.

run_as names that identity. "auto", the only value, is the session's user, or, in a local run, the user of MISTRAL_API_KEY.

ParameterTypeDefaultDescription
ttl_secondsint3600How long one credential lives; it is renewed before it expires.
AttributeTypeDescription
kindLiteral['mistral_api']Always "mistral_api".
run_asLiteral['auto']"auto", the only value: the session's user, or, in a local run, the user of MISTRAL_API_KEY.
WorkspaceHeaderBindings

WorkspaceHeaderBindings

class WorkspaceHeaderBindings()

Route a Mistral sandbox through Sandbox Proxy, and apply the rules a workspace admin stored there.

Each rule adds a workspace secret to the HTTPS requests that match it. The rules and their secrets are not in the agent code, and the sandbox never sees the secrets.

AttributeTypeDescription
kindLiteral['workspace_header_bindings']Always "workspace_header_bindings".