This document describes how to install mimobox-python, its public API, exception hierarchy, and complete examples. The prose is in English, while code examples and variable names remain in English as originally written.
mimobox-python provides Python bindings through PyO3 + maturin, exposing the Rust SDK's mimobox_sdk::Sandbox as the Python module mimobox.
Core relationship:
Python user code
|
v
mimobox.Sandbox
|
v
mimobox_sdk::Sandbox
|
+-- OS sandbox
+-- Wasm sandbox
`-- microVM sandbox
The Python SDK supports:
- Basic command execution.
- Multi-language code execution with
execute_code(). - Command-level environment variable injection and timeouts.
- Streaming output.
- Argv-style command execution and streaming with
exec()andstream_exec(). - Active backend introspection with
active_isolation. - microVM file reads and writes.
- Directory listing with
list_dir(). - Recursive directory creation, file metadata, existence checks, removal, and rename operations.
- Namespaced helpers through
fs,process,snapshot,network, andpty. - microVM snapshots, restoration, and CoW fork.
- Host-side HTTP proxy.
- HTTP ACL allow and deny rules for method, host, and path filtering.
- Interactive PTY sessions.
- Python exception hierarchy and standard exception mapping.
- Global sandbox registry:
Sandbox.id,Sandbox.list(),SandboxInfo. - Runtime resource metrics:
Sandbox.metrics(),SandboxMetrics. - Persistent environment variables at creation:
env_varsparameter. - Structured sandbox error attributes, including stable error codes, suggestions, process output, and resource-limit exceptions.
Build from source (recommended):
cd mimobox && maturin develop --features vmOS-level backend only:
maturin developNotes:
--features vmrequires a Linux + KVM environment to fully use microVM capabilities.- The OS-level backend is suitable for fast local development, basic command execution, and API debugging.
Sandbox is the main entry point and delegates internally to the Rust SDK's Sandbox.
Constructor:
Sandbox(*, isolation=None, allowed_http_domains=None, http_acl_allow=None, http_acl_deny=None, memory_limit_mb=None, timeout_secs=None, max_processes=None, trust_level=None, network=None, env_vars=None)Parameters:
isolation: Isolation level. The defaultNoneis equivalent to"auto".allowed_http_domains: List of domains allowed by the HTTP proxy. Exact hosts and leading wildcard domains such as"*.example.com"are supported.http_acl_allow: Optional list of HTTP ACL allow rules in"METHOD host/path"format. Hosts support exact matches,*, and leading wildcard domains; paths support exact matches,/*, and prefix rules ending in/*.http_acl_deny: Optional list of HTTP ACL deny rules in"METHOD host/path"format. Deny rules take precedence over allow rules.memory_limit_mb: Memory limit in MiB. Defaults to 512.timeout_secs: Sandbox command timeout in seconds. Defaults to 30.max_processes: Maximum process count. Defaults to backend default (unlimited).trust_level: Trust level:"trusted","semi_trusted", or"untrusted". Defaults to"semi_trusted".network: Network policy:"deny_all","allow_domains", or"allow_all". Defaults to"deny_all".env_vars: Optional dict of persistent environment variables set at sandbox creation. These are applied to every subsequent command. Security-critical and baseline names (LD_PRELOAD,LD_LIBRARY_PATH,BASH_ENV,ENV,DYLD_INSERT_LIBRARIES,DYLD_LIBRARY_PATH,PATH,HOME,TMPDIR,PWD,SHELL,USER,LOGNAME) are blocked. Keys with=, NUL, or spaces, and values with NUL, are rejected. The Python layer also caps this input to 64 variables, 128 bytes per key, 8 KiB per value, and 64 KiB total.
Public methods:
execute(command, env=None, timeout=None, cwd=None) -> ExecuteResult
execute_code(language, code, *, env=None, timeout=None, cwd=None) -> ExecuteResult
stream_execute(command, env=None, timeout=None, cwd=None) -> StreamIterator
list_dir(path) -> list[DirEntry]
read_file(path) -> bytes
write_file(path, data: bytes)
snapshot -> SnapshotOps
fork() -> Sandbox
http_request(method, url, headers=None, body=None) -> HttpResponse
wait_ready(timeout_secs=None)
is_ready -> bool
close()Additional public methods and properties:
exec(argv, env=None, timeout=None, cwd=None) -> ExecuteResult
stream_exec(argv) -> StreamIterator
active_isolation -> str | None
id -> str | None
info -> SandboxInfo
env_vars -> dict[str, str]
metrics -> SandboxMetrics
make_dir(path)
stat(path) -> FileStat
file_exists(path) -> bool
remove_file(path)
rename(from, to)
fs -> FileSystem
process -> Process
snapshot -> SnapshotOps
network -> Network
pty -> PtyClass method:
Sandbox.from_snapshot(snapshot) -> Sandbox
Sandbox.list() -> list[SandboxInfo]Executes a shell command inside the sandbox.
Parameters:
command: str: Shell command to execute.env: dict | None: Optional environment variables to set for the command.timeout: float | None: Optional timeout in seconds. Must be > 0 and finite.cwd: str | None: Optional working directory for the command. When set, the binding parses the command and passescwdthroughexec_with_optionsrather than concatenating a shellcdcommand.
Example:
with Sandbox() as sb:
result = sb.execute("ls -la", cwd="/workspace")
print(result.stdout)Executes source code in the specified language inside the sandbox. This is a convenience method that wraps the code in the appropriate interpreter invocation.
Supported languages: "bash", "sh", "shell", "python", "python3", "py", "javascript", "js", "node", "nodejs".
Parameters:
language: str: Programming language name.code: str: Source code to execute.env: dict | None: Optional environment variables to set for the command.timeout: float | None: Optional timeout in seconds. Must be > 0 and finite.cwd: str | None: Optional working directory for the command.
Example:
with Sandbox() as sb:
result = sb.execute_code("python", "print(42)")
print(result.stdout, end="")
result = sb.execute_code("bash", "echo hello", timeout=5.0)
print(result.stdout, end="")
result = sb.execute_code("node", "console.log('hi')", cwd="/app")
print(result.stdout, end="")Raises:
ValueError: If the language is not supported.
Context manager:
with Sandbox() as sb:
result = sb.execute("/bin/echo hello")When the with block exits, the underlying sandbox resources are released.
Command execution result.
Attributes:
stdout: str: Standard output, decoded using UTF-8 lossy decoding.stderr: str: Standard error, decoded using UTF-8 lossy decoding.exit_code: int: Exit code;-1when the underlying layer has no exit code.timed_out: bool: Whether the execution timed out.elapsed: float | None: Execution duration in seconds;Nonewhen unknown.
A single directory entry returned by Sandbox.list_dir().
Attributes:
name: str: File or directory name.file_type: str: Type string:"file","dir","symlink", or"other".size: int: File size in bytes.is_symlink: bool: Whether this entry is a symbolic link.
HTTP proxy response.
Attributes:
status: int: HTTP status code.headers: dict: Response headers.body: bytes: Raw response body bytes.
microVM sandbox snapshot.
Methods:
Snapshot.from_bytes(data: bytes) -> Snapshot
Snapshot.from_file(path: str) -> Snapshot
to_bytes() -> bytes
size -> intNotes:
from_bytesis a classmethod that reconstructs a snapshot from its serialized byte representation.from_fileis a classmethod that loads a snapshot from a file on disk, without reading the entire file into memory. Suitable for large snapshot files previously saved viato_bytes().to_bytes()serializes the snapshot into bytes.sizereturns the snapshot size.
Streaming execution event.
Attributes:
stdout: bytes | None: stdout chunk.stderr: bytes | None: stderr chunk.exit_code: int | None: Exit code event.timed_out: bool: Whether this is a timeout event.
Each event usually carries only one kind of information: stdout, stderr, exit code, or timeout.
StreamIterator is the iterator returned by Sandbox.stream_execute().
Behavior:
- Implements the Python iteration protocol.
- Yields one
StreamEventon each iteration. - Stops iteration after the underlying stream ends.
Example:
for event in sb.stream_execute("/bin/sh -c 'echo hello; echo err >&2'"):
if event.stdout is not None:
print(event.stdout.decode(), end="")list_dir(path) -> list[DirEntry]Lists directory entries inside the sandbox.
Parameters:
path: str: Absolute path inside the sandbox filesystem.
Returns a list of DirEntry objects.
Example:
with Sandbox(isolation="microvm") as sb:
entries = sb.list_dir("/tmp")
for entry in entries:
print(f"{entry.name} ({entry.file_type}) - {entry.size} bytes")Raises:
SandboxError: If the directory cannot be read.
close()Explicitly releases sandbox resources. Safe to call multiple times; subsequent calls are no-ops. Also called automatically by the context manager exit.
exec(argv, env=None, timeout=None, cwd=None) -> ExecuteResultExecutes a command with explicit argv. Unlike execute(), this method does not
use shell parsing, shell expansion, pipes, redirection, or quoting rules. Each
item in argv is passed as one argument.
Parameters:
argv: list[str]: Command and arguments. The list must be non-empty.env: dict | None: Optional environment variables to set for the command.timeout: float | None: Optional timeout in seconds. Must be > 0 and finite.cwd: str | None: Optional working directory for the command.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
result = sb.exec(["ls", "-la", "/tmp"], timeout=5.0)
print(result.stdout)
# Shell metacharacters are ordinary argument bytes here.
result = sb.exec(["printf", "%s\n", "hello; rm -rf /"])
print(result.stdout, end="")Notes:
- Use
exec()for user-controlled arguments to avoid shell injection risks. - Use
execute()when you explicitly need shell features such as pipes, glob expansion, redirection, or compound commands. - Passing an empty
argvraisesValueError.
stream_exec(argv) -> StreamIteratorExecutes an argv-style command and returns a streaming iterator of
StreamEvent objects. It is the argv-style counterpart to
stream_execute(), which accepts a shell command string.
Parameters:
argv: list[str]: Command and arguments. The list must be non-empty.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
sb.make_dir("/tmp/src")
sb.write_file("/tmp/src/a.txt", b"pattern\nother\n")
sb.write_file("/tmp/src/b.txt", b"pattern again\n")
for event in sb.stream_exec(["grep", "-r", "pattern", "/tmp/src"]):
if event.stdout is not None:
print(event.stdout.decode("utf-8", errors="replace"), end="")
if event.stderr is not None:
print(event.stderr.decode("utf-8", errors="replace"), end="")
if event.exit_code is not None:
print(f"exit={event.exit_code}")Notes:
stream_exec()does not run through a shell, so|,>,*,$VAR, and quotes are not interpreted.- Use
stream_execute()when the command intentionally depends on shell syntax. - Passing an empty
argvraisesValueError.
active_isolation -> str | NoneReturns the actual isolation level of the active backend. This is useful when
isolation="auto" selects the backend at runtime.
Example:
from mimobox import Sandbox
with Sandbox(isolation="auto") as sb:
print(sb.active_isolation) # None before the first operation.
result = sb.execute("/bin/echo ready")
print(result.stdout, end="")
print(sb.active_isolation)Notes:
- The value is
Nonebefore the first operation initializes a backend. - After auto-routing, the value is the actual backend, such as
"os","wasm", or"microvm". - The returned value describes the active backend, not just the requested constructor argument.
id -> str | NoneReturns the unique UUID string assigned to this sandbox instance. The value is None after the sandbox has been closed.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
print(sb.id) # e.g. "a1b2c3d4-e5f6-7890-abcd-ef1234567890"Notes:
- The ID is a UUID4 string generated at sandbox creation.
- The ID is registered in the global sandbox registry and can be used for logging, tracing, and debugging.
Sandbox.list() -> list[SandboxInfo]Class method that returns all currently registered sandbox instances in the current process.
Example:
from mimobox import Sandbox
sb1 = Sandbox()
sb2 = Sandbox()
active = Sandbox.list()
print(f"{len(active)} sandbox(es) registered")
for info in active:
print(f" id={info.id}, isolation={info.active_isolation}, ready={info.is_ready}")
sb1.close()
sb2.close()Notes:
- This is a class method, called on the
Sandboxtype. - Closed sandboxes are automatically unregistered.
- Useful for orchestration, dashboard displays, and resource auditing.
metrics -> SandboxMetricsReturns runtime resource usage metrics from the most recent command execution. If no command has been executed yet, all fields return None.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
result = sb.execute("/bin/echo hello")
m = sb.metrics
if m.memory_usage_bytes is not None:
print(f"memory: {m.memory_usage_bytes} bytes")
if m.cpu_time_user_us is not None:
print(f"CPU user: {m.cpu_time_user_us} us")
if m.cpu_time_system_us is not None:
print(f"CPU system: {m.cpu_time_system_us} us")
if m.wasm_fuel_consumed is not None:
print(f"Wasm fuel: {m.wasm_fuel_consumed}")
if m.io_read_bytes is not None:
print(f"IO read: {m.io_read_bytes} bytes")
if m.io_write_bytes is not None:
print(f"IO write: {m.io_write_bytes} bytes")Notes:
- Metrics are cached after each
execute()call. - Not all backends populate every field.
collected_atis exposed as seconds elapsed since metric collection.
info -> SandboxInfoReturns a registry snapshot for the current sandbox. It is equivalent to
searching Sandbox.list() by sb.id, but avoids the extra lookup in user code.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
info = sb.info
print(info.id)
print(info.configured_isolation)
print(info.active_isolation)
print(info.is_ready)env_vars -> dict[str, str]Returns a copy of the persistent environment variables configured at sandbox
creation. Backend built-in defaults such as HOME and PATH are not included.
Example:
from mimobox import Sandbox
with Sandbox(env_vars={"MIMOBOX_MODE": "audit"}) as sb:
print(sb.env_vars["MIMOBOX_MODE"])SandboxInfo represents a snapshot of a sandbox instance's registration info. Returned by Sandbox.list().
Attributes:
id: str: UUID string of the sandbox instance.is_ready: bool: Whether the backend is initialized and ready.configured_isolation: str | None: Configured isolation level string.active_isolation: str | None: Active isolation level string, such as"os","wasm", or"microvm".Nonebefore the first command.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
info = sb.info
print(info.id)
print(info.configured_isolation)
print(info.is_ready)
print(info.active_isolation)SandboxMetrics represents runtime resource usage metrics. Returned by Sandbox.metrics().
Attributes:
memory_usage_bytes: int | None: Current memory usage in bytes.memory_limit_bytes: int | None: Memory limit in bytes.cpu_time_user_us: int | None: User-mode CPU time in microseconds.cpu_time_system_us: int | None: Kernel-mode CPU time in microseconds.wasm_fuel_consumed: int | None: Wasm fuel consumed (Wasm backend only).io_read_bytes: int | None: I/O read bytes.io_write_bytes: int | None: I/O write bytes.collected_at: float | None: Seconds elapsed since metric collection.
The Sandbox constructor accepts env_vars as an optional dict of persistent environment variables:
from mimobox import Sandbox
with Sandbox(env_vars={"API_KEY": "secret", "DEBUG": "1"}) as sb:
result = sb.execute("/usr/bin/printenv API_KEY")
print(result.stdout, end="") # "secret"
# env_vars persist across commands
result = sb.execute("/usr/bin/printenv DEBUG")
print(result.stdout, end="") # "1"Notes:
env_varsare set at creation time and apply to every command.- Per-command
envparameter takes precedence overenv_vars. - Security-critical and baseline names are blocked:
LD_PRELOAD,LD_LIBRARY_PATH,BASH_ENV,ENV,DYLD_INSERT_LIBRARIES,DYLD_LIBRARY_PATH,PATH,HOME,TMPDIR,PWD,SHELL,USER,LOGNAME. - Python input quotas: maximum 64 variables, 128 bytes per key, 8 KiB per value, and 64 KiB total.
- Keys containing
=, NUL, or spaces are rejected. Values containing NUL are rejected.
make_dir(path)Creates a directory and any missing parent directories inside the sandbox.
Parameters:
path: str: Directory path inside the sandbox filesystem.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
sb.make_dir("/tmp/a/b/c")
sb.write_file("/tmp/a/b/c/hello.txt", b"hello\n")
print(sb.read_file("/tmp/a/b/c/hello.txt").decode("utf-8"), end="")Notes:
- The operation is recursive, similar to
mkdir -p. - Invalid paths such as empty strings, NUL bytes, or parent traversal raise
ValueError. - A backend failure raises
SandboxError.
FileStat represents file metadata returned by Sandbox.stat() and
Sandbox.fs.stat().
Attributes:
path: str: Path for the file metadata.is_dir: bool: Whether the path is a directory.is_file: bool: Whether the path is a regular file.size: int: File size in bytes.mode: int: File mode bits as reported by the backend.modified_ms: int | None: Last modification time in milliseconds, orNonewhen unavailable.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
sb.write_file("/tmp/file.txt", b"hello\n")
info = sb.stat("/tmp/file.txt")
print(info.path)
print(info.is_file)
print(info.is_dir)
print(info.size)
print(oct(info.mode))
print(info.modified_ms)Notes:
modeis backend and platform dependent.modified_mscan beNonewhen the backend cannot provide a timestamp.- Use
is_fileandis_dirinstead of deriving file type frommode.
stat(path) -> FileStatReturns metadata for a file or directory inside the sandbox.
Parameters:
path: str: Path inside the sandbox filesystem.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
sb.make_dir("/tmp/data")
sb.write_file("/tmp/data/input.txt", "hello")
file_stat = sb.stat("/tmp/data/input.txt")
dir_stat = sb.stat("/tmp/data")
print(file_stat.is_file, file_stat.size)
print(dir_stat.is_dir)Notes:
- Use
stat()when you need metadata, not just existence. - Missing paths can raise
FileNotFoundError. - Permission or backend failures can raise
PermissionErrororSandboxError.
file_exists(path) -> boolChecks whether a path exists inside the sandbox.
Parameters:
path: str: Path inside the sandbox filesystem.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
print(sb.file_exists("/tmp/created.txt"))
sb.write_file("/tmp/created.txt", b"created\n")
print(sb.file_exists("/tmp/created.txt"))Notes:
- This method returns only a boolean.
- Use
stat()when you need file type, size, mode, or modification time. - Backend errors can still raise
SandboxError.
remove_file(path)Removes a file or an empty directory inside the sandbox.
Parameters:
path: str: File or empty directory path inside the sandbox filesystem.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
sb.write_file("/tmp/delete-me.txt", b"temporary\n")
print(sb.file_exists("/tmp/delete-me.txt"))
sb.remove_file("/tmp/delete-me.txt")
print(sb.file_exists("/tmp/delete-me.txt"))Notes:
- The method removes files and empty directories.
- It is not a recursive directory delete operation.
- Missing paths, non-empty directories, permission errors, or backend failures can raise an exception.
rename(from, to)Renames or moves a file inside the sandbox filesystem.
Parameters:
from: str: Source path inside the sandbox filesystem.to: str: Destination path inside the sandbox filesystem.
Example:
from mimobox import Sandbox
with Sandbox() as sb:
sb.write_file("/tmp/old.txt", b"renamed\n")
sb.rename("/tmp/old.txt", "/tmp/new.txt")
print(sb.file_exists("/tmp/old.txt"))
print(sb.read_file("/tmp/new.txt").decode("utf-8"), end="")Notes:
- Both paths are interpreted inside the sandbox.
- Parent directories for the destination must already exist.
- Permission errors or backend failures can raise an exception.
Sandbox.fs provides a file system namespace for common file operations.
Methods:
read(path) -> bytes
read_text(path, encoding="utf-8") -> str
write(path, data: str | bytes)
list(path) -> list[DirEntry]
exists(path) -> bool
remove(path)
mkdir(path)
copy(src, to)
rename(from, to)
stat(path) -> FileStatExample:
from mimobox import Sandbox
with Sandbox() as sb:
fs = sb.fs
fs.mkdir("/tmp/fs-demo")
fs.write("/tmp/fs-demo/input.txt", "hello from fs\n")
fs.copy("/tmp/fs-demo/input.txt", "/tmp/fs-demo/copy.txt")
fs.rename("/tmp/fs-demo/copy.txt", "/tmp/fs-demo/final.txt")
print(fs.read_text("/tmp/fs-demo/input.txt"), end="")
print(fs.read("/tmp/fs-demo/final.txt").decode("utf-8"), end="")
print(fs.exists("/tmp/fs-demo/final.txt"))
print(fs.stat("/tmp/fs-demo/final.txt").size)
for entry in fs.list("/tmp/fs-demo"):
print(entry.name)
fs.remove("/tmp/fs-demo/final.txt")Notes:
read()returnsbytes.read_text()returnsstrand accepts a configurableencodingargument, defaulting to"utf-8".write()acceptsstrorbytes.mkdir()creates missing parent directories.copy()copies files; userename()when you want move semantics.
Sandbox.process provides a process namespace for execution operations.
Methods:
run(command, env=None, timeout=None, cwd=None) -> ExecuteResult
run_code(language, code, *, env=None, timeout=None, cwd=None) -> ExecuteResult
stream(command) -> StreamIteratorExample:
from mimobox import Sandbox
with Sandbox() as sb:
shell_result = sb.process.run("echo shell")
print(shell_result.stdout, end="")
argv_result = sb.process.run(["printf", "%s\n", "argv"])
print(argv_result.stdout, end="")
code_result = sb.process.run_code("python", "print(6 * 7)")
print(code_result.stdout, end="")
for event in sb.process.stream("printf 'stream\\n'"):
if event.stdout is not None:
print(event.stdout.decode("utf-8"), end="")Notes:
run()accepts eitherstrorlist[str].- A
strcommand delegates toexecute()and uses shell-style execution. - A
list[str]command delegates toexec()and uses argv-style execution. stream()accepts only a shell command string and delegates tostream_execute().
Sandbox.snapshot provides a snapshot namespace. It is also callable for
backward compatibility, so sb.snapshot() still captures a snapshot.
Methods:
sb.snapshot() -> Snapshot
sb.snapshot.capture() -> Snapshot
sb.snapshot.fork() -> SandboxExample:
from mimobox import Sandbox, Snapshot
with Sandbox(isolation="microvm") as sb:
sb.write_file("/tmp/state.txt", b"snapshot state\n")
snap_a = sb.snapshot()
snap_b = sb.snapshot.capture()
child = sb.snapshot.fork()
try:
print(snap_a.size)
print(snap_b.size)
print(child.read_file("/tmp/state.txt").decode("utf-8"), end="")
finally:
child.close()Notes:
sb.snapshot()andsb.snapshot.capture()are equivalent.snapshot.fork()delegates toSandbox.fork()and returns an independentSandbox.- Snapshot support depends on backend capabilities; microVM is the intended backend for full snapshot behavior.
Sandbox.network provides a network namespace for HTTP proxy requests.
Methods:
request(method, url, headers=None, body=None) -> HttpResponseExample:
from mimobox import Sandbox
with Sandbox(
network="allow_domains",
allowed_http_domains=["example.com"],
) as sb:
response = sb.network.request(
"GET",
"https://example.com",
headers={"accept": "text/html"},
)
print(response.status)
print(response.headers.get("content-type", ""))
print(response.body[:80])Notes:
request()delegates tohttp_request().bodyisbytes | None.- Requests are subject to
network,allowed_http_domains, and HTTP ACL configuration.
Sandbox.pty provides interactive PTY sessions.
Methods and event objects:
sb.pty.create(command, *, cols=80, rows=24, env=None, cwd=None, timeout=None) -> PtySession
PtySession.send_input(data)
PtySession.resize(cols, rows)
PtySession.kill()
PtySession.wait(*, timeout=None) -> int
PtyOutput.data -> bytes
PtyExit.code -> intExample:
from mimobox import PtyExit, PtyOutput, Sandbox
with Sandbox() as sb:
with sb.pty.create(["/bin/sh"], cols=80, rows=24, timeout=10.0) as session:
session.send_input("echo hello from pty\n")
session.send_input("exit 0\n")
for event in session:
if isinstance(event, PtyOutput):
print(event.data.decode("utf-8", errors="replace"), end="")
elif isinstance(event, PtyExit):
print(f"exit={event.code}")
breakNotes:
commandcan bestrorlist[str].send_input()acceptsstrorbytes.- Iterating a
PtySessionyieldsPtyOutputandPtyExitobjects. PtyOutput.dataisbytes.PtyExit.codeisint.- The context manager kills the PTY session on exit if it is still active.
Sandbox accepts HTTP ACL rules through http_acl_allow and
http_acl_deny. Rules use "METHOD host/path" format. Deny rules take
precedence over allow rules.
Constructor parameters:
Sandbox(
*,
http_acl_allow=None,
http_acl_deny=None,
)Example:
from mimobox import Sandbox, SandboxHttpError
with Sandbox(
http_acl_allow=[
"GET example.com/*",
"POST api.example.com/v1/*",
],
http_acl_deny=[
"* api.example.com/v1/admin/*",
],
) as sb:
response = sb.http_request("GET", "https://example.com/")
print(response.status)
try:
sb.http_request("POST", "https://api.example.com/v1/admin/delete")
except SandboxHttpError as exc:
print(f"denied: {exc.code}")Notes:
- Rule format is
"METHOD host/path", for example"GET api.example.com/v1/*". METHODcan be an HTTP method or*.- Hosts support exact matches,
*, and leading wildcard domains such as*.example.com. - Paths support exact matches,
/*, and prefix rules ending in/*. - Deny rules are evaluated before allow rules.
allowed_http_domainsremains supported and can be used for domain-level allow lists.network="allow_all"is not compatible with HTTP ACL rules; use allow-domain style policy when applying ACLs.
The Python SDK defines the following mimobox exceptions:
SandboxError: Base class.SandboxProcessError: Command execution error when the backend reportsCommandExitorCommandKilled. The normal execution result path returns non-zero process exits inExecuteResult.exit_code.SandboxHttpError: HTTP proxy error, such as a rejected domain, invalid URL, or oversized body.SandboxLifecycleError: Sandbox lifecycle error, such as not ready, destroyed, or failed to create.
It also maps backend error codes to standard or SDK-specific Python exceptions:
SandboxTimeoutError: Command or HTTP request timeout.FileNotFoundError: File does not exist.PermissionError: File permission denied.ConnectionError: HTTP connection or TLS failure.ValueError: Invalid configuration or parameter.NotImplementedError: Unsupported on the current platform or feature set.
Callers are encouraged to catch specific exceptions first, then use SandboxError as a fallback:
from mimobox import Sandbox, SandboxError, SandboxHttpError
try:
with Sandbox(isolation="microvm") as sb:
sb.http_request("GET", "https://example.com")
except SandboxHttpError as exc:
print(f"HTTP proxy failed: {exc}")
except SandboxError as exc:
print(f"sandbox failed: {exc}")Additional exception attributes and subclasses:
SandboxError.code: str | None: Stable backend error code, such as"command_timeout", orNonewhen not provided.SandboxError.suggestion: str | None: Human-readable remediation suggestion, orNonewhen not provided.SandboxProcessError.exit_code: int: Process exit code, or-1when the process was killed and no code is available.SandboxProcessError.stdout: bytes: Currently empty bytes in the Rust binding.SandboxProcessError.stderr: bytes: Currently empty bytes in the Rust binding.SandboxMemoryError: Raised when the memory limit is exceeded.SandboxCpuLimitError: Raised when the CPU limit is exceeded.SandboxTimeoutError: Raised on command or HTTP timeout.
Example:
from mimobox import (
Sandbox,
SandboxCpuLimitError,
SandboxError,
SandboxMemoryError,
SandboxProcessError,
SandboxTimeoutError,
)
try:
with Sandbox(memory_limit_mb=128, timeout_secs=5.0) as sb:
sb.exec(["/bin/sh", "-c", "echo out; echo err >&2; exit 7"])
except SandboxProcessError as exc:
print(exc.exit_code)
print(exc.stdout.decode("utf-8", errors="replace"))
print(exc.stderr.decode("utf-8", errors="replace"))
print(exc.code)
print(exc.suggestion)
except SandboxMemoryError as exc:
print(f"memory limit exceeded: {exc.code}")
except SandboxCpuLimitError as exc:
print(f"cpu limit exceeded: {exc.code}")
except SandboxTimeoutError as exc:
print(f"operation timed out: {exc.code}")
except SandboxError as exc:
print(f"sandbox failed: {exc.code} {exc.suggestion}")Notes:
- Catch specific subclasses before
SandboxError. codeandsuggestionare optional and may beNone.stdoutandstderronSandboxProcessErrorare bytes, not strings.
Sandbox(isolation=...) supports the following values:
"auto": Default intelligent routing."os": OS-level sandbox."wasm": Wasm sandbox."microvm": microVM sandbox."micro_vm": Alias for"microvm"."micro-vm": Alias for"microvm".
Omitting isolation is equivalent to "auto".
Intelligent routing semantics:
.wasm,.wat, and.wastfiles prefer Wasm.- Untrusted tasks can use the microVM fail-closed path in the Rust SDK through
TrustLevel::Untrusted. - Other normal commands use the OS-level sandbox by default.
from mimobox import Sandbox, Snapshot
with Sandbox(isolation="auto") as sb:
result = sb.execute("/bin/echo hello")
print(result.stdout, end="")
print(f"exit_code={result.exit_code}")from mimobox import Sandbox, Snapshot
with Sandbox(isolation="os") as sb:
result = sb.execute(
"/usr/bin/printenv MIMOBOX_MODE",
env={"MIMOBOX_MODE": "python-demo"},
timeout=5.0,
)
print(result.stdout, end="")from mimobox import Sandbox
with Sandbox(isolation="microvm") as sb:
stream = sb.stream_execute("/bin/sh -c 'echo start; echo err >&2; echo done'")
for event in stream:
if event.stdout is not None:
print(event.stdout.decode("utf-8", errors="replace"), end="")
if event.stderr is not None:
print(event.stderr.decode("utf-8", errors="replace"), end="")
if event.exit_code is not None:
print(f"exit={event.exit_code}")
if event.timed_out:
print("timed out")from mimobox import Sandbox
with Sandbox(
isolation="microvm",
allowed_http_domains=["example.com"],
) as sb:
response = sb.http_request("GET", "https://example.com")
print(response.status)
print(response.body[:80])from mimobox import Sandbox
with Sandbox(isolation="microvm") as sb:
sb.write_file("/tmp/hello.txt", b"hello from python\n")
data = sb.read_file("/tmp/hello.txt")
print(data.decode("utf-8"), end="")from mimobox import Sandbox, Snapshot
with Sandbox(isolation="microvm") as sb:
sb.write_file("/tmp/state.txt", b"before snapshot\n")
snapshot = sb.snapshot()
snapshot_bytes = snapshot.to_bytes()
restored_snapshot = Snapshot.from_bytes(snapshot_bytes)
with Sandbox.from_snapshot(restored_snapshot) as restored:
data = restored.read_file("/tmp/state.txt")
print(data.decode("utf-8"), end="")from mimobox import Sandbox
with Sandbox(isolation="microvm") as parent:
parent.write_file("/tmp/state.txt", b"parent\n")
child = parent.fork()
try:
child.write_file("/tmp/state.txt", b"child\n")
parent_data = parent.read_file("/tmp/state.txt")
child_data = child.read_file("/tmp/state.txt")
print(parent_data.decode("utf-8"), end="")
print(child_data.decode("utf-8"), end="")
finally:
child.__exit__(None, None, None)Note: fork() returns an independent sandbox. The example explicitly releases child by calling the context manager's exit method; in real code, it is recommended to wrap the forked object in your own resource management logic.
from mimobox import Sandbox
with Sandbox(isolation="microvm") as sb:
# Python
result = sb.execute_code("python", "import sys; print(sys.version)")
print(result.stdout, end="")
# Bash
result = sb.execute_code("bash", "uname -a", timeout=5.0)
print(result.stdout, end="")
# JavaScript (requires Node.js in the sandbox)
result = sb.execute_code("node", "console.log('hello from node')", cwd="/app")
print(result.stdout, end="")from mimobox import Sandbox
with Sandbox(isolation="microvm") as sb:
sb.write_file("/tmp/test.txt", b"hello")
entries = sb.list_dir("/tmp")
for entry in entries:
symlink_info = " -> symlink" if entry.is_symlink else ""
print(f"{entry.name} ({entry.file_type}) - {entry.size} bytes{symlink_info}")from mimobox import Sandbox, Snapshot
# Save snapshot to disk
with Sandbox(isolation="microvm") as sb:
sb.write_file("/tmp/state.txt", b"saved to disk")
snapshot = sb.snapshot()
with open("/tmp/snapshot.bin", "wb") as f:
f.write(snapshot.to_bytes())
# Restore from file
snapshot = Snapshot.from_file("/tmp/snapshot.bin")
with Sandbox.from_snapshot(snapshot) as restored:
data = restored.read_file("/tmp/state.txt")
print(data.decode("utf-8"), end="")from mimobox import PtyExit, PtyOutput, Sandbox
with Sandbox(isolation="microvm") as sb:
with sb.pty.create(["/bin/sh"], cols=100, rows=30, timeout=10.0) as session:
session.send_input("echo interactive\n")
session.resize(120, 40)
session.send_input("exit 0\n")
for event in session:
if isinstance(event, PtyOutput):
print(event.data.decode("utf-8", errors="replace"), end="")
elif isinstance(event, PtyExit):
print(f"exit={event.code}")
breakfrom mimobox import Sandbox, SandboxHttpError
with Sandbox(
isolation="microvm",
http_acl_allow=[
"GET example.com/*",
"POST api.example.com/v1/*",
],
http_acl_deny=[
"* api.example.com/v1/admin/*",
],
) as sb:
response = sb.network.request("GET", "https://example.com/")
print(response.status)
try:
sb.network.request("POST", "https://api.example.com/v1/admin/delete")
except SandboxHttpError as exc:
print(f"denied by ACL: {exc.code}")from mimobox import Sandbox
with Sandbox(isolation="microvm") as sb:
fs = sb.fs
fs.mkdir("/tmp/project/data")
fs.write("/tmp/project/data/input.txt", "hello\n")
fs.copy("/tmp/project/data/input.txt", "/tmp/project/data/copy.txt")
fs.rename("/tmp/project/data/copy.txt", "/tmp/project/data/final.txt")
print(fs.read_text("/tmp/project/data/input.txt"), end="")
print(fs.read("/tmp/project/data/final.txt").decode("utf-8"), end="")
info = fs.stat("/tmp/project/data/final.txt")
print(f"size={info.size}")
for entry in fs.list("/tmp/project/data"):
print(f"{entry.name}: {entry.file_type}")
fs.remove("/tmp/project/data/final.txt")
print(fs.exists("/tmp/project/data/final.txt"))from mimobox import Sandbox
with Sandbox(isolation="microvm") as sb:
result = sb.exec(["printf", "%s\n", "hello from exec"])
print(result.stdout, end="")
sb.make_dir("/tmp/search")
sb.write_file("/tmp/search/a.txt", b"needle\n")
sb.write_file("/tmp/search/b.txt", b"haystack\nneedle\n")
for event in sb.stream_exec(["grep", "-r", "needle", "/tmp/search"]):
if event.stdout is not None:
print(event.stdout.decode("utf-8", errors="replace"), end="")
if event.stderr is not None:
print(event.stderr.decode("utf-8", errors="replace"), end="")
if event.exit_code is not None:
print(f"exit={event.exit_code}")from mimobox import Sandbox
with Sandbox(isolation="microvm") as sb:
sb.make_dir("/tmp/a/b/c")
sb.write_file("/tmp/a/b/c/result.txt", b"created recursively\n")
stat = sb.stat("/tmp/a/b/c/result.txt")
print(stat.path)
print(stat.is_file)
print(sb.read_file("/tmp/a/b/c/result.txt").decode("utf-8"), end="")