Skip to content

Sandbox Overview

runok can execute allowed commands inside a sandbox that restricts file system writes and network access. Sandboxing is applied at the OS level using platform-native mechanisms (macOS Seatbelt or Linux Landlock + seccomp), so sandboxed processes cannot bypass the restrictions.

See Security Model for trust boundaries and design rationale.

Sandbox policies are defined as named presets under definitions.sandbox in your runok.yml:

definitions:
sandbox:
restricted:
fs:
read:
deny:
- '~/.ssh'
- '~/.gnupg'
write:
allow:
- '.'
deny:
- '.git'
- '.runok'
network:
allow: false

Each preset contains two sections:

The fs section controls both read and write access through read and write sub-sections:

FieldTypeDescription
allowstring[]Directories the sandboxed process can write to. Paths support ~ expansion.
denystring[]Paths the sandboxed process cannot write to, even within writable directories. Supports glob patterns (*, **, ?, [...], {a,b}) and <path:name> references.

The deny list always takes priority over allow. For example, if allow includes "." (the current directory) and deny includes ".git", the process can write anywhere in the project except the .git directory.

FieldTypeDescription
denystring[]Paths the sandboxed process cannot read. Supports glob patterns and <path:name> references.

fs.read.deny blocks read access to the listed paths. Since write access requires read access, these paths become completely inaccessible to the sandboxed process.

FieldTypeDescription
allowboolWhether the sandboxed process can make network connections. Defaults to true if omitted.

When network.allow is false, TCP/UDP sockets are blocked. Unix domain sockets (used for local IPC) are always permitted regardless of this setting.

You can define reusable path lists under definitions.paths and reference them in sandbox deny lists:

definitions:
paths:
sensitive:
- '.env*'
- 'credentials.*'
secrets:
- '~/.ssh/**'
- '~/.gnupg/**'
sandbox:
restricted:
fs:
read:
deny:
- '<path:secrets>'
write:
deny:
- '<path:sensitive>'

The <path:secrets> reference expands to all paths listed under definitions.paths.secrets. References can be used in both read.deny and write.deny. See Path References and definitions.paths for details.

Attach a sandbox preset to any allow or ask rule with the sandbox field:

rules:
- allow: 'python3 *'
sandbox: restricted
- allow: 'npm test'
sandbox: restricted

Use defaults.sandbox to apply a sandbox preset to all rules that do not specify one explicitly:

defaults:
sandbox: restricted
rules:
- allow: 'git status' # uses "restricted" sandbox
- allow: 'cargo build *'
sandbox: build-env # overrides with "build-env"

When a compound command like sh -c "cmd1 && cmd2" is evaluated, each sub-command may match a different sandbox preset. runok merges all matched policies using the Strictest Wins rule:

FieldMerge strategyEffect
write.allowIntersectionOnly directories allowed by all policies remain writable
write.denyUnionWrite-denied paths from any policy are protected
read.denyUnionRead-denied paths from any policy are protected
network.allowANDNetwork is blocked if any policy denies it

This ensures that a less-restricted command in a pipeline cannot weaken the restrictions of a more-restricted command.

If the intersection of writable roots becomes empty (a contradiction), runok escalates the action to ask so the user can decide whether to proceed.

definitions:
paths:
sensitive:
- '.env*'
- 'credentials.*'
secrets:
- '~/.ssh/**'
- '~/.gnupg/**'
sandbox:
workspace-write:
fs:
read:
deny:
- '<path:secrets>'
write:
allow:
- '.'
deny:
- '.git'
- '.runok'
- '<path:sensitive>'
network:
allow: true
no-network:
fs:
write:
allow:
- '.'
deny:
- '.git'
- '.runok'
network:
allow: false
defaults:
sandbox: workspace-write
rules:
- allow: 'git *'
- allow: 'python3 *'
sandbox: no-network
- allow: 'npm test'
sandbox: no-network
- deny: 'rm -rf /'