Features

Execution-layer capabilities of agentsh — traffic control, data protection, deterministic enforcement, 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 operation (not an error), receives the guidance message, and moves on without wasting retries.

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}"

Rate Limiting#

Control how fast agents can call LLM APIs with token-bucket rate limiters for both request count and token consumption.

proxy:
  rate_limits:
    enabled: true
    requests_per_minute: 60    # RPM limit
    request_burst: 10           # Burst allowance (default: RPM/6)
    tokens_per_minute: 90000   # TPM limit
    token_burst: 15000          # Token burst (default: TPM/6)

Requests that exceed the rate limit receive an HTTP 429 Too Many Requests response. Token consumption is tracked post-response — the actual token count from the provider response is deducted from the budget. A concurrency limiter (max 4 in-flight requests when TPM is enabled) bounds worst-case overspend.

HTTP Service Gateway#

agentsh proxies an agent's HTTP traffic to third-party APIs through a local gateway that enforces path-level and method-level access rules, substitutes credentials on the wire, and prevents credential exfiltration. Declare a service in your policy, and agentsh handles routing, filtering, and secret management in a single configuration block.

# Allow the agent to read repo contents and manage issues on GitHub.
# The real token is fetched from Vault; the agent only sees a fake.
http_services:
  - name: github
    upstream: https://api.github.com
    secret:
      ref: vault://kv/github#token
      format: "ghp_{rand:36}"
    inject:
      header:
        name: Authorization
        template: "Bearer {{secret}}"
    default: deny
    rules:
      - name: read-contents
        methods: [GET]
        paths: ["/repos/myorg/*/contents/**"]
        decision: allow
      - name: manage-issues
        methods: [GET, POST, PATCH]
        paths: ["/repos/myorg/*/issues", "/repos/myorg/*/issues/*"]
        decision: allow

Key properties:

For the full schema, credential substitution model, and worked examples, see Policy Reference → HTTP Services.

Cooperative vs. Non-Cooperative Services#

How the agent's traffic reaches the gateway depends on whether the service's SDK supports a custom base URL.

Cooperative services

