-
Notifications
You must be signed in to change notification settings - Fork 0
security
This page describes the Prime Agent trust model: what runs with the user's permissions, how credentials are stored and scoped, where the web server binds and why it is not multi-user, the security policy and input-validation stance, and the optional OS-level sandbox extension.
The authoritative policy statement is SECURITY.md at the repository root, and the README security note at the repo root states the core guarantee.
Prime Agent executes model-generated Python and project (shell) commands with the user's permissions. Worker and kernel processes improve lifecycle isolation and recovery, but they are not a security sandbox: a model, a prompt, or a skill can cause arbitrary commands to run as the current user. Review changes and use trusted repositories, instructions, skills, and extensions only.
Concretely:
- The agent runs tools (IPython, Bash, file edits) in the process or kernel for the session, under the invoking user's account.
- Worker processes and the Jupyter kernel add isolation between long-running jobs and the daemon/client, and provide recovery, but they do not confine file access or network access.
- There is no jail or capability-dropping by default.
Credentials are API keys and OAuth tokens for model providers.
The credential resolver is packages/coding-agent/src/core/auth-storage.ts (AuthStorage). It resolves an API key from, in priority order:
- Runtime override (CLI
--api-key, in memory only, not persisted). - For Prime Inference: environment variable, then Prime CLI config, then
auth.json. - For other providers:
auth.json, then environment variable. - Fallback resolver (custom providers from
models.json).
AuthStatus.source reports where a credential came from (stored, runtime, environment, prime_cli, fallback, models_json_key, models_json_command, or stale).
The FileAuthStorageBackend persists credentials to ~/.prime/agent/auth.json (via getAgentDir()). It:
- Creates the parent directory with mode
0700. - Creates the file with mode
0600and rewrites it as0600after every write. - Uses
proper-lockfilefor sync and async locking so concurrent processes cannot race on token refresh. - Supports API-key credentials (
{ type: "api_key", key }) and OAuth credentials ({ type: "oauth", access, refresh, expires, ... }). - Expired OAuth tokens are refreshed under the lock; a refresh failure leaves the credentials in place for a later
/loginand marks the source stale.
setRuntimeApiKey keeps CLI-supplied keys in memory and never writes them to disk.
API keys can come from environment variables via packages/ai/src/env-api-keys.ts (wired through @earendil-works/pi-ai findEnvKeys/getEnvApiKey). The prime-agent.sh --no-env flag starts the agent with ambient environment credentials stripped, so secrets from the shell environment are not exposed to the model session. The DAEMON_CLIENT_ENV_KEYS allowlist (packages/coding-agent/src/modes/daemon/daemon-protocol.ts) limits which client env vars (the HERDR_* set) a client may forward to the daemon, and the daemon re-filters on receipt because the socket peer is untrusted.
OAuth tokens are stored in auth.json with type oauth and refreshed under lock when they expire (AuthStorage.refreshOAuthTokenWithLock). Device-code and callback flows are initiated and polled through web/app/src/routes/api/chat/providers/oauth.ts (handleChatProvidersOAuth). markAuthSourceStale fingerprints a credential and tracks stale (expired) sources without exposing the secret values.
The web server binds to 127.0.0.1 and uses no tokens, so there is no multi-user authentication: anyone who can reach the port can use the agent in that process. This is intentional for a local single-user tool. web/app/ARCHITECTURE.md and the README both list "no multi-user auth (binds to 127.0.0.1 with no tokens)" as a current limitation. The browser can be pointed at a remote runtime via the optional VITE_FLEET_PI_CHAT_RUNTIME_URL, which changes the trust boundary: when set, the HTTP handlers run against that remote runtime. The prime-agent web command binds to 127.0.0.1:3000 by default; --host/--port override the bind address.
The web surface validates every request body with zod schemas in web/protocol/src/chat-protocol.zod.ts. Schemas are per-domain under web/protocol/src/schemas/ and registered with zod-to-openapi. Handlers validate before acting, and userId/userEmail are server-set only (client-supplied values are stripped after validation).
Two renderer-facing security plans address untrusted model output:
-
plans/010-markdown-link-allowlist.md(DONE): the custom markdownarenderer was replaced with aMarkdownLinkcomponent that allowlists onlyhttp(s)andmailto:schemes inhref;javascript:,data:,vbscript:, protocol-relative, and relative links render as inert<span>text instead of navigable anchors. This avoids React 19 script-URL throws andjsx-no-script-urlviolations. -
plans/001-sanitize-chart-color-html-sink.md(DONE): OpenUI chart colors are sanitized at the HTML<style>sink so a malicious color cannot close the<style>tag.sanitizeChartColorinweb/design/src/components/chart.tsxallows only hex colors,var(--token)references, andrgb/rgba/hsl/hslawithout CSS structural characters.
These are examples of the broader rule that dynamic data is sanitized at the trust boundary; the plans avoid adding DOMPurify.
packages/coding-agent/examples/extensions/sandbox/index.ts is an example extension that applies OS-level sandboxing to bash commands via @anthropic-ai/sandbox-runtime (sandbox-exec on macOS, bubblewrap on Linux). It replaces the built-in bash tool with a sandboxed BashOperations wrapper:
- Config merges a default config, a global
~/.prime/agent/extensions/sandbox.json, and a project-local<cwd>/.prime/agent/sandbox.json(project takes precedence). - Network allow/deny domain lists, plus filesystem deny-read/allow-write/deny-write lists.
- Enforced via
SandboxManager.wrapWithSandboxon the bash subprocess, with SIGKILL on timeout or abort. - Disabled on unsupported platforms and via
--no-sandboxorenabled: false.
This is an example of adding a sandbox, not a default guarantee: the sandbox is only active when the extension is installed and enabled.
- API index, the three wire surfaces
- Web API, validation and local-only binding
- Handlers and PrimeBridge, the HTTP adapter
- System overview