A kernel-enforced, lightweight process sandbox for AI agent workloads (and other usecases). Powered by seatbelt (MacOS) + landlock (Linux).
Lightbox uses Landlock and seccomp-BPF on Linux and Seatbelt on macOS to confine a single child process so it cannot read, write, execute, or connect outside the policy you declare.
go install github.com/burntcarrot/lightbox/cmd/lightbox@latestOr from source:
git clone https://github.com/burntcarrot/lightbox
cd lightbox
go build -o lightbox ./cmd/lightboxRequirements
- Linux 5.13+ (landlock ABI v1) with seccomp-BPF (kernel 3.17+).
- macOS 10.5+ requires XCode command line tools for the CGo build.
lightbox checkOn Linux this prints the landlock ABI version your kernel supports and whether seccomp-BPF is available. On macOS it confirms Seatbelt is present.
If landlock is below v5, lightbox degrades gracefully and logs which protections are weaker.
mkdir -p ~/.config/lightbox/profiles
# if you cloned the repo:
cp examples/profiles/*.toml ~/.config/lightbox/profiles/
# confirm they are visible:
lightbox profile listYou should see minimal, offline, code, browsing, restrict, build, claude, codex, and opencode.
lightbox run --profile-file examples/profiles/code.toml -- echo "sandbox active" ┌─┐
│█│ lightbox v0.1.0
└─┘ kernel-enforced sandbox for AI agents
Capabilities:
Filesystem:
/home/user/.cache [read ] (dir)
/home/user/.config/git [read ] (dir)
/home/user/.gitconfig [read ] (file)
/home/user/project [read+protect] (dir)
/tmp [read+write ] (dir)
+ 13 system/group paths (use -v to show)
Network:
blocked
› applying kernel sandbox protections...
sandbox active
Pass -v to see every system path and deny rule:
lightbox run -v --profile restrict -- python3 untrusted_script.pyProfiles are plain TOML files in ~/.config/lightbox/profiles. Lightbox
follows the XDG Base Directory spec on both Linux and macOS:
$XDG_CONFIG_HOME/lightbox/profiles -> ~/.config/lightbox/profiles
Print the exact path on your machine:
lightbox profile dirA profile may extends one other profile (one level, resolved at load time).
The parent must also live in the config directory.
[profile]
name = "myprofile"
extends = "code" # optional
[filesystem]
read = ["/usr", "/lib", "$HOME/.cache"]
protect = ["$PWD"] # write without delete
write = ["/tmp", "$HOME/.myapp"] # full write including delete
exec = ["/usr/bin", "/usr/local/bin"]
deny_read = ["$HOME/.ssh", "$HOME/.aws"]
deny_write = ["/sensitive"]
deny_unlink = false # global kill switch for all deletion
[limits]
max_procs = 256 # RLIMIT_NPROC (0 = no limit)
max_file_size_mb = 512 # RLIMIT_FSIZE (0 = no limit)
max_open_files = 4096 # RLIMIT_NOFILE (0 = no limit)
[network]
mode = "filter" # block | filter | allow-all
allow = [
"api.example.com:443",
"github.com:443",
]Variables are expanded in all path fields: $HOME, $PWD, $TMPDIR,
$PID, and any environment variable.
Access level table
| Field | Creates | Modifies | Deletes | Notes |
|---|---|---|---|---|
read |
no | no | no | Read-only |
protect |
yes | yes | no | Write without unlink or rmdir |
write |
yes | yes | yes | Full POSIX write semantics |
deny_read |
Overrides read grants | |||
deny_write |
Overrides write grants | |||
deny_unlink |
Blocks all deletion globally |
protect is the right choice for agent working directories. The agent can
create and modify files but cannot delete them. This is enforced at the
Landlock layer (no AccessFSRemoveFile/AccessFSRemoveDir bits granted) on
Linux and by enumerating specific Seatbelt write sub-operations (omitting
file-write-unlink) on macOS. There is no command-name blocklist and none
is needed: a process that calls unlink(2) directly will be blocked
regardless of what binary it came from.
/tmp and agent config directories should stay in write so subprocess
cleanup and state management continue to work normally.
deny_unlink = true is a stronger global switch: every unlink/rmdir
call fails regardless of path. On Linux this is enforced by both Landlock
and seccomp-BPF. On macOS by Seatbelt (deny file-write-unlink).
When a profile uses extends, limits merge by taking the stricter (lower,
nonzero) value. A child profile can tighten a parent's limits but cannot
loosen them.
Profile table
| Profile | Extends | Network | Protect | Write | Limits | Use case |
|---|---|---|---|---|---|---|
minimal |
blocked | none | none | none | Read-only inspection | |
offline |
minimal |
blocked | $PWD |
none | none | Air-gapped local work |
code |
blocked | $PWD |
/tmp |
none | Local coding, no network | |
browsing |
minimal |
filter (user-specified) | none | /tmp |
none | Web research with allowlist |
restrict |
minimal |
blocked | none | none | procs=32, file=64M, fd=256 | Untrusted binaries, read-only |
build |
code |
filter (major registries) | $PWD |
/tmp + caches |
procs=256, file=2G, fd=8192 | CI / compilation |
claude |
code |
filter (Anthropic endpoints) | $PWD |
/tmp + ~/.claude* |
procs=512, file=512M, fd=4096 | Claude Code |
codex |
code |
filter (OpenAI endpoints) | $PWD |
/tmp + ~/.codex |
procs=512, file=512M, fd=4096 | OpenAI Codex CLI |
opencode |
code |
filter (multi-provider) | $PWD |
/tmp + opencode dirs |
procs=512, file=512M, fd=4096 | anomalyco/opencode |
None of these are baked into the binary. They are plain TOML files in
examples/profiles/ that you copy, read, and edit.
Create a TOML file anywhere. Profiles can extend a built-in base so you only declare what changes:
[profile]
name = "myagent"
extends = "code"
[filesystem]
write = ["$HOME/.myagent"]
[limits]
max_procs = 128
max_file_size_mb = 256
[network]
mode = "filter"
allow = ["api.myagent.com:443"]Validate the file before using it:
lightbox profile validate myagent.tomlOption A: use it directly (no install needed):
lightbox run --profile-file ./myagent.toml -- myagentOption B: install it so you can refer to it by name:
cp myagent.toml $(lightbox profile dir)/
lightbox run --profile myagent -- myagentlightbox run [flags] -- command [args...]
lightbox learn [flags] -- command [args...]
lightbox why --path <path> [--op read|write|exec] [profile flags]
lightbox profile list
lightbox profile dir
lightbox profile show [--profile NAME | --file PATH] [--format toml|seatbelt|landlock]
lightbox profile validate PATH
lightbox check
Flag reference
| Flag | Description |
|---|---|
-p, --profile |
Installed profile name |
-f, --profile-file |
Path to a profile TOML file |
--read |
Additional read-only paths (repeatable) |
--write |
Additional read-write paths (repeatable) |
--protect |
Write paths where deletion is denied (repeatable) |
--exec |
Additional exec paths (repeatable) |
--deny-read |
Explicit read denials (repeatable) |
--deny-write |
Explicit write denials (repeatable) |
--deny-unlink |
Block all file and directory deletion |
--allow-net |
Allow outbound host:port (repeatable) |
--allow-all-net |
Allow all outbound network (dangerous) |
--block-net |
Block all outbound network (default) |
--ldd |
Auto-detect the target's shared libraries |
-v, --verbose |
Enable debug logging |
-q, --quiet |
Suppress the banner and capabilities header |
why evaluates a path against a profile and tells you exactly which rule
grants or denies it, useful for diagnosing EPERM without guesswork.
# Denied by the deny_read list
lightbox why --profile code --path ~/.ssh/id_rsa --op read path: /Users/you/.ssh/id_rsa
op: read
✗ matched deny list entry /Users/you/.ssh
-> denied
# Allowed by an explicit rule
lightbox why --profile code --path /tmp/work.txt --op write path: /tmp/work.txt
op: write
matched rule: /tmp [read+write]
-> allowed
# Default deny - no rule covers this path
lightbox why --profile code --path ~/Documents/secret.txt --op read path: /Users/you/Documents/secret.txt
op: read
✗ no rule covers this path
-> default deny
# Test an ad-hoc policy without a profile file
lightbox why --protect ~/project --path ~/project/plan.txt --op write path: /Users/you/project/plan.txt
op: write
matched rule: /Users/you/project [read+protect]
-> allowed (write allowed - delete denied (protect mode))
learn traces a command and reports every filesystem path and network
connection it touched, then suggests the minimal profile additions needed
to sandbox it.
Linux only.
learnusesstraceon Linux (install:apt/dnf/apk install strace). macOS kernel tracing (fs_usage, DTrace) is blocked by System Integrity Protection on macOS 14+.
lightbox learn -- python3 script.pyThe output lists observed accesses and prints a ready-to-paste [filesystem]
and [network] block you can drop into a profile TOML.
MIT
lightbox is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
A sandbox is a layer of defence, not a guarantee of isolation. Kernel enforcement depends on the ABI version, kernel configuration, and privileges of the host system. lightbox does not protect against vulnerabilities in the kernel itself, misconfigured profiles, or workloads running as root. You are solely responsible for validating that your profiles are appropriate for your threat model and environment.
