Policy Reference

Deterministic policy schema for controlling file, network, command, signal, registry, and package access at the execution layer.

Policy Model#

Decisions

DecisionDescription
allowPermit the operation
denyBlock the operation
approveRequire human approval
redirectSwap to a different target
auditAllow + log (explicit logging)
soft_deleteQuarantine with restore option

Scopes

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:

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.

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

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

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.

FieldTypeDescription
match.packagesstring[]Exact package names
match.name_patternsstring[]Glob/regex patterns for package names
match.finding_typestringType of finding: vulnerability, license, malware, typosquat, provenance, reputation
match.severitystringMinimum severity: critical, high, medium, low, info
match.reasonsstring[]Specific reason codes to match
match.license_spdx.allowstring[]Allowlisted SPDX license identifiers
match.license_spdx.denystring[]Denylisted SPDX license identifiers
match.ecosystemstringPackage ecosystem: npm, pypi, cargo, etc.
actionstringallow, warn, approve, or block
reasonstringHuman-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.

FieldTypeDescription
namestringRule name (for logging)
matchstringRegex pattern to match against the queried domain
resolve_tostringIP address to return instead of the real resolution
visibilitystringsilent, audit_only, or warn
on_failurestringfail_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.

FieldTypeDescription
namestringRule name (for logging)
matchstringRegex pattern to match against host:port
redirect_tostringNew host:port destination
tls.modestringpassthrough or rewrite_sni
tls.snistringNew SNI value (required when mode is rewrite_sni)
visibilitystringsilent, audit_only, or warn
on_failurestringfail_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.

ci-strict.yaml

Safe for CI runners.

agent-sandbox.yaml

"Agent runs unknown code" mode.

agent-default.yaml

Comprehensive policy for AI coding agents (Claude Code, Codex CLI, etc.). Designed for use with agentsh wrap.