arapuca - Man Page
sandbox a command with kernel-enforced isolation
Synopsis
arapuca run [flags] -- command [args...]
arapuca -h | --help
arapuca -V | --version
Description
arapuca run launches command in a process-level sandbox with user-friendly CLI flags. Uses the library’s platform abstraction for cross-platform support (Landlock + seccomp on Linux, Seatbelt on macOS, AppContainer on Windows).
The internal wrapper path (arapuca -- command) applies sandbox restrictions to the current process, then replaces itself with command via execve(2). Configured via environment variables. Used internally by the library as a subprocess wrapper — direct CLI invocations require ARAPUCA_WRAPPER=1 to be set by the library. Unrecognized subcommands or flags are rejected.
On Linux, arapuca enforces Landlock filesystem restrictions, seccomp BPF syscall filtering, POSIX resource limits, and sets PR_SET_PDEATHSIG so the subprocess is killed if the parent dies.
On macOS, arapuca enforces POSIX resource limits only (filesystem and network restrictions are applied by the caller via sandbox-exec(1)).
The sandbox is fail-closed: if any restriction fails to apply, arapuca exits non-zero and the target command never runs.
All ARAPUCA_* environment variables are stripped before exec so the sandboxed process cannot inspect its own configuration. Non-ARAPUCA variables (e.g., AGENT_NETWORK_PROXY) are preserved.
OPTIONS (arapuca run)
- -v path[:ro]
Allow access to path. Read-write by default; append :ro for read-only. Repeatable. Paths must be absolute. Default system paths (/usr, /lib, /bin, /etc/ssl, /tmp, device nodes) are readable. Only the private per-task temp dir is writable by default. /proc, /sys, and blanket /dev are NOT included by default. Use -v /tmp to grant write access to all of /tmp.
- --env KEY=VALUE
Pass an environment variable to the sandboxed process. Dangerous variables (LD_PRELOAD, ARAPUCA_*, DYLD_*, interpreter injection vectors) are rejected. Sandbox-managed variables (HOME, TMPDIR, PATH, LANG) cannot be overridden. Repeatable.
- --timeout N
Kill the process after N seconds. Sends SIGTERM first, then SIGKILL after a 5-second grace period. N must be greater than 0.
- --memory N
Memory limit in megabytes. Enforced via cgroups v2 on Linux, RSS polling on macOS, Job Objects on Windows.
- --cpus N
CPU limit as a percentage of a single core. 200 means 2 cores.
- --pids N
Maximum number of PIDs (cgroups v2 on Linux, Job Objects on Windows).
- --task-id NAME
Identifier for cgroup directory and audit events. Must match [a-zA-Z0-9-]+, max 128 characters. Defaults to run-pid.
- --seccomp mode
Seccomp BPF filter profile. strict (default) blocks AF_INET, symlink, memfd_create, io_uring, and other syscalls — designed for untrusted code. baseline blocks only sandbox-escape syscalls (ptrace, mount, namespace ops, kernel modules, bpf) and adds
/proc+/sysread access — designed for trusted-but-isolated applications like Claude Code that need full runtime capabilities while being confined by Landlock + netns.- --allow-host host:port
Allow HTTPS traffic to host:port via a CONNECT proxy tunnel. Repeatable. Supports exact match (api.example.com:443) and wildcard suffix match (*.googleapis.com:443 — matches the domain itself and any subdomain). When specified, the sandboxed process runs in a network namespace with no direct network access. A CONNECT proxy on the host network tunnels traffic only to listed hosts. DNS resolution happens in the proxy with IP validation (loopback, RFC 1918, CGNAT, link-local, and cloud metadata addresses are rejected to prevent DNS rebinding SSRF). Linux only.
- -t, --tty
Allocate a pseudo-terminal (PTY) for the sandboxed process. Enables interactive programs (shells, editors, TUI applications) that require a terminal. Requires stdin and stdout to be a terminal. The TERM environment variable is forwarded from the host (sanitized).
ENVIRONMENT (internal wrapper)
- ARAPUCA_WRAPPER
Internal sentinel. Must be set to 1 by the library when invoking the wrapper path. Direct invocations without this variable are rejected. Stripped before exec.
The following variables configure the internal wrapper path. They are not used by arapuca run (which uses CLI flags instead).
- ARAPUCA_READ_PATHS
Colon-separated list of paths the subprocess may read. Each path and everything below it is readable.
ARAPUCA_READ_PATHS=/usr:/lib:/lib64:/bin:/etc:/dev
- ARAPUCA_WRITE_PATHS
Colon-separated list of paths the subprocess may read and write.
ARAPUCA_WRITE_PATHS=/tmp/workspace
- ARAPUCA_RLIMIT_AS
Maximum virtual memory size in bytes. Enforced via setrlimit(2) (RLIMIT_AS). Set to 0 or omit to leave unlimited.
On Apple Silicon, RLIMIT_AS should not be set – macOS aggressively maps virtual memory and setting this limit causes immediate SIGKILL.
- ARAPUCA_RLIMIT_NPROC
Maximum number of processes for the user. Enforced via setrlimit(2) (RLIMIT_NPROC). Set to 0 or omit to leave unlimited.
- ARAPUCA_RLIMIT_CPU
Maximum CPU time in seconds. Enforced via setrlimit(2) (RLIMIT_CPU). The kernel sends SIGXCPU when the soft limit is reached and SIGKILL at the hard limit. Set to 0 or omit to leave unlimited.
- ARAPUCA_RLIMIT_FSIZE
Maximum file size in bytes. Enforced via setrlimit(2) (RLIMIT_FSIZE). Writes that would exceed the limit receive SIGXFSZ. Set to 0 or omit to leave unlimited.
Exit Status
- 0
The target command exited successfully.
- 1
Usage error (unrecognized subcommand or flag, missing ARAPUCA_WRAPPER sentinel, no -- separator, command not found, or sandbox setup failed). A diagnostic is printed to stderr.
- >1
The target command exited with a non-zero status. The exit code is passed through.
- 125
Sandbox infrastructure error (arapuca run only). Invalid flags, sandbox setup failure, or CONNECT proxy failure.
- 137
The target command was killed by SIGKILL (e.g., parent died and PR_SET_PDEATHSIG fired, cgroup OOM kill, or timeout SIGKILL).
- 143
The target command was killed by SIGTERM (e.g., timeout fired).
Security
Linux
On Linux (kernel 5.13+), arapuca enforces:
- Landlock
Restricts filesystem access to the paths listed in ARAPUCA_READ_PATHS and ARAPUCA_WRITE_PATHS. All other paths are inaccessible. Supports ABI versions 1 through 6.
- Seccomp BPF
Installs a syscall filter with two tiers:
KILL_PROCESS: ptrace, mount, chroot, unshare, setns, clone3, memfd_create, io_uring, bpf, kexec, kernel module loading.
EPERM: symlink, link, socket(AF_INET), socket(AF_INET6), perf_event_open.
socket(AF_UNIX) is allowed – it is needed for IPC with the host (JSON-RPC control socket, LLM proxy).
- PR_SET_NO_NEW_PRIVS
Set before Landlock and seccomp. Prevents privilege escalation via setuid binaries.
- PR_SET_PDEATHSIG
Sends SIGKILL to the subprocess if the parent process dies. Prevents orphaned sandboxed processes.
macOS
On macOS, arapuca enforces only POSIX resource limits. Filesystem and network restrictions are applied externally by sandbox-exec(1) using a Seatbelt profile generated by the arapuca library.
Both Platforms
- POSIX resource limits
RLIMIT_AS, RLIMIT_NPROC, RLIMIT_CPU, and RLIMIT_FSIZE are set as both soft and hard limits, so the subprocess cannot raise them.
- Environment stripping
All ARAPUCA_* variables are removed. The subprocess cannot discover its own sandbox configuration.
Examples
arapuca run
Run a command with default sandboxing:
arapuca run -- /bin/echo hello
Grant read-only access to a project directory:
arapuca run -v /home/user/project:ro -- ls /home/user/project
Sandbox a Claude Code agent with selective HTTPS access:
arapuca run \ -v /home/user/repo \ --allow-host us-east5-aiplatform.googleapis.com:443 \ --allow-host oauth2.googleapis.com:443 \ --env VERTEXAI_PROJECT=my-project \ --timeout 600 --memory 3072 \ -- claude --bare -p --model claude-sonnet-4-6
Interactive PTY session inside the sandbox:
arapuca run -t \ -v /home/user/project:ro \ --seccomp baseline \ -- /bin/bash
Internal wrapper (arapuca --)
Run a Python script with read access to system paths and write access to a workspace directory (requires ARAPUCA_WRAPPER=1):
ARAPUCA_READ_PATHS=/usr:/lib:/lib64:/bin:/etc:/dev \ ARAPUCA_WRITE_PATHS=/tmp/workspace \ arapuca -- python3 agent.py
Run a command with resource limits (2 GB memory, 256 processes, 1 hour CPU, 1 GB max file size):
ARAPUCA_RLIMIT_AS=2147483648 \ ARAPUCA_RLIMIT_NPROC=256 \ ARAPUCA_RLIMIT_CPU=3600 \ ARAPUCA_RLIMIT_FSIZE=1073741824 \ arapuca -- ./run-tests.sh
The internal wrapper requires ARAPUCA_WRAPPER=1 — direct CLI invocations are rejected. Use arapuca run instead.
See Also
landlock(7), seccomp(2), setrlimit(2), sandbox-exec(1), prctl(2), execve(2), unshare(1)
Authors
Sergio Correia <scorreia@redhat.com>