Policy Reference
Deterministic policy schema for controlling file, network, command, signal, registry, and package access at the execution layer.
Policy Model#
Decisions
| Decision | Description |
|---|---|
allow | Permit the operation |
deny | Block the operation |
approve | Require human approval |
redirect | Swap to a different target |
audit | Allow + log (explicit logging) |
soft_delete | Quarantine with restore option |
Scopes
- File operations
- Commands
- Environment variables
- Network (DNS/connect)
- PTY/session settings
- Signals (Linux only for blocking)
- Registry (Windows only)
Evaluation
First matching rule wins. Rules live in a named policy; sessions choose a policy at creation time.
File Rules#
Control file system operations by path and operation type.
file_rules:
# Allow reading workspace
- name: allow-workspace-read
paths:
- "/workspace"
- "/workspace/**"
operations: [read, open, stat, list, readlink]
decision: allow
# Require approval for deletes
- name: approve-workspace-delete
paths: ["/workspace/**"]
operations: [delete, rmdir]
decision: approve
message: "Agent wants to delete: {{.Path}}"
timeout: 5m
# Block sensitive paths
- name: deny-ssh-keys
paths: ["/home/**/.ssh/**", "/root/.ssh/**"]
operations: ["*"]
decision: deny
Operations: read, open, stat, list, readlink, write, create, mkdir, chmod, rename, delete, rmdir, * (all)
Network Rules#
Control network connections by domain, CIDR, or port.
network_rules:
# Allow package registries
- name: allow-npm
domains: ["registry.npmjs.org", "*.npmjs.org"]
ports: [443, 80]
decision: allow
# Block private networks
- name: block-private
cidrs:
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
decision: deny
# Block cloud metadata
- name: block-metadata
cidrs: ["169.254.169.254/32"]
decision: deny
# Approve unknown HTTPS
- name: approve-unknown
ports: [443]
decision: approve
message: "Connect to {{.RemoteAddr}}:{{.RemotePort}}?"
Command Rules#
Pre-execution checks for commands. With execve interception enabled (Linux full mode), rules also apply to nested commands spawned by scripts.
command_rules:
# Safe commands
- name: allow-safe
commands: [ls, cat, grep, find, pwd, echo, git, node, python]
decision: allow
# Approve package installs
- name: approve-install
commands: [npm, pip, cargo]
args_patterns: ["install*", "add*"]
decision: approve
message: "Install packages: {{.Args}}"
# Block dangerous patterns
- name: block-rm-rf
commands: [rm]
args_patterns: ["*-rf*", "*-fr*"]
decision: deny
# Block system commands
- name: block-system
commands: [shutdown, reboot, systemctl, mount, dd, kill]
decision: deny
Signal Rules#
Control which processes can send signals to which targets. Full blocking only on Linux; macOS and Windows provide audit only.
signal_rules:
# Allow signals to self and children
- name: allow-self
signals: ["@all"]
target:
type: self
decision: allow
# Redirect SIGKILL to graceful SIGTERM
- name: graceful-kill
signals: ["SIGKILL"]
target:
type: children
decision: redirect
redirect_to: SIGTERM
# Block fatal signals to external processes
- name: deny-external-fatal
signals: ["@fatal"]
target:
type: external
decision: deny
# Silently absorb job control signals from external sources
- name: absorb-external-job
signals: ["@job"]
target:
type: external
decision: absorb
Signal groups:
@all- All signals (1-31)@fatal- SIGKILL, SIGTERM, SIGQUIT, SIGABRT@job- SIGSTOP, SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU@reload- SIGHUP, SIGUSR1, SIGUSR2
Target types: self, children, descendants, session, external, system
Signal decisions: allow, deny, audit, approve, redirect (to another signal), absorb (discard silently)
Registry Rules (Windows)#
Control Windows registry access. Requires mini filter driver.
registry_rules:
# Block persistence locations
- name: block-run-keys
paths:
- 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run*'
- 'HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run*'
operations: [write, create, delete]
decision: deny
# Block security settings
- name: block-defender
paths: ['HKLM\SOFTWARE\Policies\Microsoft\Windows Defender*']
operations: [write, create, delete]
decision: deny
# Allow reads everywhere
- name: allow-read
paths: ["*"]
operations: [read]
decision: allow
Resource Limits#
Constrain resource usage per session. Full enforcement on Linux only.
resource_limits:
# Memory
max_memory_mb: 2048
memory_swap_max_mb: 0
# CPU
cpu_quota_percent: 80
# Disk I/O
disk_read_bps_max: 104857600 # 100 MB/s
disk_write_bps_max: 52428800 # 50 MB/s
# Network
net_bandwidth_mbps: 100
# Process limits
pids_max: 100
# Time limits
command_timeout: 5m
session_timeout: 4h
idle_timeout: 30m
MCP Rules#
The mcp_rules section in a policy file defines MCP security enforcement. This is the policy-file equivalent of the sandbox.mcp runtime configuration.
mcp_rules:
enforce_policy: true
# Server-level access control
server_policy: "allowlist"
allowed_servers:
- id: "trusted_*"
denied_servers:
- id: "untrusted_*"
# Tool-level access control
tool_policy: "allowlist"
allowed_tools:
- server: "database"
tool: "query_users"
content_hash: "sha256:abc123..."
- server: "notes"
tool: "read_*"
denied_tools:
- server: "*"
tool: "exec_*"
# Version pinning
version_pinning:
enabled: true
on_change: "block"
auto_trust_first: true
# Cross-server attack detection
cross_server:
enabled: true
read_then_send:
enabled: true
burst:
enabled: true
See the MCP Policy Configuration section for detailed descriptions of each option.
Environment Policy#
Control which environment variables processes can access.
- Defaults: With no
env_allow, agentsh builds a minimal env (PATH/LANG/TERM/HOME) and strips built-in secret keys - Overrides: Per-command
env_allow/env_denyplus limits - Block iteration:
env_block_iteration: truehides env enumeration
Global policy (applies to all commands)
env_policy:
# Allowlist - only these vars are visible (supports wildcards)
allow:
- "PATH"
- "HOME"
- "LANG"
- "TERM"
- "NODE_*" # All NODE_ prefixed vars
- "npm_*"
# Denylist - these are always stripped (even if in allow)
deny:
- "AWS_*"
- "GITHUB_TOKEN"
- "*_SECRET*"
- "*_KEY"
- "*_PASSWORD"
# Size limits
max_bytes: 1000000 # Max total env size
max_keys: 100 # Max number of variables
# Block enumeration (env, printenv, /proc/*/environ)
block_iteration: true
Per-command overrides
Override the global policy for specific commands:
command_rules:
# npm needs registry tokens
- name: npm-with-tokens
commands: [npm]
decision: allow
env_allow:
- "NPM_TOKEN"
- "NODE_AUTH_TOKEN"
env_deny:
- "AWS_*" # Still deny cloud creds
# Build tools get more env access
- name: build-tools
commands: [make, cargo, go]
decision: allow
env_allow:
- "CC"
- "CXX"
- "GOPATH"
- "CARGO_*"
env_max_bytes: 500000
env_max_keys: 50
# Prevent scripts from discovering env vars
- name: untrusted-scripts
commands: [python, node, ruby]
args_patterns: [".*\\.sh$", ".*eval.*"]
decision: allow
env_block_iteration: true
Environment Injection#
Inject operator-trusted environment variables into every command execution, regardless of the parent environment. Injected variables bypass env_policy filtering since they are configured by the operator.
Use cases
- Shell builtin hardening: Set
BASH_ENVto disable bash builtins that bypass seccomp policy (likekill,ulimit) - Runtime injection: Add variables for environments that strip Docker ENV (e.g., Blaxel)
- Operator defaults: Configure variables that should always be present
Global configuration
Set in your config.yml to apply to all executions:
sandbox:
env_inject:
BASH_ENV: "/usr/lib/agentsh/bash_startup.sh"
# Add custom variables as needed
MY_CUSTOM_VAR: "value"
Policy-level configuration
Override or extend global settings in a policy file:
version: 1
name: my-policy
env_inject:
BASH_ENV: "/etc/mycompany/bash_startup.sh"
EXTRA_VAR: "policy-specific"
# ... rest of policy
Merge behavior
- Start with global
sandbox.env_inject - Layer policy
env_injecton top - Policy wins on key conflicts
- Result bypasses
env_policyfiltering (operator-trusted)
Bundled bash startup script
agentsh includes a script at /usr/lib/agentsh/bash_startup.sh that disables bash builtins which could bypass seccomp policy enforcement:
#!/bin/bash
# Disable builtins that bypass seccomp policy enforcement
enable -n kill # Signal sending
enable -n enable # Prevent re-enabling
enable -n ulimit # Resource limits
enable -n umask # File permission mask
enable -n builtin # Force builtin bypass
enable -n command # Function/alias bypass
This script is included in Linux packages (deb, rpm, arch, tarballs). Set BASH_ENV to this path to automatically disable these builtins in bash sessions.
Package Rules#
Package rules control what happens when an agent installs packages. Each rule has a match object and an action. Rules are evaluated top-to-bottom; the first match wins.
| Field | Type | Description |
|---|---|---|
match.packages | string[] | Exact package names |
match.name_patterns | string[] | Glob/regex patterns for package names |
match.finding_type | string | Type of finding: vulnerability, license, malware, typosquat, provenance, reputation |
match.severity | string | Minimum severity: critical, high, medium, low, info |
match.reasons | string[] | Specific reason codes to match |
match.license_spdx.allow | string[] | Allowlisted SPDX license identifiers |
match.license_spdx.deny | string[] | Denylisted SPDX license identifiers |
match.ecosystem | string | Package ecosystem: npm, pypi, cargo, etc. |
action | string | allow, warn, approve, or block |
reason | string | Human-readable reason for the rule |
package_rules:
# Block critical vulnerabilities
- match:
finding_type: vulnerability
severity: critical
action: block
reason: "Critical vulnerability detected"
# Block malware and typosquats
- match:
finding_type: malware
action: block
- match:
finding_type: typosquat
action: block
# Block copyleft licenses
- match:
finding_type: license
license_spdx:
deny: [GPL-2.0-only, GPL-3.0-only, AGPL-3.0-only]
action: block
# Warn on medium vulnerabilities
- match:
finding_type: vulnerability
severity: medium
action: warn
# Allow a specific trusted package regardless of findings
- match:
packages: [lodash]
action: allow
DNS Redirects#
DNS redirect rules steer domain resolution at the ptrace level. When a traced process resolves a domain matching a rule, the DNS response is rewritten to the specified IP address. Available in ptrace mode only.
| Field | Type | Description |
|---|---|---|
name | string | Rule name (for logging) |
match | string | Regex pattern to match against the queried domain |
resolve_to | string | IP address to return instead of the real resolution |
visibility | string | silent, audit_only, or warn |
on_failure | string | fail_closed, fail_open, or retry_original |
dns_redirects:
# Redirect npm registry to internal mirror
- name: npm-mirror
match: "^registry\\.npmjs\\.org$"
resolve_to: "10.0.1.50"
visibility: audit_only
on_failure: retry_original
# Redirect all PyPI traffic
- name: pypi-mirror
match: "^(files|upload)\\.pythonhosted\\.org$"
resolve_to: "10.0.1.51"
visibility: silent
on_failure: fail_closed
Connect Redirects#
Connect redirect rules steer outbound TCP connections at the ptrace level. When a traced process connects to a host:port matching a rule, the connection is redirected to the specified destination. Supports optional TLS SNI rewriting. Available in ptrace mode only.
| Field | Type | Description |
|---|---|---|
name | string | Rule name (for logging) |
match | string | Regex pattern to match against host:port |
redirect_to | string | New host:port destination |
tls.mode | string | passthrough or rewrite_sni |
tls.sni | string | New SNI value (required when mode is rewrite_sni) |
visibility | string | silent, audit_only, or warn |
on_failure | string | fail_closed, fail_open, or retry_original |
connect_redirects:
# Route API traffic through internal proxy
- name: api-proxy
match: "^api\\.openai\\.com:443$"
redirect_to: "proxy.internal:8443"
tls:
mode: rewrite_sni
sni: "api.openai.com"
visibility: audit_only
on_failure: fail_closed
Transparent Commands Override#
Control which commands are transparently unwrapped by the execve interceptor. When a transparent command (like sudo, env, or nice) is detected, agentsh unwraps it and evaluates the payload command against policy instead. You can add custom wrappers or remove built-in ones.
transparent_commands:
# Add custom task runners to the transparent list
add:
- myrunner
- taskrunner
- doas
# Remove commands from the built-in defaults
remove:
- sudo # Evaluate sudo itself, don't unwrap
Built-in transparent commands include: sudo, env, nice, nohup, timeout, strace, ltrace, time, xargs.
Starter Policy Packs#
Pre-built policies for common scenarios:
dev-safe.yaml
Safe for local development.
- Allow workspace read/write
- Approve deletes in workspace
- Deny
~/.ssh/**,/root/.ssh/** - Restrict network to allowlisted domains
ci-strict.yaml
Safe for CI runners.
- Deny anything outside workspace
- Deny outbound network except artifact registries
- Deny interactive shells
- Audit everything
agent-sandbox.yaml
"Agent runs unknown code" mode.
- Default deny + explicit allowlist
- Approve any credential/path access
- Redirect network tools to internal proxies
- Soft-delete destructive operations
agent-default.yaml
Comprehensive policy for AI coding agents (Claude Code, Codex CLI, etc.). Designed for use with agentsh wrap.
- Git guardrails — Redirects force push, hard reset, git clean, and direct push to main/master with helpful guidance messages
- Destructive command protection — Blocks catastrophic
rm -rf /patterns - System admin denial — Denies
shutdown,reboot,systemctl,mount,dd - Dev toolchain allowlist — Allows git, make, npm, pip, gcc, node, python, and more
- Credential protection — Denies access to
.ssh/,.aws/,.kube/,.gnupg/ - Environment filtering — Blocks iteration and denies
*_SECRET*,*_API_KEY*,*_TOKENpatterns - Network — Default deny with allowlists for LLM APIs (Anthropic, OpenAI), package registries, GitHub, and localhost
- Resource limits — 8 GB memory, 500 PIDs, 15 min command timeout, 12 h session timeout
- Package rules — Blocks critical vulnerabilities and known malware