Skip to main content
Polpo runs AI agents as subprocesses that can execute arbitrary code. This guide covers the security measures in place to limit the blast radius.

Environment Variable Filtering (safeEnv)

By default, child processes inherit the full process.env of their parent — every agent subprocess would have access to all API keys, tokens, and secrets in the environment. Polpo uses safeEnv() to create a filtered copy of process.env containing only system-essential variables. API keys and secrets are never passed to agent processes. The allowlist of system-essential variables:
CategoryVariables
Core systemPATH, HOME, USER, SHELL, TERM, LANG, LC_ALL, LC_CTYPE
Temp directoriesTMPDIR, TMP, TEMP
Node.jsNODE_ENV, NODE_PATH, NODE_OPTIONS
EditorEDITOR, VISUAL
XDG directoriesXDG_DATA_HOME, XDG_CONFIG_HOME, XDG_CACHE_HOME, XDG_RUNTIME_DIR
Linux displayDISPLAY, WAYLAND_DISPLAY, DBUS_SESSION_BUS_ADDRESS
WindowsSYSTEMROOT, COMSPEC, PATHEXT, APPDATA, LOCALAPPDATA, PROGRAMFILES, WINDIR
SSH (for git)SSH_AUTH_SOCK, SSH_AGENT_PID
GitGIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL
ProxyHTTP_PROXY, HTTPS_PROXY, NO_PROXY, http_proxy, https_proxy, no_proxy
TimezoneTZ

Adding Extra Variables

If agents need specific environment variables, pass them explicitly:
import { safeEnv } from "polpo/tools/safe-env";

// Add extra vars from config (takes precedence over process.env)
const env = safeEnv({ MY_API_URL: "https://api.example.com" });

// Allow specific process.env vars through
const env = safeEnv(undefined, ["DATABASE_URL", "REDIS_URL"]);
Convenience helpers are available for common use cases:
import { bashSafeEnv } from "polpo/tools/safe-env";

// For bash tool commands — system vars only
const bashEnv = bashSafeEnv();

JSON Condition DSL

Notification rules support conditions that filter which events trigger notifications. Instead of eval() or new Function(), Polpo uses a pure-data JSON condition format (NotificationCondition) evaluated without any string parsing or code execution:
{
  "field": "passed",
  "op": "==",
  "value": false
}
Supported operators:
OperatorDescription
==Equal
!=Not equal
>, >=, <, <=Numeric comparison
includesString contains or array includes
not_includesNegation of includes
existsField is not null/undefined
not_existsField is null/undefined
Logical combinators:
{
  "and": [
    { "field": "passed", "op": "==", "value": false },
    { "field": "globalScore", "op": "<", "value": 2 }
  ]
}
{
  "or": [
    { "field": "level", "op": "==", "value": "error" },
    { "field": "level", "op": "==", "value": "warn" }
  ]
}
{
  "not": { "field": "status", "op": "==", "value": "done" }
}
Fields use dot-path notation resolved on the event payload (e.g. "task.status", "scores[0].score").

Network Security

Localhost-Only Default

The Polpo server binds to 127.0.0.1 by default — it is not accessible from other machines on the network:
polpo serve --port 3000
# Binds to 127.0.0.1:3000 (localhost only)

Binding to 0.0.0.0

If you need to expose the server to the network (e.g., for remote dashboards), you can override the host:
polpo serve --host 0.0.0.0 --port 3000
When binding to 0.0.0.0, the server is accessible to anyone on the network. Always configure API key authentication when exposing the server:
{
  "server": {
    "host": "0.0.0.0",
    "port": 3000,
    "apiKeys": ["your-secret-key-here"]
  }
}

CORS

The server restricts CORS origins to common local development ports by default:
  • http://localhost:3000, http://localhost:3001
  • http://localhost:5173, http://localhost:5174
  • http://127.0.0.1:3000, http://127.0.0.1:3001
  • http://127.0.0.1:5173, http://127.0.0.1:5174
Configure custom origins via corsOrigins in the server config.

Filesystem Sandbox

allowedPaths

Each agent can be restricted to specific filesystem paths using allowedPaths. All file-based tools (read, write, edit, glob, grep) enforce path sandboxing when this is configured.
{
  "team": {
    "agents": [
      {
        "name": "frontend-dev",
        "allowedPaths": ["src/", "public/", "package.json"],
        "role": "Frontend developer"
      }
    ]
  }
}
How it works:
  • Paths are resolved relative to the project’s workDir
  • Every file operation checks if the target path falls within the allowed directories
  • If no allowedPaths are configured, the agent defaults to the project’s workDir (cannot access parent directories)
  • Attempts to access paths outside the sandbox throw an error with a descriptive message

Path Resolution

// Agent with allowedPaths: ["src/", "tests/"]
// workDir: /home/user/my-project

// Allowed:
//   /home/user/my-project/src/index.ts       ✓
//   /home/user/my-project/tests/unit.test.ts  ✓

// Blocked:
//   /home/user/my-project/secrets.env         ✗
//   /home/user/.ssh/id_rsa                    ✗
//   /etc/passwd                               ✗

General Security Posture

ConcernMitigation
API key leakage to agentssafeEnv() filters environment variables
Code injection in conditionsJSON condition DSL — no eval()
Network exposureLocalhost-only by default (127.0.0.1)
Unauthorized API accessOptional API key authentication
Agent filesystem accessallowedPaths sandbox per agent
Credential storageAES-256-GCM encrypted vault (.polpo/vault.enc)
Cross-origin requestsCORS restricted to localhost origins
Log store errorsWrapped in try/catch — never breaks the system

Encrypted Vault

Agent credentials (SMTP, IMAP, OAuth, API keys, logins) are stored in .polpo/vault.enc, encrypted with AES-256-GCM. Credentials are never stored in polpo.json.

Encryption Key

The vault key is loaded from (in order):
  1. POLPO_VAULT_KEY environment variable — use this in CI/CD or containerized deployments
  2. ~/.polpo/vault.key — auto-generated on first use with chmod 600 (user-read-only)
The key file is stored in the home directory (~/.polpo/), not the project directory, so it is never committed to version control.

Vault File Structure

.polpo/vault.enc contains a single AES-256-GCM encrypted blob. Each entry is keyed by agentName:serviceName and contains:
FieldTypeDescription
typestring"smtp", "imap", "oauth", "api_key", "login", or "custom"
labelstringHuman-readable label
credentialsRecord<string, string>Key-value credential fields. Supports ${ENV_VAR} references

Managing Credentials

MethodCommand / Tool
CLI wizardpolpo agent onboard <name> (Email + Vault steps)
Add/update entryset_vault_entry tool (TUI / chat)
Remove entryremove_vault_entry tool
List entrieslist_vault tool (credentials masked)
View agent vaultpolpo agent show <name> (credentials masked)
Polpo follows a defense-in-depth approach. No single measure is sufficient on its own, but together they significantly limit the blast radius of a compromised agent.