Agent Locker
A sandbox for running coding agents (and arbitrary commands) with restricted filesystem access. The whole filesystem is made read-only via Landlock; only a small, explicit set of paths is writable.
Requires Linux 5.13+ with Landlock enabled, and currently x86_64 only.
Usage
agent-locker [-C DIR] <command> [args...]
agent-locker [-C DIR] <preset> [args...]
-C, --context-dir DIR— the main writable project directory (defaults to the current directory).- The first positional selects a built-in preset (
claude,codex,opencode); anything else is run as a generic command.
What's writable
Every invocation grants write access to:
- the context directory (and its real git directory, if it's a worktree),
/tmp,/dev/nulland/dev/tty— the only device nodes any agent needs to write (verified viastrace). Reads elsewhere (e.g./dev/urandom) still work because the rest of the filesystem remains readable.
Everything else — including the home directory, /dev, and /run/user — is
read-only unless listed below.
Preset-specific writable paths
Presets additionally allow their own config/cache directories:
| Preset | Program | Extra writable paths |
|---|---|---|
claude |
claude |
~/.claude, ~/.claude.json |
codex |
codex |
~/.codex |
opencode |
opencode |
~/.opencode, ~/.local/share/opencode, ~/.cache/opencode |
Generic commands get no extra paths beyond the common set above.
Presets run in their default mode — agent-locker does not force flags like
--dangerously-skip-permissions. Use the config file to opt in.
Configuration
Optional config file at $XDG_CONFIG_HOME/agent-locker/config.toml, falling
back to ~/.config/agent-locker/config.toml when XDG_CONFIG_HOME is unset.
Each preset section may supply args, which are prepended to the preset's
command line, before any arguments you pass on the command line. A missing
config file is fine; a malformed one — including unknown sections or keys — is
an error.
Exhaustive example
# Arguments are prepended to the preset's command line, before any arguments
# passed on the command line. Each section is optional; omit a section to run
# that preset with no extra arguments.
[]
# Skip claude's interactive permission prompts.
= ["--dangerously-skip-permissions"]
[]
# Bypass codex's own approval/sandbox prompts.
= ["--dangerously-bypass-approvals-and-sandbox"]
[]
# opencode takes no special arguments by default; add your own here.
= []