The Sandlock Sandbox configuration follows a sectioned schema shared
by the CLI, Python SDK, and TOML profiles. Sections are named for the
concern they cover (config, determinism, program, filesystem,
network, http, syscalls, limits); the Python Sandbox
dataclass exposes the same fields as keyword arguments. Unless noted
otherwise, each field is optional, and omitting a field means "no
restriction" beyond Sandlock's default seccomp blocklist, which is
always applied.
Where a Python field name differs from its TOML key (mostly the
[filesystem] and [limits] sections, which drop the fs_ and max_
prefixes the dataclass uses), the field tables list both.
from sandlock import Sandbox, BranchAction
sandbox = Sandbox(
# [config]
http_ca=None, http_key=None,
fs_storage=None, workdir=None,
# [determinism]
random_seed=None, time_start=None,
deterministic_dirs=False, no_randomize_memory=False,
# [program] (process knobs only; exec/args are arguments to .run/.cmd)
env={}, cwd=None, uid=None,
clean_env=False, no_coredump=False, no_huge_pages=False,
# [filesystem]
fs_readable=(), fs_writable=(), fs_denied=(),
chroot=None, fs_mount={},
on_exit=BranchAction.COMMIT, on_error=BranchAction.ABORT,
# [network]
net_bind=(), net_allow=(), port_remap=False,
# [http]
http_ports=(), http_allow=(), http_deny=(),
# [syscalls]
extra_allow_syscalls=(), extra_deny_syscalls=(),
# [limits]
max_memory=None, max_processes=64, max_open_files=None,
max_cpu=None, max_disk=None,
gpu_devices=None, cpu_cores=None, num_cpus=None,
# Runtime kwargs (not serialized as policy)
name=None, policy_fn=None, init_fn=None, work_fn=None,
# Advanced (internal; usually configured via the fields above)
notif_policy=None,
)[config]
http_ca = "/path/to/ca.pem"
http_key = "/path/to/ca.key"
fs_storage = "/var/lib/sandlock"
workdir = "/opt/project"
[determinism]
random_seed = 42
time_start = "2026-01-01T00:00:00Z"
deterministic_dirs = true
no_randomize_memory = true
[program]
exec = "/usr/bin/make"
args = ["-j4"]
env = { CC = "gcc" }
cwd = "/work"
uid = 0
clean_env = true
no_coredump = true
no_huge_pages = true
[filesystem]
read = ["/usr", "/lib"]
write = ["/tmp"]
deny = ["/proc/kcore"]
chroot = "/opt/rootfs"
mount = ["/work:/host/sandbox/work"]
on_exit = "commit" # "commit" | "abort" | "keep"
on_error = "abort"
[network]
bind = [8080]
allow = ["api.example.com:443", "udp://1.1.1.1:53"]
port_remap = false
[http]
ports = [80]
allow = ["POST api.openai.com/v1/*"]
deny = ["* */admin/*"]
[syscalls]
extra_allow = ["sysv_ipc"]
extra_deny = []
[limits]
memory = "512M"
processes = 64
open_files = 256
cpu = 50
disk = "1G"
gpu_devices = [0]
cpu_cores = [0, 1]
num_cpus = 2Top-level configuration for the supervisor and COW workspace.
| Python | TOML | Type | Default | Description |
|---|---|---|---|---|
http_ca |
http_ca |
str | None |
None |
PEM CA certificate path for HTTPS MITM. When set, port 443 is added to http_ports. |
http_key |
http_key |
str | None |
None |
PEM CA private key path. Required whenever http_ca is set. |
fs_storage |
fs_storage |
str | None |
None |
Separate storage directory for the seccomp COW upper layer / deltas. |
workdir |
workdir |
str | None |
None |
COW root directory. Controls which directory COW tracks; does not set the child's working directory. |
HTTPS interception is opt-in: without http_ca, port 443 is not
intercepted, and net_allow host:443 permits raw TLS to the host with
no content inspection. When http_ca is set, the CA must be one the
caller has generated and installed into the sandbox's trust store
(typically /etc/ssl/certs/).
Knobs that pin sources of non-determinism in the child process.
| Python | TOML | Type | Default | Description |
|---|---|---|---|---|
random_seed |
random_seed |
int | None |
None |
Seed for deterministic getrandom(). Identical seeds yield identical byte streams. |
time_start |
time_start |
float | str | None |
None |
Frozen start time as a Unix timestamp or RFC 3339 / ISO 8601 string. Time advances at real speed from the given epoch. |
deterministic_dirs |
deterministic_dirs |
bool |
False |
Sort readdir() entries lexicographically so that ls, glob, and os.listdir return a stable order. |
no_randomize_memory |
no_randomize_memory |
bool |
False |
Disable ASLR via personality(ADDR_NO_RANDOMIZE). |
Process-level knobs applied to the child. In a TOML profile, exec
and args also live in this section; in the Python SDK those are
arguments to sandbox.run([...]) or sandbox.cmd([...]) and are not
fields on Sandbox.
| Python | TOML | Type | Default | Description |
|---|---|---|---|---|
env |
env |
Mapping[str, str] |
{} |
Variables to set or override in the child. Applied after clean_env. |
cwd |
cwd |
str | None |
None |
Child working directory (chdir target). Independent of workdir. |
uid |
uid |
int | None |
None |
UID to map the child to inside a user namespace (e.g. 0 for fake root). The child retains no host privileges regardless of the mapped UID. Requires user namespaces to be available. |
clean_env |
clean_env |
bool |
False |
When True, start with a minimal environment (PATH, HOME, USER, TERM, LANG) instead of inheriting the parent's. |
no_coredump |
no_coredump |
bool |
False |
Apply prctl(PR_SET_DUMPABLE, 0). Disables core dumps and restricts /proc/<pid> access from other processes. Breaks gdb, strace, and perf. |
no_huge_pages |
no_huge_pages |
bool |
False |
Disable transparent huge pages via prctl(PR_SET_THP_DISABLE). |
Landlock filesystem rules plus chroot, mount mapping, and COW filesystem isolation.
| Python | TOML | Type | Default | Description |
|---|---|---|---|---|
fs_readable |
read |
Sequence[str] |
() |
Paths the sandbox may read (in addition to fs_writable). |
fs_writable |
write |
Sequence[str] |
() |
Paths the sandbox may read and write. |
fs_denied |
deny |
Sequence[str] |
() |
Paths explicitly denied (neither read nor write), even if implied by a broader rule. |
chroot |
chroot |
str | None |
None |
Path to chroot into before applying other confinement. |
fs_mount |
mount |
Mapping[str, str] |
{} |
Map virtual paths inside the chroot to host directories. Python form: {"/work": "/host/sandbox/work"}. TOML form: list of "VIRTUAL:HOST" strings. |
on_exit |
on_exit |
BranchAction |
BranchAction.COMMIT |
Branch action on normal sandbox exit. |
on_error |
on_error |
BranchAction |
BranchAction.ABORT |
Branch action on sandbox error or exception. |
Landlock rules are kernel-evaluated and TOCTOU-immune.
Outbound allowlist, bind allowlist, and port virtualization. Each
entry of net_allow is a single rule of the form protocol, host,
port. Rules are OR'd. An empty net_allow denies all outbound
traffic. Protocol gating falls out of rule presence: without a UDP
rule, UDP socket creation is denied at the seccomp layer; without an
ICMP rule, kernel ping socket creation is denied. Raw ICMP (`SOCK_RAW
- IPPROTO_ICMP`) is never exposed. See the project README's "Network Model" section for the full grammar.
Rule shapes:
host:port[,port,...]: TCP, default scheme (no prefix).tcp://host:port: TCP, explicit scheme.udp://host:port: UDP.udp://*:*opens any UDP destination.icmp://host: kernel ping socket (SOCK_DGRAM + IPPROTO_ICMP).icmp://*opens any echo destination.
| Python | TOML | Type | Default | Description |
|---|---|---|---|---|
net_allow |
allow |
Sequence[str] |
() |
Outbound endpoint allowlist. Empty list denies all outbound. |
net_bind |
bind |
Sequence[int | str] |
() |
TCP ports the sandbox may bind. Each entry is a port or a "lo-hi" range. Landlock ABI v4+ (TCP only; UDP bind() is not separately gated). |
port_remap |
port_remap |
bool |
False |
Enable transparent TCP port virtualization. Each sandbox receives an independent virtual port space; conflicting binds are remapped to unique real ports via pidfd_getfd. |
Hostnames are resolved once at sandbox creation and pinned via a
synthetic /etc/hosts that is only injected when at least one rule
references a concrete host. Pure :port, udp://*:*, and icmp://*
rules leave the host's real DNS configuration visible.
HTTP-level access control via a transparent MITM proxy.
| Python | TOML | Type | Default | Description |
|---|---|---|---|---|
http_allow |
allow |
Sequence[str] |
() |
Allow rules of the form "METHOD host/path" with glob path matching. |
http_deny |
deny |
Sequence[str] |
() |
Deny rules, checked before allow rules. Same format as http_allow. |
http_ports |
ports |
Sequence[int] |
() |
TCP ports to intercept. Defaults to [80]; 443 is added when http_ca is set. |
When http_allow or http_deny is non-empty, the supervisor spawns
the proxy and redirects matching ports to it. HTTP rules with concrete
hosts auto-extend net_allow with the corresponding TCP entry on each
entry of http_ports (and on 443 when http_ca is set). Wildcard
hosts auto-add :80 (and :443 when http_ca is set). All
auto-added entries are TCP.
Adjustments to Sandlock's default seccomp-bpf blocklist. The default blocklist is applied unconditionally; the fields below alter it.
| Python | TOML | Type | Default | Description |
|---|---|---|---|---|
extra_allow_syscalls |
extra_allow |
Sequence[str] |
() |
Syscall group names to re-allow (e.g. "sysv_ipc"). |
extra_deny_syscalls |
extra_deny |
Sequence[str] |
() |
Additional syscall names to block on top of the default blocklist. |
Resource caps and visibility limits. The TOML schema drops the max_
prefix that the Python field names carry, because [limits] makes the
prefix redundant; the GPU and CPU placement fields keep their names.
| Python | TOML | Type | Default | Description |
|---|---|---|---|---|
max_memory |
memory |
str | int | None |
None |
Memory limit. Accepts strings such as "512M", "1G", or an integer byte count. |
max_processes |
processes |
int |
64 |
Maximum lifetime fork count permitted in the sandbox (not concurrent). Also enables fork interception used by checkpoint freeze. |
max_open_files |
open_files |
int | None |
None |
Maximum number of open file descriptors. Enforced via RLIMIT_NOFILE (kernel, survives exec). |
max_cpu |
cpu |
int | None |
None |
CPU throttle as a percentage of one core (1 to 100). Applied to the entire process group via SIGSTOP/SIGCONT cycling. |
max_disk |
disk |
str | None |
None |
COW storage quota (e.g. "1G"). Returned as ENOSPC when the upper layer exceeds it. |
gpu_devices |
gpu_devices |
Sequence[int] | None |
None |
GPU device indices to expose. None denies GPU access entirely; [] exposes every GPU; a list exposes only those devices. Adds Landlock rules for /dev/nvidia* and /dev/dri/* and sets CUDA_VISIBLE_DEVICES / ROCR_VISIBLE_DEVICES. |
cpu_cores |
cpu_cores |
Sequence[int] | None |
None |
CPU cores to pin the sandbox to via sched_setaffinity in the child. |
num_cpus |
num_cpus |
int | None |
None |
Visible CPU count in /proc/cpuinfo (renumbered 0..N-1). Also virtualizes /proc/meminfo when max_memory is set. |
These fields are not part of the policy serialization (they are
flagged with metadata={"runtime": True} and skipped by serializers)
and have no TOML counterpart.
| Field | Type | Default | Description |
|---|---|---|---|
name |
str | None |
None |
Sandbox name and virtual hostname inside the sandbox. Auto-generated as sandbox-{pid} when omitted. Maximum 64 bytes; must not contain NUL. |
policy_fn |
Callable | None |
None |
Per-event dynamic policy callback. See the project README's "Dynamic Policy" section. |
init_fn |
Callable | None |
None |
Callback invoked once in the template process prior to COW fork. |
work_fn |
Callable | None |
None |
Callback invoked in each COW clone; receives clone_id as its argument. |
| Field | Type | Default | Description |
|---|---|---|---|
notif_policy |
NotifPolicy | None |
None |
Seccomp user-notification policy for /proc and /sys virtualization. Usually configured implicitly by the other fields; advanced use only. |
class BranchAction(Enum):
COMMIT = "commit" # Merge branch writes into the parent branch.
ABORT = "abort" # Discard all branch writes.
KEEP = "keep" # Leave the branch as-is; caller decides.@dataclass(frozen=True)
class Change:
kind: str # "A" = added, "M" = modified, "D" = deleted.
path: str # Path relative to workdir.@dataclass
class DryRunResult:
success: bool
exit_code: int
stdout: bytes
stderr: bytes
changes: list[Change]
error: str | Nonefrom sandlock import parse_ports
parse_ports([80, "443", "8000-8005"])
# => [80, 443, 8000, 8001, 8002, 8003, 8004, 8005]- Default-deny network.
net_allow=()(the default) denies all outbound traffic. Protocol gating is a function of rule presence: the seccomp layer denies UDP and ICMP socket creation when no rule of that protocol is configured. - Seccomp COW with
workdir. Whenworkdiris set, the seccomp-based COW path intercepts writes underworkdirand stages them in an upper layer, committed or aborted on exit peron_exit/on_error. - HTTP host auto-expansion. HTTP rules referencing concrete hosts
auto-add corresponding TCP entries on
http_ports(and on443whenhttp_cais set). Wildcard hosts add the equivalent any-IP entries. All auto-added entries are TCP. - TOCTOU and
policy_fn. Path strings are never exposed on policy events because seccomp user notification re-reads user-memory pointers afterContinue. Path-based control belongs in static Landlock rules (fs_readable,fs_writable,fs_denied) or inctx.deny_path()for runtime additions.event.argvis exposed and TOCTOU-safe; the supervisor freezes peer tasks before exposing it.