pi-daemon1.3

Reference

Security

A Pi Daemon instance is one operator trust domain, not a sandbox. The server process holds no agent code: tools, MCP servers and extension modules run in supervised per-profile worker processes, and each worker is one trust domain. Real isolation between mutually untrusted workloads needs a separate uid, container or remote execution environment.

All clients of an instance share its bearer, so each can see and drive every session of that instance. Run a separate instance (its own state directory, port and bearer) for a client that must not.

Enforced defaults

  • exactly one effective service bearer source: when no external file, fd, or environment source is configured, first launch atomically generates STATE_DIR/api-token
  • generated bearer files are random, owner-only, complete before publication, stable across restart, and never overwrite an existing path
  • configured secret-manager bearer symlinks, including relative and nested links, are resolved to a canonical target that is opened with no-follow protection against a second traversal; the opened inode must be regular, current-user-owned, owner-only, and bounded
  • bearer material is reduced to a one-way digest after loading and never logged or returned
  • every HTTP request and WebSocket upgrade except /healthz and /readyz must present the bearer as Authorization: Bearer <token>; missing or wrong bearers are refused with 401 before routing
  • state and Pi agent directories are created owner-only; foreign-owned, symlinked or group/world-accessible daemon paths are refused
  • auth.json is seeded at most once, from an owner-only regular non-symlink JSON file, and never overwrites an existing file
  • instance YAML must be current-user- or root-owned, not group/world writable, bounded, alias-free and free of unknown fields
  • the listener binds loopback by default and speaks plain HTTP; binding another address (api.bind) is an explicit choice, so use a trusted network such as a tailnet, or a TLS reverse proxy, for remote clients
  • extension modules load only from the files a profile names, and a session may only leave some of them out; a built-in tool a profile does not list is never registered for its sessions
  • a session's cwd must be under its profile's cwdRoots (symlinks resolved), and clients may set only the nonsecret variables the profile's sessionEnv allows (a PREFIX* pattern never matches PATH, HOME, LD_PRELOAD or the other protected names)
  • a session's own MCP servers that run commands are allowed only for a session that has bash; skills, prompt templates and context files a client names must be within the profile's cwdRoots or agentDir; a session's variables, prompt and server definitions are never logged or sent to clients
  • clients never supply secrets: keep MCP server and tool credentials in a profile's envFiles, outside the Nix store, rather than in YAML; per-session credentials go in files the profile's sessionEnvFiles find from each session's cwd, read only in the profile's worker
  • release self-update adopts only the exact npm-global launcher symlink produced by just install; regular files, arbitrary/mismatched links, and foreign targets remain collisions
  • logs never contain prompts, model output, credentials, environment values or tokens

See also the repository SECURITY.md.