pando-ai
v0.7.5
Published
AI coding firewall for Codex and Claude Code: supervised launchers, Pando MCP, policy enforcement, Claude hooks, and local provider gateway.
Maintainers
Readme
pandō — AI coding firewall
pando-ai is an AI coding firewall for Codex and Claude Code. It installs
supervised launchers so developers keep running codex ... and claude ...
normally while Pando applies local policy on each launch.
Its jobs:
- Constrain tool use — install project-scoped Pando MCP servers, deny native tools where supported, and deny non-Pando MCP tools by default.
- Inspect tool traffic — use Claude Code hooks on every Claude launch and a local provider gateway where available to block off-policy tool calls and tool results.
- Keep code local by default — run indexing, search, snapshots, and edits locally; optional working-set memory is off unless explicitly enabled.
- Prefer semantic operations — give agents structured code tools for search, references, renames, edits, snapshots, and shell execution instead of making raw shell access the default control surface.
It is built on the same indexing + MCP engine as the Pandō VS Code extension.
Install
Requires Node.js 22.5.0 or newer.
npx -y pando-aiOn a terminal this opens the firewall console: it detects whether codex
and claude are protected, offers to install supervised launchers for any that
aren't, and shows current status and policy. It also installs a persistent
~/.pando/bin/pando-ai command shim and, when the current PATH contains a
stable writable command directory, a same-terminal pando-ai shim. Future
commands such as pando-ai uninstall then work even when the first run came
from npx. After installation you keep running codex and claude normally
— Pando supervises each launch.
How it works
Four layers, one ruleset:
- Launch shim (
~/.pando/binahead of the real tools on PATH) — supervises everycodex/claudeinvocation. It disables the agent's native tools where supported, dynamically injects root-scoped Pando MCP servers, and applies the MCP allow/deny policy via the agent's own launch flags. Pando does not permanently add itself to the user's MCP config files by default; supervised launches pass generated config on each run. - Wire gateway (a local reverse proxy speaking the OpenAI Responses API and
Anthropic Messages API) — sits inline on every supported request and forwards
it to the upstream you control. Policy enforcement happens locally before a
request is forwarded and before a model response reaches the agent. It blocks
off-policy tool definitions, tool calls, and tool results before they can
cross the provider boundary, and blocks off-policy tool calls on model
responses before the agent can execute them. When enabled, it runs the
protocol-adapted working-set memory engine ported from
pando-proxy. - Memory manager (
PANDO_GATEWAY_MEMORY=1, off by default) — maintains a per-session working set and archive recall path. The memory core is separate from policy enforcement: policy decides which tools are allowed; memory decides which prior context remains visible. OpenAI Responses and Anthropic Messages use protocol adapters around the same core. The maintenance classifier currently uses an OpenAI-compatible structured model backend. - Policy (
~/.pando/policy.toml, or a Pando DB table) — one allowlist plus a default action. By default, native tools are denied and non-Pando MCP servers are denied; Pando MCP is always allowed. See[native_tools],[other_mcp],[proxy],[firewall].
Security model
Pando's security model has three separate jobs:
- Containment: the
pando_workspaceMCP process runs model-triggered workspace mutation in an OS sandbox. The sandbox limits the process to the project root, sandbox-local home/cache/temp directories, and no network by default. This is the hard boundary for broad operations such as shell commands and generated code. - Audit and recovery: mutating Pando tools and
shell-commandcreate Pando snapshots around workspace changes. These are local, commit-like history records for inspection and restore; they are not commits to the user's Git repository. - Semantic control: Pando tools give the agent structured operations such as
find-nodes,find-references,rename,replace,insert,filter-map-reduce,snapshot-worktree, andrestore-files. These tools keep requests bounded, make intent inspectable, require stable paths/hashes where relevant, and reduce the need for raw shell/file access.
The sandbox and snapshots are the fallback safety boundary. The Pando tools are the preferred control plane: they make ordinary coding work narrower, more auditable, and more semantically meaningful than arbitrary command execution. Shell remains available for tests, builds, package managers, generated files, unsupported languages, and tasks where a structured Pando operation is not the right tool.
Workspace sandbox and privileged helper
Supervised launches split Pando into two MCP roles:
pando_coreruns trusted read/search/planning/index operations.pando_workspaceruns model-triggered workspace mutation and shell commands.
The workspace role re-execs into the OS sandbox before serving mutating tools. On macOS this uses Seatbelt; on Linux it uses bubblewrap. The sandbox is configured to allow the project root, sandbox-local home/cache/temp directories, and the minimal runtime/toolchain paths needed to start common commands. Network is denied by default. There is no silent unsandboxed fallback for workspace mutation.
Install-time security setup also provisions dedicated local users
(pando-workspace and pando-core) and a root-owned helper daemon. The helper
is intentionally narrow: it can only launch Pando's known roles for validated
project roots over stdio. It is not a general command runner and does not accept
custom argv, shells, or arbitrary commands.
The helper entrypoint is:
pando-ai security launch --role <core|workspace> --project-root <path> --stdioThe unprivileged CLI validates the request, connects to the local helper socket, and sends only the fixed launch request. The root-owned daemon validates again, selects the role's dedicated user when privilege is available, starts the fixed Pando MCP server invocation, and lets the normal workspace sandbox apply before mutating tools are served.
Security setup is normally invoked by install/uninstall, but can be run directly:
pando-ai security setup-usersDiagnostics use the same JSONL log as the gateway and installer:
~/.pando-data/gateway-debug.jsonl by default, or PANDO_GATEWAY_LOG_FILE when
set. Logging is on by default and can be forced with PANDO_DEBUG=1; set
PANDO_DEBUG=0 to disable default helper logging. Useful event prefixes include
security_helper_client_* and security_helper_daemon_*.
Supported model APIs
The gateway supports the current agent-facing APIs used by the supervised launchers:
- OpenAI/OpenAI-compatible Responses API:
/v1/responses - Anthropic Messages API:
/v1/messages
Legacy completion APIs, including OpenAI chat/completions and Anthropic
/v1/complete, are intentionally unsupported.
Enforcement matrix (what is actually enforced)
| Capability | Claude Code | Codex |
| --- | --- | --- |
| Disable native tools | ✅ --tools "" (MCP stays available) + gateway/hook block | ⚠️ read-only sandbox + web search disabled + request/response gateway block |
| Install Pando MCP, root-scoped | ✅ dynamic --mcp-config + --strict-mcp-config | ✅ dynamic required pando_core / pando_workspace MCP config with Pando tools pre-approved |
| other_mcp: deny_all | ✅ --strict-mcp-config with generated Pando-only config + gateway/hook block | ✅ request/response gateway block |
| other_mcp: allow_list | ✅ strict config with Pando + named servers + gateway/hook block | ✅ request/response gateway block |
| other_mcp: deny_list | ✅ --disallowedTools removes denied names + gateway/hook block | ✅ request/response gateway block |
| Route traffic through local gateway | ✅ API-key/token/helper auth via ANTHROPIC_BASE_URL; hooks are always on | ✅ provider override |
- Codex has no documented strict-MCP switch and no exact
--tools ""equivalent, so launch-time stripping is best-effort. Pando marks its MCP servers required, setsdefault_tools_approval_mode="approve"for Pando's own MCP tools so non-interactive Codex launches can use them, disables Codex web search when native tools are denied, strips off-policy tool definitions from/v1/responsesrequests, strips historical off-policy tool call/result transcript items from resumed requests before they reach the model provider, and blocks new off-policy tool calls in model responses before Codex can execute them. - Claude Code always gets Pando hook settings for tool-call/tool-result
enforcement. API-key, auth-token, or Claude Code
apiKeyHelperauth also enables gateway mode throughANTHROPIC_BASE_URL. Subscription-only launches use hooks-only fallback because Claude Code does not route subscription OAuth through a custom gateway. - Claude Code launch hardening strips user-supplied flags that can reopen
native tools, MCP config, plugins, custom settings, IDE attachment, or bypass
permissions (
--tools,--mcp-config,--settings,--plugin-dir,--permission-mode,--allowedTools, dangerous permission/channel flags, and related aliases). Pando also setsENABLE_CLAUDEAI_MCP_SERVERS=falseand injects--no-chromeon Claude builds that expose it. Pando intentionally does not pass--bare, because it can force API-key billing paths and skip hooks on some Claude builds. Slash commands/skills are left enabled for now; tool execution is constrained through--tools "", strict MCP config, hooks, and the gateway. If Claude settings setdisableAllHooks=true, Pando refuses to launch Claude because hooks are required for subscription-mode enforcement. - Claude Code built-in/project MCP state is stricter than normal Claude config. When policy blocks non-Pando MCP servers, Pando launches Claude with strict generated MCP config containing only allowed servers. Generated Pando MCP config is passed dynamically on each supervised launch, and Pando leaves the user's on-disk Claude MCP config untouched by default.
- Claude Desktop general chat: not part of the strong-enforcement claim; transparent interception is not reliable there without app-level controls.
Claude Code gateway and hook modes
Claude Code tool enforcement is hook-based for every launch. Official Claude
Code gateway configuration uses ANTHROPIC_BASE_URL with
ANTHROPIC_AUTH_TOKEN, ANTHROPIC_API_KEY, or apiKeyHelper; those
credentials take precedence over subscription OAuth. When any of those
credentials are configured and [proxy].claude = "enforce", Pando also routes
Claude through the local gateway for full-wire interception and optional memory.
Pando injects Claude hook settings on each launch:
PreToolUseblocks off-policy tool calls before execution.PostToolUsereplaces off-policy tool output with a Pando block message before Claude can use it.PostToolUseFailureadds retry context; the tool has already failed, so this hook is not treated as a hard enforcement boundary.PostToolBatchblocks the loop before the next model request if a serialized off-policy tool result appears in the batch.
Hooks are the Claude tool firewall: they prevent Claude from using non-Pando/off-policy tools and prevent their results from becoming usable model context. Gateway mode is an additional full-wire layer for request/response inspection, memory, and provider-bound enforcement; hooks still run when the gateway is active.
Enabling gateway mode
Gateway mode starts only when Claude Code gateway credentials are present and
[proxy].claude = "enforce". Provide one of:
# Environment variable (highest precedence)
export ANTHROPIC_API_KEY=sk-ant-...
# …or an auth token
export ANTHROPIC_AUTH_TOKEN=...For local development you can keep the key in a git-ignored .env at the repo
root and source it before launching:
# .env (already git-ignored — never commit it)
ANTHROPIC_API_KEY=sk-ant-...
export $(grep -v '^#' .env | xargs) # load it into the shell
claude -p 'hello' # Pando supervises + routes via gatewayA apiKeyHelper configured in Claude Code settings works as well. With any of
these present Pando logs gateway listening on http://127.0.0.1:<port>
(memory=off) and sets ANTHROPIC_BASE_URL for the child claude process.
Without them, Pando falls back to hooks-only enforcement over subscription OAuth.
The gateway forwards the Anthropic Messages and OpenAI Responses APIs
transparently, including compressed upstream responses: fetch decodes the
content-encoding (gzip/brotli) before Pando inspects or rewrites the body, and
the gateway emits the decoded, identity-encoded bytes — it never re-advertises a
content-encoding it isn't sending.
Provider proxy toggle
Users can enable or disable the local provider proxy per supervised tool:
pando-ai proxy status
pando-ai proxy enable claude
pando-ai proxy disable claude
pando-ai proxy enable codex
pando-ai proxy disable codexThis writes the [proxy] section in ~/.pando/policy.toml:
[proxy]
codex = "enforce" # enforce | off
claude = "enforce" # enforce | offWhen Claude proxying is off, Pando uses hooks-only fallback for tool-call/tool-result enforcement. When Codex proxying is off, Pando still launches Codex with Pando MCP and best-effort local restrictions, but provider-bound gateway enforcement is disabled.
Surfaces
pando-ai # firewall console: status, install, uninstall
pando-ai install # force a (re)install pass
pando-ai uninstall # remove Pando shims, managed PATH block, install state, and global npm install when detected
pando-ai serve [path] # stdio MCP server for MCP clients
pando-ai serve-http # explicit HTTP MCP server for debugging/integrations
pando-ai gateway # run the firewall gateway in the foreground (debug)
pando-ai proxy status|enable|disable [codex|claude]
pando-ai security setup-users
pando-ai security launch --role <core|workspace> --project-root <path> --stdio
pando-ai login|logout|whoami
pando-ai config set telemetry falsepando-ai launch codex|claude -- <args> is the supervised launcher the shims
call; you don't run it directly.
Uninstall
To stop supervising codex and claude:
pando-ai uninstallThis removes Pando-owned codex/claude shims from ~/.pando/bin, removes
the Pando-owned pando-ai command shim, removes the managed PATH block from
your shell startup file when present, and deletes ~/.pando/state.json so
declined/install state does not suppress future setup prompts. It does not
delete policy files, logs, or other user data. If the command is running from a
global npm install, it also removes that global pando-ai package
automatically. npx runs are temporary, so there is no persistent npm package
to remove in that case.
MCP serve mode
MCP mode is explicit. Bare pando-ai always opens the firewall console; it does
not become an MCP server just because stdin/stdout are non-interactive.
Use stdio MCP for agents:
pando-ai serve /path/to/projectSupervised Codex and Claude launches register two Pando MCP server entries for the same project:
pando_coreexposes read, search, indexing, graph, planning, and snapshot inspection tools.pando_workspaceexposes workspace-mutating tools such as rename, replace, insert, delete, signature changes, filter-map-reduce, shell execution, restore operations, and exclude-dir changes.
The split is process-level. Each server process starts with a
PANDO_PROCESS_ROLE value, and tools outside that role are not registered. A
manual pando-ai serve /path/to/project command without PANDO_PROCESS_ROLE
starts a combined server for direct integrations; supervised Codex and Claude
launches use the split servers.
The pando_workspace process re-execs into a local workspace sandbox by
default. In that sandbox:
HOME, temp, and package-manager cache paths point under.pando-sandbox/.- Network is denied by default.
- The process can read and write the project root.
- Pando workspace data created by the sandboxed process stays sandbox-local.
Auth is handled separately from the sandbox home. The launcher passes the user's
real auth home through PANDO_AUTH_HOME_OVERRIDE, so the sandboxed workspace
process can read the existing ~/.pando-ai/session.json without making the
real home its writable HOME and without opening network login inside the
sandbox.
serve-http remains available as an explicit command for debugging or
integrations that need HTTP:
pando-ai serve-http /path/to/project --host 127.0.0.1 --port 5888pando-ai config set telemetry false disables usage telemetry.
Transport behavior
- MCP does not run by default. Agents should use
pando-ai serve. - HTTP MCP does not run by default. It only starts through explicit
pando-ai serve-http. - The CLI redirects incidental runtime logs to stderr so stdout stays valid JSON-RPC/MCP traffic in stdio mode.
Agent setup
At startup, the CLI detects common MCP clients and prints a matching configuration snippet when possible.
Current detection targets:
- Claude Code
- OpenCode
- Codex CLI
- Cursor
If no known agent is detected, the CLI still prints a generic MCP config snippet.
Telemetry
The CLI sends usage telemetry by default. It is non-blocking and can be disabled with:
pando-ai config set telemetry falseTelemetry includes:
- operation name, success/failure, and duration
- file and symbol counts (not names or contents)
- HMAC-hashed project path (not the raw path)
- MCP client name and version (e.g. "claude-code 1.x")
- a stable install ID and per-session ID
- your authenticated user ID (derived server-side from your login session)
- event timestamp
The CLI does not send source code, file paths, file names, or symbol names.
Support level
- TypeScript / JavaScript / Clojure / C# / C / C++: included in the standalone CLI distribution.
- Python / Java / Dart: adapters exist in the source tree, but their helper assets are not included in the published npm package yet.
Notes
- Some host-only extension tools are intentionally omitted from the CLI in V1.
- If a language helper is missing, the CLI should degrade with clear tool-level errors instead of failing startup.
