Run Untrusted Code Without Handing It Your Machine
Sandlock is a lightweight Linux process sandbox. It confines a command's filesystem, network, syscalls, and resources with kernel-enforced policy, in about 5 milliseconds. No root. No image build. No container runtime. No hypervisor.
# Read-only system dirs, one writable dir, 512 MB cap,
# and exactly one host reachable on exactly one port.
$ sandlock run -r /usr -r /lib -r /etc -w /tmp \
-m 512M -P 20 -t 300 \
--net-allow api.openai.com:443 \
-- python3 agent.py
Open source, Apache-2.0, written in Rust
Strict Confinement Without the Weight
Containers and microVMs are powerful, but both were designed to package and boot a system. When all you need is to run one command you do not trust, they make you pay for a system anyway.
Containers
Require an image build, a runtime daemon, and root or a configured user-namespace setup. Every container shares the host kernel, and the isolation boundary is a namespace, not a policy the kernel evaluates per access.
MicroVMs
A separate guest kernel gives a strong boundary, but it needs KVM, a guest image, and a device model. Nested virtualization inside a cloud VM adds real overhead, and there is no visibility into what the guest is doing at the syscall level.
Sandlock
A policy applied to a process. Landlock and seccomp-bpf enforce filesystem, network, IPC, and syscall rules in the kernel; a userspace supervisor adds resource limits, destination IP checks, HTTP method and path rules, and copy-on-write writes.
One Policy, Six Enforcement Surfaces
Every dimension of the sandbox is described in the same policy object, whether you write it as CLI flags, a TOML profile, or a struct in Python, Rust, or Go.
Filesystem
Landlock grants read and write paths as recursive rules the kernel evaluates on every access, so the policy is immune to time-of-check/time-of-use races. Denied paths override broader grants.
Network
Default-deny egress. Allow a hostname, an IP, or a CIDR on specific ports over TCP, UDP, or ICMP, or invert it with a denylist. Bind ports are governed separately, and can be virtualized per sandbox.
HTTP ACL
Go past host:443: allow POST api.openai.com/v1/chat/completions and nothing else. Zero-config HTTPS interception generates an ephemeral CA and splices it into the trust bundles you name.
Credential injection
The API key lives in the supervisor and is attached to the request in the proxy, strictly after the ACL check. The sandboxed process never holds the secret and cannot exfiltrate it.
Copy-on-write
Writes under the working directory are staged in an upper layer and committed on success or discarded on failure. --dry-run shows exactly which files a command would add, modify, or delete.
Resources
Memory, process count, open files, CPU percentage, disk quota, CPU pinning, and GPU device selection, all without cgroups. A given GPU index is a hard Landlock boundary, not an environment variable.
Kernel First, Supervisor Second
Sandlock forks, installs confinement in the child before it executes anything, and only then lets the workload start. What the kernel can enforce, the kernel enforces.
Landlock
Unprivileged, kernel-evaluated access control for filesystem paths, TCP connect and bind ports, and IPC scoping. Applied once and irreversible for the lifetime of the process tree.
seccomp-bpf
A default blocklist removes syscall families a confined workload has no business using. Extra denials compose on top; the blocklist itself is always applied.
seccomp notify
The supervisor sees selected syscalls before they run: destination IP checks, memory and process accounting, COW interception, /proc virtualization, and port remapping.
A Sandbox You Can Program, Not Just Configure
Every policy on this page is declarative. Handlers are the layer underneath: your own code runs inside the supervisor, registered on any syscall you choose, and decides what the workload actually observes.
ctypes and raw syscalls do not route around it.import sandlock
from sandlock.handler import Handler, NotifAction
class Audit(Handler):
def handle(self, ctx):
# Runs in the supervisor, before the kernel acts.
print(f"open {ctx.read_path()} from pid {ctx.pid}")
return NotifAction.continue_() # fall through
sb = sandlock.Sandbox(fs_readable=["/usr", "/lib", "/etc"])
sb.run_with_handlers(
cmd=["python3", "task.py"],
handlers=[("openat", Audit())],
)
Confinement You Can Leave On
Landlock and the seccomp blocklist cost nothing per syscall once installed. Only the syscalls the supervisor explicitly registers for take a userspace round trip.
| Workload | Bare metal | Sandlock | Docker |
|---|---|---|---|
/bin/echo startup |
2 ms | 7 ms | 307 ms |
| Redis SET, 100K ops | 82K rps | 80K rps | 52K rps |
| Redis GET, 100K ops | 79K rps | 77K rps | 53K rps |
| Redis p99 latency | 0.5 ms | 0.6 ms | 1.5 ms |
| COW fork × 1000 | No equivalent | 530 ms | No equivalent |
Measured on a typical Linux workstation. Redis under Sandlock holds 97.1% of bare-metal throughput. COW fork produces roughly 1,900 forks per second, or 530 µs per clone.
Reach for It From Where You Already Are
One Rust core, exposed through a CLI, a C ABI, three language SDKs, an OCI runtime, and an MCP server. Every surface takes the same policy.
sandlock run, plus ps, inspect, kill, learn, and profile management.
A Sandbox dataclass, pipelines, COW fork/reduce, and Python callbacks for dynamic policy.
The core library itself: a typed builder, async execution, and custom seccomp-notify handlers.
cgo bindings over the C ABI, with a plain configuration struct that is safe to share across goroutines.
A drop-in low-level runtime for containerd, CRI-O, and Kubernetes. Namespace-less and cgroup-less.
Sandboxed shell, Python, and file tools for Claude Desktop, Cursor, and any other MCP client.
Built for Code You Did Not Write
AI agents and tool use
Give an agent a workspace, one API endpoint, and nothing else. Credential injection keeps the key out of the agent's reach, and a policy callback can revoke network access the moment the agent finishes starting up.
Read moreCI and untrusted builds
Run a pull request's build steps with the source tree copy-on-write, the network closed, and a memory cap, on a shared runner with no root and no Docker socket to hand out.
Read morePer-request code execution
Notebook backends, code interpreters, function platforms, and grading systems that start a fresh sandbox per request. At 5 ms there is no warm pool to keep resident and nothing carries over.
Read moreStart Confining in Under Five Minutes
Sandlock is Apache-2.0 licensed, free at any scale, and builds from source with Cargo. When you outgrow one machine, the Sandbox HTTP API and Sandbox Scheduler run it as a fleet.