Security Model
This page explains what runok’s sandbox is designed to protect against, the trust boundaries it enforces, and the rationale behind its design decisions.
What the sandbox protects
Section titled “What the sandbox protects”The sandbox restricts two capabilities of executed commands:
- File system writes — Prevents commands from modifying files outside of explicitly allowed directories
- Network access — Prevents commands from making TCP/UDP connections when configured
File system protection
Section titled “File system protection”Without a sandbox, an allowed command like python3 script.py has full write access to the entire file system (within the user’s permissions). A malicious or buggy script could:
- Modify
.git/hooks/to inject code that runs on every commit - Overwrite
.envfiles or credentials - Tamper with the
runok.ymlconfiguration itself - Write to
~/.ssh/or other sensitive locations
With a sandbox, write access is restricted to explicitly listed directories. Common protected paths include .git, .runok, and credential files.
Network protection
Section titled “Network protection”When network.allow is false, the sandbox prevents the command from:
- Exfiltrating data to external servers
- Downloading and executing remote payloads
- Making unintended API calls
Trust model
Section titled “Trust model”Commands are untrusted
Section titled “Commands are untrusted”The sandbox treats every executed command as potentially untrusted. Even commands from allow rules may behave unexpectedly — a dependency could have a supply-chain vulnerability, a script could contain bugs, or an AI agent could construct a command that does more than intended.
The sandbox provides a second layer of defense beyond the rule-based allow/deny system. A command is first checked against rules (is it allowed to run?), then executed inside a sandbox (what can it do?).
Read access is always permitted
Section titled “Read access is always permitted”The sandbox does not restrict read access. This is a deliberate design decision:
- Most development commands need to read source files, configuration, and dependencies
- Restricting reads would break most practical workflows
- The primary risk from AI agent-executed commands is modification (writes) and exfiltration (network), not reading
Unix domain sockets are always permitted
Section titled “Unix domain sockets are always permitted”Unix domain sockets (AF_UNIX) are never blocked, even when network.allow is false. Many development tools (package managers, build tools, language servers) use Unix sockets for local inter-process communication. Blocking them would break basic tool functionality without meaningful security benefit, since Unix sockets cannot be used for network exfiltration.
Deny takes priority over allow
Section titled “Deny takes priority over allow”Within the sandbox itself, deny paths always override writable directories. If you configure:
fs: writable: - '.' deny: - '.git'Then .git is protected even though . (its parent) is writable. This matches how both macOS Seatbelt and Linux bubblewrap work:
- On macOS, Seatbelt’s
(deny file-write*)rules take priority over(allow file-write*)rules - On Linux, bubblewrap applies
--ro-bindafter--bind, so read-only mounts overlay writable mounts
Strictest Wins for compound commands
Section titled “Strictest Wins for compound commands”When multiple sandbox policies apply, runok uses a Strictest Wins strategy. See Sandbox Overview for the merge rules.
This prevents a less-restricted command from weakening the sandbox of a more-restricted command in the same pipeline.
OS-level enforcement
Section titled “OS-level enforcement”The sandbox is enforced by the operating system kernel, not by runok’s own process. A sandboxed command cannot:
- Disable or modify the sandbox policy at runtime
- Escape the restrictions via child processes (children inherit the sandbox)
- Use
exec()to replace itself with an unrestricted process
On macOS, this is provided by the Seatbelt kernel extension. On Linux, it is provided by mount namespaces (bubblewrap), Landlock LSM, and seccomp-bpf filters. See the platform-specific pages for details:
Limitations
Section titled “Limitations”- Read access is not restricted — the sandbox cannot prevent data from being read
- Network granularity is binary — network access is either fully allowed or fully blocked; per-host or per-port filtering is not supported
- macOS Seatbelt is deprecated — Apple has deprecated
sandbox-execbut provides no replacement. It continues to work and is used by Apple’s own tools - Linux glob deny patterns are expanded at startup — on Linux, glob patterns in
denyare expanded against the filesystem before the sandbox starts. Files created after startup that match a glob pattern will not be protected - Landlock kernel version — Landlock requires Linux 5.13 or later. On older kernels, file system restrictions may be partially enforced