Features

Core capabilities of agentsh — traffic control, data protection, runtime security, and operational tools.

Steering#

Most systems can deny an action. agentsh can also steer it to an approved alternative.

When an agent tries the wrong approach (or brute-force workarounds), policy can steer it to the right path—keeping the agent productive and reducing wasted retries. The technical action is called redirect in policy files.

Steer npm to internal registry

network_rules:
  - name: steer-npm
    destinations: ["registry.npmjs.org"]
    decision: redirect
    redirect_to: "npm.internal.corp"
    message: "Steered to internal registry"

Steer curl to an audited wrapper

command_rules:
  - name: redirect-curl
    commands: [curl, wget]
    decision: redirect
    message: "Downloads routed through audited fetch"
    redirect_to:
      command: agentsh-fetch
      args: ["--audit"]

Steer writes outside workspace

file_rules:
  - name: redirect-outside-writes
    paths: ["/home/**", "/tmp/**"]
    operations: [write, create]
    decision: redirect
    redirect_to: "/workspace/.scratch"
    message: "Writes outside workspace redirected to /workspace/.scratch"

Discourage unnecessary deletions

Instead of blocking destructive commands outright, steer the agent away with guidance:

command_rules:
  - name: discourage-rm
    commands: [rm, rmdir]
    args_patterns: ["(-rf?|--recursive)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["Skipped: deletion not needed. Files can remain; they don't affect the build or tests."]
    message: "Cleanup is unnecessary—focus on the task instead of removing files."

The agent sees a "successful" cleanup, receives the guidance message, and moves on without wasting retries or attempting workarounds.

The agent sees a successful operation (not an error), but you control where things actually land.

Prevent bypassing merge workflow

Steer agents away from shortcuts that bypass proper code review and merge workflows:

command_rules:
  - name: no-direct-push-to-main
    commands: [git]
    args_patterns: ["push.*(origin\\s+)?(main|master)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["Push declined. Create a branch and open a pull request instead."]
    message: "Direct pushes to main/master are not allowed. Use feature branches and pull requests."

  - name: no-force-push
    commands: [git]
    args_patterns: ["push.*(--force|-f)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["Force push blocked. Resolve conflicts through proper merge."]
    message: "Force pushing is not allowed—merge changes through the standard workflow."

  - name: no-hard-reset
    commands: [git]
    args_patterns: ["reset.*(--hard)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["Hard reset blocked. Use git stash or git checkout to preserve changes."]
    message: "git reset --hard discards uncommitted work. Use non-destructive alternatives."

  - name: no-git-clean
    commands: [git]
    args_patterns: ["clean.*(-f|--force)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["git clean blocked. Untracked files may be needed—review before deleting."]
    message: "git clean -fd permanently deletes untracked files. Review them manually first."

The agent receives guidance to use the proper workflow instead of attempting workarounds.

DNS & Connect Redirects#

For transparent network-level routing without application changes, agentsh intercepts DNS resolution and TCP connections at the syscall level. This enables scenarios like routing API calls through internal proxies or gateways.

Process-scoped, not system-wide

DNS and connect redirects only affect processes running under agentsh—not the entire system. This makes it safe to run alongside other applications.

DNS Redirects

Intercept DNS resolution and return alternative IP addresses based on hostname patterns.

dns_redirects:
  # Route Anthropic API through internal proxy
  - name: anthropic-to-vertex
    match: ".*\\.anthropic\\.com"  # Regex pattern
    resolve_to: "10.0.0.50"       # IP to return
    visibility: audit_only
    on_failure: fail_closed

Fields:

Connect Redirects

Intercept TCP connections and route them to different destinations. Supports TLS/SNI rewriting for MITM proxy scenarios.

connect_redirects:
  # Route API calls through corporate proxy
  - name: api-to-proxy
    match: "api\\.anthropic\\.com:443"  # Regex for host:port
    redirect_to: "vertex-proxy.internal:443"
    tls:
      mode: passthrough  # or rewrite_sni
    visibility: warn
    message: "Routed through Vertex AI proxy"
    on_failure: fail_closed

Fields:

SNI Rewriting

When routing through a MITM proxy that presents its own certificate, use SNI rewriting:

connect_redirects:
  - name: tls-mitm
    match: ".*:443"
    redirect_to: "proxy.internal:8443"
    tls:
      mode: rewrite_sni
      sni: "proxy.internal"  # SNI sent to proxy

Combined Example

Route Anthropic API calls through a Vertex AI proxy with DNS and connect redirects working together:

# First, resolve anthropic.com to proxy IP
dns_redirects:
  - name: anthropic-dns
    match: ".*\\.anthropic\\.com"
    resolve_to: "10.0.0.50"
    visibility: audit_only

# Then, redirect the connection to proxy port
connect_redirects:
  - name: anthropic-connect
    match: "api\\.anthropic\\.com:443"
    redirect_to: "vertex-proxy.internal:443"
    tls:
      mode: passthrough
    visibility: warn
    message: "API call routed through Vertex AI"

Visibility & Failure Options

VisibilityBehavior
silentNo event, no stderr output
audit_onlyEvent logged to audit system only (default)
warnEvent logged + message shown to stderr
On FailureBehavior
fail_closedReturn error to application (default)
fail_openRetry connection to original destination
retry_originalSame as fail_open

Platform Support

DNS and connect redirects are available on all platforms:

Network ACLs#

Process-level network access control lists provide fine-grained control over which processes can make which network connections. ACL rules are evaluated per-process based on the executable name.

# Show active network ACL rules
agentsh network-acl list --json

# Add a rule allowing curl to reach an API
agentsh network-acl add curl api.example.com --port 443 --decision allow

# Test what decision would be made for a connection
agentsh network-acl test node registry.npmjs.org --port 443

# Remove a rule by index
agentsh network-acl remove 3 --process curl

# Watch live connection attempts
agentsh network-acl watch --json

# Learn network patterns for policy generation
agentsh network-acl learn --process node --duration 1h --output learned-rules.yaml

The network-acl command is also available as nacl or pnacl for convenience.

LLM Proxy & DLP#

agentsh includes an embedded proxy that intercepts all LLM API requests from agents.

proxy:
  mode: embedded
  providers:
    anthropic: https://api.anthropic.com
    openai: https://api.openai.com

dlp:
  mode: redact
  patterns:
    email: true
    api_keys: true
  custom_patterns:
    - name: customer_id
      display: identifier
      regex: "CUST-[0-9]{8}"

Workspace Checkpoints#

Create snapshots of workspace state for recovery from destructive operations:

# Create a checkpoint before risky operations
agentsh checkpoint create --session $SID --workspace /workspace --reason "before cleanup"

# List checkpoints for a session
agentsh checkpoint list --session $SID

# Show what changed since a checkpoint
agentsh checkpoint show <cp-id> --session $SID --workspace /workspace --diff

# Preview what rollback would restore (dry-run)
agentsh checkpoint rollback <cp-id> --session $SID --workspace /workspace --dry-run

# Restore workspace to checkpoint state
agentsh checkpoint rollback <cp-id> --session $SID --workspace /workspace

# Clean up old checkpoints
agentsh checkpoint purge --session $SID --older-than 24h --keep 5

All commands accept --storage-dir to override the default checkpoint storage location. The list and show commands support --json for machine-readable output.

Auto-checkpoint

When enabled, agentsh automatically creates checkpoints before risky commands (rm, mv, git reset, git checkout, git clean, git stash). Configure in sessions.checkpoints.auto_checkpoint. Snapshots use copy-on-write with SHA-256 hash verification on restore. Individual files over 100 MB are skipped.

Trash & Soft Delete#

When a policy uses the soft_delete decision, files are diverted to a trash directory instead of being permanently deleted. Manage diverted files with the trash command:

# List diverted items
agentsh trash list --session $SID

# Restore a diverted item by token
agentsh trash restore <token>

# Restore to a different location
agentsh trash restore <token> --dest /workspace/recovered.txt --force

# Purge old trash entries
agentsh trash purge --ttl 7d --quota 5GB

Backup & Restore#

Create and restore backups of agentsh data including configuration, policies, sessions, and audit logs:

# Create a backup
agentsh backup -o agentsh-backup.tar.gz --verify

# Restore from backup (dry-run first)
agentsh restore -i agentsh-backup.tar.gz --dry-run

# Restore and verify
agentsh restore -i agentsh-backup.tar.gz --verify

Process Taint Tracking#

agentsh tracks process ancestry to determine which processes are “tainted” (spawned by or descended from an AI agent). Use the taint command to inspect and debug taint propagation:

# List all tainted processes
agentsh taint list --json

# Show taint details for a specific process
agentsh taint show <pid>

# Show full ancestry trace
agentsh taint trace <pid>

# Stream taint events in real-time
agentsh taint watch --agent-only

# Simulate taint evaluation for testing
agentsh taint simulate --ancestry bash,node,claude --command curl --args "https://example.com"

Authentication#

agentsh supports multiple authentication methods:

Type Use Case
api_keySimple deployments with static keys
oidcEnterprise SSO (Okta, Azure AD, etc.)
hybridBoth methods accepted

Approval modes for human-in-the-loop verification:

OIDC Configuration

When using OIDC, the server discovers the provider's endpoints at startup. The discovery_timeout field controls how long to wait for the OIDC discovery request (default: 5s). Increase this if your identity provider is slow to respond, or decrease it to fail fast in environments where the provider may be unreachable.

auth:
  type: oidc
  oidc:
    issuer: https://login.example.com
    audience: agentsh
    discovery_timeout: 5s   # default; accepts Go duration format

WebAuthn Credentials

Manage hardware security key credentials for the webauthn approval mode:

# Register a new security key
agentsh auth webauthn register --name "YubiKey 5"

# List registered credentials
agentsh auth webauthn list

# Delete a credential
agentsh auth webauthn delete --credential-id <base64-id>

Threat Intelligence Feeds#

agentsh can block network connections to known-malicious domains using external threat intelligence feeds and local blocklists. When a DNS query or connection attempt matches a threat feed entry, the configured action (deny or audit) is applied and recorded with the feed name and matched domain in the event metadata.

How it works

Feeds are downloaded at startup and periodically refreshed in the background. The on-disk cache uses ETag-based HTTP caching to minimize bandwidth. Parent-domain matching is supported—blocking evil.com also blocks sub.evil.com. An allowlist lets you override specific domains.

Configuration

threat_feeds:
  enabled: true
  action: deny              # deny or audit
  feeds:
    - name: urlhaus
      url: https://urlhaus.abuse.ch/downloads/hostfile/
      format: hostfile        # hostfile or domain-list
    - name: custom-blocklist
      url: https://internal.corp/threat-domains.txt
      format: domain-list
  local_lists:
    - /etc/agentsh/local-blocklist.txt
  allowlist:
    - safe.example.com
  sync_interval: 6h         # background refresh interval (default: 6h)
  cache_dir: ""             # on-disk cache directory
FieldDefaultDescription
enabledfalseEnable threat feed checking
actiondenydeny blocks the connection; audit logs but permits
feeds[].nameUnique feed identifier (alphanumeric, dots, hyphens, underscores)
feeds[].urlFeed URL (http:// or https://)
feeds[].formathostfile (127.0.0.1 domain) or domain-list (one per line)
local_lists[]Local file paths containing domains to block (one per line)
allowlist[]Domains that override threat feed matches (always permitted)
sync_interval6hHow often to refresh remote feeds

Event Metadata

When a DNS query or network connection matches a threat feed, the event's policy info includes threat-specific fields:

{
  "type": "dns_query",
  "domain": "malware.evil.com",
  "policy": {
    "decision": "deny",
    "threat_feed": "urlhaus",
    "threat_match": "evil.com",
    "threat_action": "deny"
  }
}

Package Install Security#

agentsh intercepts package manager commands (npm install, pip install, pnpm add, yarn add, uv pip install, poetry add) and evaluates their dependency trees against multiple security providers before the install proceeds. A policy-based rule engine determines whether to allow, warn, require approval, or block each package.

Fail-closed by default

If no policy rule matches a finding, the default verdict is block. This ensures that unrecognized threats are not silently permitted. Configure explicit allow rules for packages you trust.

Providers#

Providers run in parallel and return findings that the policy engine evaluates. Each provider can be independently enabled, configured with timeouts, and assigned a failure action.

ProviderFinding TypesAPI Key RequiredDescription
osvvulnerabilityNoOpenSSF Vulnerability Database with CVSS v3 scoring
depsdevlicense, reputationNodeps.dev for license metadata and OpenSSF Scorecard scores
locallicenseNoOffline license metadata scanning (zero network calls)
socketmalware, reputationYesSocket.dev for supply-chain malware and typosquatting detection
snykvulnerability, licenseYesSnyk vulnerability and license scanning
execanyCustom external checker via stdin/stdout JSON protocol

Policy Rules#

Package rules use first-match-wins evaluation. Each rule matches on package name, finding type, severity, ecosystem, reason codes, or SPDX license identifiers.

package_rules:
  # Allow known internal packages
  - match:
      name_patterns: ["@acme/*"]
    action: allow

  # Block critical vulnerabilities
  - match:
      finding_type: vulnerability
      severity: critical
    action: block

  # Require approval for malware findings
  - match:
      finding_type: malware
    action: approve
    reason: "Malware detected in dependency"

  # Block copyleft licenses
  - match:
      finding_type: license
      license_spdx:
        deny: [GPL-3.0-only, AGPL-3.0-only]
    action: block

  # Warn on low-reputation packages
  - match:
      finding_type: reputation
      severity: high
    action: warn
Match FieldDescription
packagesExact package name list
name_patternsGlob patterns for package names (e.g. @acme/*)
finding_typevulnerability, license, provenance, reputation, malware
severitycritical, high, medium, low, info
ecosystemnpm or pypi
reasonsMatch specific provider reason codes
license_spdxSPDX allow/deny lists for license identifiers

Verdict actions: allow, warn (log and permit), approve (require human approval), block (deny the install).

Configuration#

package_checks:
  enabled: true
  scope: new_packages_only   # new_packages_only or all_installs
  cache:
    ttl:
      vulnerability: 1h
      license: 24h
      provenance: 24h
      reputation: 24h
      malware: 1h
  providers:
    osv:
      enabled: true
      timeout: 10s
      on_failure: warn        # warn, deny, allow, or approve
    depsdev:
      enabled: true
      timeout: 10s
      on_failure: warn
    local:
      enabled: true
      on_failure: warn
    socket:
      enabled: false
      api_key_env: SOCKET_API_KEY
      timeout: 10s
      on_failure: warn
    snyk:
      enabled: false
      api_key_env: SNYK_TOKEN
      timeout: 10s
      on_failure: warn
  registries:
    npmjs.org:
      trust: check_full       # check_full, check_local_only, or trusted
    internal.corp:
      trust: trusted
      scopes: ["@acme"]
FieldDefaultDescription
enabledfalseEnable package install security checks
scopenew_packages_onlynew_packages_only skips already-installed packages; all_installs checks every install
cache.ttl.*variesPer-finding-type cache lifetimes for provider results
providers.*.enabledvariesEnable/disable each provider independently
providers.*.timeout10sPer-provider request timeout
providers.*.on_failurewarnAction when provider fails: warn, deny, allow, approve
providers.*.api_key_envEnvironment variable containing the API key
registries.*.trustcheck_fulltrusted skips checks; check_local_only uses only the local provider

Policy Generation#

The profile-then-lock workflow lets you generate restrictive policies from observed session behavior. Run your workload once in permissive mode, then lock down future runs to only what was observed.

The Profile-Then-Lock Workflow

1. Profile

Run your build/test/agent task with a permissive policy. agentsh records every operation.

2. Generate

Use the recorded session to generate a policy that allows only what was observed.

3. Lock

Apply the generated policy to future runs. Any deviation is blocked or flagged.

4. Iterate

If legitimate behavior is blocked, re-profile and regenerate.

Generating a Policy

# Step 1: Run your workload (profile phase)
SID=$(agentsh session create --workspace . --policy permissive | jq -r .id)
agentsh exec "$SID" -- npm install
agentsh exec "$SID" -- npm run build
agentsh exec "$SID" -- npm test

# Step 2: Generate policy from observed behavior
agentsh policy generate "$SID" --output=ci-locked.yaml --name=ci-build

# Step 3: Use the locked policy for future runs
SID=$(agentsh session create --workspace . --policy ci-build | jq -r .id)
agentsh exec "$SID" -- npm install  # Only allowed packages from profile

Generation Options

FlagDescription
--outputWrite policy to file (default: stdout)
--namePolicy name (default: generated-<session-id>)
--thresholdMinimum occurrences before including a path pattern (default: 1)
--include-blockedInclude blocked operations as commented-out rules for review

What Gets Generated

The generated policy:

Example Generated Policy

# Generated from session sess-abc123 on 2025-01-15
# Profile: npm install && npm run build && npm test

version: 1
name: ci-build

file_rules:
  - name: workspace-src
    paths: ["/workspace/src/**"]
    operations: [read, open, stat, list]
    decision: allow

  - name: workspace-dist
    paths: ["/workspace/dist/**"]
    operations: [read, write, create, mkdir]
    decision: allow

  - name: node-modules
    paths: ["/workspace/node_modules/**"]
    operations: ["*"]
    decision: allow

network_rules:
  - name: npm-registry
    domains: ["registry.npmjs.org", "*.npmjs.org"]
    ports: [443]
    decision: allow

  - name: default-deny
    domains: ["*"]
    decision: deny

Use Cases

Daemon Management#

Manage the agentsh background daemon for continuous monitoring:

# Install startup integration (systemd/launchd)
agentsh daemon install

# Check daemon status
agentsh daemon status --json

# Restart with fresh session
agentsh daemon restart

# Remove startup integration
agentsh daemon uninstall

Agent Wrapping#

The wrap command provides a single-command shortcut to run an AI agent under agentsh with exec interception. It creates a session, sets up the environment, runs the agent, and generates a report on exit:

# Wrap an AI agent with default policy
agentsh wrap -- claude --dangerously-skip-permissions -p "implement the feature"

# Use a specific policy and workspace root
agentsh wrap --policy ci-locked --root /workspace -- npm test

# Reuse an existing session
agentsh wrap --session $SID -- codex --full-auto "fix the failing tests"

# Skip the exit report
agentsh wrap --report=false -- node script.js