A cooperative service is any API whose SDK or CLI lets you override the base URL via an environment variable, constructor parameter, or config file. agentsh sets the appropriate variable automatically (e.g., GITHUB_API_URL=http://127.0.0.1:<port>/svc/github/), so the SDK routes all traffic through the gateway without code changes. This is the cleanest integration path.

CategoryServiceBase URL override
Source controlGitHubGITHUB_API_URL
GitLabGITLAB_URL / self-hosted base URL
PaymentsStripestripe.api_base (SDK constructor)
CloudAWSAWS_ENDPOINT_URL, per-service AWS_ENDPOINT_URL_<SVC>
Google CloudCLOUDSDK_API_ENDPOINT_OVERRIDES_<SVC>
AzureAZURE_AUTHORITY_HOST / per-service endpoint config
CommunicationSlackbase_url (SDK constructor)
PagerDutybase_url (SDK constructor)
InfrastructureHashiCorp VaultVAULT_ADDR
Kubernetes--server flag / kubeconfig clusters[].cluster.server
ObservabilityDatadogDD_DD_URL, DD_SITE
SentryHost encoded in SENTRY_DSN
ElasticsearchELASTICSEARCH_URL
Package registriesnpmNPM_CONFIG_REGISTRY
pip / PyPIPIP_INDEX_URL
Cargo / crates.ioCARGO_REGISTRIES_<NAME>_INDEX

Non-cooperative services

For services that do not support a base URL override, agentsh uses fail-closed network enforcement. When a service is declared in http_services: with allow_direct: false (the default), agentsh blocks any direct connection to the upstream host at the network monitor level. The agent receives a 403 with a message directing it to use the gateway URL.

To make non-cooperative SDKs work transparently, combine the HTTP service declaration with DNS & connect redirects to steer the traffic through the gateway automatically:

# Redirect direct api.stripe.com traffic through the gateway
dns:
  overrides:
    api.stripe.com: 127.0.0.1

http_services:
  - name: stripe
    upstream: https://api.stripe.com
    default: deny
    rules:
      - name: read-customers
        methods: [GET]
        paths: ["/v1/customers", "/v1/customers/*"]
        decision: allow

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. Soft-delete is supported across both the FUSE and ptrace enforcement backends—in ptrace mode, unlinkat calls are intercepted and replaced with mkdirat + renameat2 syscall injection to atomically move files to trash. 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

Ptrace Enforcement#

When seccomp user-notify, eBPF, and FUSE are unavailable — AWS Fargate, restricted Kubernetes, gVisor, Modal — agentsh can use the Linux ptrace API to intercept syscalls and enforce policy. Ptrace mode provides the same enforcement guarantees as full mode without requiring privileged kernel features.

For configuration and attach modes, see Setup → Ptrace Mode.

Target environments

EnvironmentWhy ptrace
AWS Fargateseccomp user-notify, eBPF, and FUSE are all blocked; SYS_PTRACE available with shared PID namespace
Restricted KubernetesSecurity contexts prevent seccomp/eBPF; ptrace via SYS_PTRACE capability
gVisor / FirecrackerCustom kernel does not support seccomp user-notify or Landlock
ModalgVisor runtime; ptrace provides full policy enforcement
exe.devHybrid mode: ptrace for execve, seccomp wrapper for file/network/signal

Performance

Overhead is ~5.4x with all optimizations enabled (versus ~23x without). Key optimizations:

Opt-in only

Ptrace mode is not auto-selected. Enable it explicitly via configuration or environment variables. See Setup → Ptrace Mode.

Hybrid ptrace + seccomp mode

On platforms where both ptrace and seccomp are available (e.g., exe.dev), agentsh supports a hybrid mode:

Configure by enabling ptrace with trace.execve: true and seccomp file_monitor with enforce_without_fuse: true, while disabling ptrace file/network/signal tracing.

Seccomp-Notify File Enforcement#

When FUSE and Landlock are both unavailable (e.g., nested containers, Firecracker microVMs), agentsh can enforce file policies using the seccomp user-notify mechanism. The supervisor process intercepts file-related syscalls, evaluates them against policy, and either allows or blocks the operation—all without a virtual filesystem layer.

How it works

The seccomp-notify file enforcement backend intercepts syscalls at the BPF filter level, then evaluates file paths against your existing file_rules policy:

Kernel requirements

Requires Linux kernel ≥ 5.9 for seccomp user-notify and ≥ 5.14 for AddFD emulation (SECCOMP_ADDFD_FLAG_SEND). Use agentsh detect to check—the file_enforcement field reports which backend is active.

Configuration

sandbox:
  seccomp:
    enabled: true
    file_monitor:
      enabled: true
      enforce_without_fuse: true  # Enable file enforcement via seccomp-notify
      intercept_metadata: true   # Intercept statx, faccessat2, etc. (default: true)
      openat_emulation: true     # AddFD emulation for openat (default: true)
      block_io_uring: true       # Block io_uring to prevent bypass (default: true)

What gets intercepted

CategorySyscallsEnforcement
File openopenat, open, creatAddFD emulation (supervisor opens, injects fd)
File open (v2)openat2CONTINUE + ID validation (never emulated)
Metadatastatx, newfstatat, faccessat2, readlinkatCONTINUE + ID validation
File modifyunlinkat, renameat2, linkat, mknodatCONTINUE + ID validation
io_uringio_uring_setup, io_uring_enter, io_uring_registerBlocked with EPERM

Emulation gating

AddFD emulation only activates when all of these conditions are met:

When any condition is unmet, file syscalls fall back to SECCOMP_RET_CONTINUE with ID validation (audit-only).

Policy Signing#

agentsh supports Ed25519 detached signatures for policy files, providing cryptographic proof of authorship and integrity. This ensures agents only enforce policies from trusted authorities — a signed policy cannot be modified without invalidating the signature.

How it works

CLI workflow

# 1. Generate an Ed25519 keypair
agentsh policy keygen --output /etc/agentsh/keys/ --label "security-team"

# 2. Sign a policy file (produces policy.yaml.sig)
agentsh policy sign policy.yaml --key /etc/agentsh/keys/private.key.json --signer "security-team"

# 3. Verify a signed policy
agentsh policy verify policy.yaml --key-dir /etc/agentsh/keys/
# → {"status":"valid","key_id":"a1b2...","signer":"security-team","signed_at":"2026-03-18T..."}

Verification modes

ModeBehavior
enforceReject policies with missing or invalid signatures. Server refuses to start or create sessions.
warnLog a warning on verification failure, but load the policy anyway.
offSkip signature verification entirely (default).

Configuration

policies:
  signing:
    trust_store: "/etc/agentsh/keys/"   # Directory of trusted public key JSON files
    mode: "enforce"                    # "enforce" | "warn" | "off"
Security hardening

Private key files must have mode 0600 (rejected otherwise). In enforce mode, world-writable trust store directories and files are rejected. Expired keys (via expires_at field) are automatically rejected.

Staged Hot-Reload#

Policy changes can be deployed via a staged validation pipeline that prevents invalid or tampered policies from reaching live enforcement. Drop signed policies into a .staging/ directory; agentsh validates the signature, promotes to the live directory, and triggers a reload—all atomically.

How it works

  1. Stage — drop policy.yaml and policy.yaml.sig into <policy_dir>/.staging/
  2. Validate — the hot-reload watcher detects the new file, verifies the Ed25519 signature against the trust store
  3. Promote — signature file is moved to the live directory first, then the policy file (atomic on same filesystem)
  4. Reloadfsnotify detects the promoted policy in the live directory and triggers a policy reload

If signature validation fails, the staged files are rejected and an error is logged. If the policy move fails after the signature was already promoted, the orphaned .sig is removed from the live directory to prevent a retry loop.

Staging behavior by signing mode

Signing ModeStaging Behavior
enforceSignature required. Invalid or missing .sig is rejected.
warnSignature validated if present. Warning logged on failure, but policy is promoted.
offNo signature check. Staged files are promoted directly.
Deployment pattern

The .staging/ directory is created automatically when the hot-reload watcher starts. In CI/CD, use agentsh policy sign then cp both files into .staging/—the watcher handles the rest. A 2-second debounce ensures the .sig file has time to arrive before validation begins.

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

# Preserve real host paths (instead of virtualizing under /workspace)
agentsh wrap --real-paths -- claude --dangerously-skip-permissions -p "implement the feature"

# Use the agent-default policy with comprehensive guardrails
agentsh wrap --policy agent-default -- codex --full-auto "fix the failing tests"

# 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