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.
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:
match: Regex pattern for hostname (full string match)resolve_to: IP address to return (IPv4 or IPv6)visibility:silent,audit_only(default), orwarnon_failure:fail_closed(default),fail_open, orretry_original
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:
match: Regex pattern forhost:port(or just host for any port)redirect_to: Destinationhost:porttls.mode:passthrough(keep original SNI) orrewrite_snitls.sni: Required if mode isrewrite_sni—the SNI to use in ClientHellovisibility,message,on_failure: Same as DNS redirects
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
| Visibility | Behavior |
|---|---|
silent | No event, no stderr output |
audit_only | Event logged to audit system only (default) |
warn | Event logged + message shown to stderr |
| On Failure | Behavior |
|---|---|
fail_closed | Return error to application (default) |
fail_open | Retry connection to original destination |
retry_original | Same as fail_open |
Platform Support
DNS and connect redirects are available on all platforms:
- Linux: eBPF-based interception of
getaddrinfo()andconnect()syscalls - macOS: Endpoint Security Framework integration
- Windows: WinDivert packet filtering with policy-aware NAT
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.
- Automatic routing: Sets
ANTHROPIC_BASE_URLandOPENAI_BASE_URLso SDKs route through the proxy - Custom providers: Route to LiteLLM, Azure OpenAI, vLLM, or corporate gateways
- DLP redaction: PII (emails, phone numbers, API keys) is redacted before reaching providers
- Usage tracking: Token counts extracted and logged for cost attribution
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.
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_key | Simple deployments with static keys |
oidc | Enterprise SSO (Okta, Azure AD, etc.) |
hybrid | Both methods accepted |
Approval modes for human-in-the-loop verification:
local_tty- Terminal prompt (default)totp- Authenticator app codeswebauthn- Hardware security keys (YubiKey)api- Remote approval via REST
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.
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
| Field | Default | Description |
|---|---|---|
enabled | false | Enable threat feed checking |
action | deny | deny blocks the connection; audit logs but permits |
feeds[].name | — | Unique feed identifier (alphanumeric, dots, hyphens, underscores) |
feeds[].url | — | Feed URL (http:// or https://) |
feeds[].format | — | hostfile (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_interval | 6h | How 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.
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.
| Provider | Finding Types | API Key Required | Description |
|---|---|---|---|
osv | vulnerability | No | OpenSSF Vulnerability Database with CVSS v3 scoring |
depsdev | license, reputation | No | deps.dev for license metadata and OpenSSF Scorecard scores |
local | license | No | Offline license metadata scanning (zero network calls) |
socket | malware, reputation | Yes | Socket.dev for supply-chain malware and typosquatting detection |
snyk | vulnerability, license | Yes | Snyk vulnerability and license scanning |
exec | any | — | Custom 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 Field | Description |
|---|---|
packages | Exact package name list |
name_patterns | Glob patterns for package names (e.g. @acme/*) |
finding_type | vulnerability, license, provenance, reputation, malware |
severity | critical, high, medium, low, info |
ecosystem | npm or pypi |
reasons | Match specific provider reason codes |
license_spdx | SPDX 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"]
| Field | Default | Description |
|---|---|---|
enabled | false | Enable package install security checks |
scope | new_packages_only | new_packages_only skips already-installed packages; all_installs checks every install |
cache.ttl.* | varies | Per-finding-type cache lifetimes for provider results |
providers.*.enabled | varies | Enable/disable each provider independently |
providers.*.timeout | 10s | Per-provider request timeout |
providers.*.on_failure | warn | Action when provider fails: warn, deny, allow, approve |
providers.*.api_key_env | — | Environment variable containing the API key |
registries.*.trust | check_full | trusted 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
| Flag | Description |
|---|---|
--output | Write policy to file (default: stdout) |
--name | Policy name (default: generated-<session-id>) |
--threshold | Minimum occurrences before including a path pattern (default: 1) |
--include-blocked | Include blocked operations as commented-out rules for review |
What Gets Generated
The generated policy:
- File rules: Allows only paths that were accessed, grouped into globs (e.g.,
/workspace/src/**/*.ts) - Network rules: Allows only domains/IPs that were contacted, with subdomain wildcards (e.g.,
*.github.com) - Command rules: Flags risky commands (
curl,wget,rm) with the exact arg patterns observed - Comments: Documents why each rule exists with session timestamp
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
- CI/CD lockdown: Profile a build/test run, lock future runs to that exact behavior
- Agent sandboxing: Let an AI agent run a task once, generate policy for future runs
- Container profiling: Profile a workload, generate minimal policy for production
- Compliance: Document exactly what a process does and enforce it doesn't change
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