05 · Configuration Reference

pi-web is fully configured through two paths: the .env.local file and the ~/.pi/agent directory. This chapter documents the purpose, default value, and an example for every variable.


Getting Started

  1. Copy the example file:

    cp .env.local.example .env.local
  2. Fill in variables as needed. By default .env.local.example only lists credentials and session defaults; add the remaining variables (attachments, session store, hot reload, etc.) manually as needed. For the full list, see Full Variable Table below.

  3. Start the dev server:

    pnpm dev

    Expected result: Next.js listens on http://localhost:3000 (the default next dev port); opening it in the browser lands you on the source-selection page.

If you have already logged in with pi, your API key is already present in ~/.pi/agent/auth.json, so you do not need to set any provider key and can skip step 2 entirely and start directly. If the page reports an authentication error after startup, see 18 · Troubleshooting FAQ.


Full Variable Table

1. pi agent directory

VariableDefaultDescription
PI_WEB_AGENT_DIR~/.pi/agentpi config directory; the agent process reads auth.json / settings.json from it; takes precedence over PI_CODING_AGENT_DIR
PI_CODING_AGENT_DIR~/.pi/agentMatches the env variable of the pi CLI itself; used as a fallback when PI_WEB_AGENT_DIR is unset

Resolution logic (lib/app/config.ts:resolveAgentDir): PI_WEB_AGENT_DIRPI_CODING_AGENT_DIR~/.pi/agent.

# Multi-tenant scenario: isolate config per user
PI_WEB_AGENT_DIR=/srv/tenants/acme/.pi/agent

2. Provider API Key (optional passthrough)

These keys only need to be filled in when you want to override or supplement ~/.pi/agent/auth.json. After the server reads them, they are passed to the agent subprocess through the spawn env, and are never written into the response body, logs, or the client.

VariableDescription
ANTHROPIC_API_KEYAnthropic Claude family
OPENAI_API_KEYOpenAI / compatible gateways
GOOGLE_GENERATIVE_AI_API_KEYGoogle Gemini (AI SDK)
GEMINI_API_KEYGoogle Gemini (native)
MISTRAL_API_KEYMistral
OPENROUTER_API_KEYOpenRouter gateway

Authentication source priority: env key (additive) > ~/.pi/agent/auth.json (read by the agent process).


3. Session default provider / model

VariableDefaultDescription
PI_WEB_DEFAULT_PROVIDER(unset, read from settings.json)Force a specific provider, e.g. openrouter
PI_WEB_DEFAULT_MODEL(unset, read from settings.json)Force a specific model, e.g. anthropic/claude-sonnet-4.6

When left unset, the defaultModel / defaultProvider in ~/.pi/agent/settings.json take effect, and the UI respects your local pi configuration.

PI_WEB_DEFAULT_PROVIDER=openrouter
PI_WEB_DEFAULT_MODEL=anthropic/claude-opus-4-5

4. Hide providers (deployment control)

VariableDefaultDescription
PI_WEB_HIDE_PROVIDERS(unset, all visible)Comma-separated provider names to remove from the GET /config/models response and the settings dropdown
# Deploy to an environment that only allows OpenRouter routing; hide direct-connect providers
PI_WEB_HIDE_PROVIDERS=anthropic,openai,google

The implementation lives in packages/server/src/config/model-options-filter.ts (exports parseHiddenProviders / excludeProviders) and lib/app/pi-handler.ts:338 (the /config/models route calls parseHiddenProviders + excludeProviders); the in-session get_available_models RPC applies the same filter (packages/server/src/http/routes/query-routes.ts:113), keeping the dropdown and the runtime-selectable set consistent. The frontend fetches data via GET /api/config/models. See 06 · Providers and Models for details.


5. Default agent source / working directory

VariableDefaultDescription
PI_WEB_DEFAULT_SOURCE(unset)Default value on the source-selection page; a local directory or git URL are both accepted
PI_WEB_DEFAULT_CWDthe server process’s process.cwd()Session working directory; affects the file tree the agent can see
PI_WEB_DEFAULT_SOURCE=./examples/hello-agent
PI_WEB_DEFAULT_CWD=/workspace/myproject

6. Attachment system

VariableDefaultDescription
PI_WEB_ATTACHMENT_DIR~/.pi/agent/attachmentsRoot directory where attachments are persisted (the sole source for the local backend); the main process delivers it to the subprocess through the spawn env, and both ends must match
PI_WEB_ATTACHMENT_SECRET(random per process when unset)HMAC signing secret; must be set explicitly in subprocess-sharing scenarios, otherwise signed URLs produced by the subprocess fail with 401 when verified by the main process
PI_WEB_ATTACHMENT_URL_BASE/api (injected by pi-handler)Prefix for attachment signed URLs; usually does not need to be set manually

The default directory resolution logic is in packages/server/src/attachment/config.ts:resolveAttachmentDir.

PI_WEB_ATTACHMENT_DIR=/data/pi-attachments
PI_WEB_ATTACHMENT_SECRET=your-stable-hmac-secret-min-32-chars

7. Development / e2e only

VariableDefaultDescription
PI_WEB_STUB_AGENT=1offSwitches every session to a deterministic local stub agent (consumes no API key); enabled by default for Playwright e2e
PI_RUNNER_HOT_RELOAD=1offIn dev mode, watches packages/tool-kit/src and automatically restarts idle runners on source changes (no need to open a new session)
PI_RUNNER_HOT_RELOAD_PATHSpackages/tool-kit/src (absolute path)Comma-separated list of directories to watch; overrides the default path
PI_WEB_AUTOSTART=1offAutomatically creates a session on the home page and skips the source-selection page; injected automatically when the CLI (bin/pi-web.mjs) starts
PI_WEB_WATCH=1offWritten by the CLI --watch mode; also enables hot reload under production standalone (not gated by NODE_ENV)
NEXT_DIST_DIR.nextSpecifies the Next.js build output directory; the CLI build uses .next-cli and the e2e build uses .next-e2e to avoid polluting the shared .next
# Offline e2e run
PI_WEB_STUB_AGENT=1 NEXT_DIST_DIR=.next-e2e pnpm build
PI_WEB_STUB_AGENT=1 NEXT_DIST_DIR=.next-e2e next start -p 3100
 
# Dev-time hot reload (edit tool-kit/src without reopening a session)
PI_RUNNER_HOT_RELOAD=1 pnpm dev

Note: Do not run pnpm build while the dev server is running, as it pollutes the shared .next and causes route webpack 500 errors. The CLI / e2e builds are isolated via NEXT_DIST_DIR. If you hit such a 500, see 18 · Troubleshooting FAQ.


8. Session store

VariableDefaultDescription
SESSION_STOREfsSession persistence backend: fs (file) / sqlite / postgres (falls back to fs when unset or empty)
SESSION_STORE_ROOT~/.pi/agent/sessionsStorage root directory for fs mode (default produced by defaultSessionsRoot()); session files are bucketed under the root by working directory cwd
SESSION_STORE_PATH:memory:Database path for sqlite mode
DATABASE_URL(unset)Connection string for postgres mode (required when SESSION_STORE=postgres)
SESSION_STORE=sqlite
SESSION_STORE_PATH=/data/pi-web-sessions.db

9. Sessions List view (sessions-list)

The visibility and placement of the Sessions List panel are controlled by two NEXT_PUBLIC_* variables. NEXT_PUBLIC_* variables are inlined into the client bundle at build time and are readable on both ends (the frontend reads them to decide rendering, the backend reads them to gate scope=all requests), so be sure to keep their values consistent across both ends.

VariableDefaultDescription
NEXT_PUBLIC_PI_WEB_SESSIONS_GLOBAL(unset, off)When set to true / 1, shows the “All” (system / cross-machine) sessions tab; when off, the backend returns 403 SESSIONS_GLOBAL_DISABLED for scope=all directly, without touching storage
NEXT_PUBLIC_PI_WEB_SESSIONS_SLOTsidebarThe host slot for the Sessions List panel: sidebar / header / footer / empty; invalid values fall back to sidebar

The frontend read logic is in components/chat-app.tsx:172 (SESSIONS_GLOBAL_ENABLED) and components/chat-app.tsx:184 (SESSIONS_SLOT); the backend gating is in packages/server/src/session-list/session-list-routes.ts:136 (scope=all && !globalEnabled → 403). See 21 · Sessions List for the complete explanation.

# Enable the system sessions view and move the list to the header
NEXT_PUBLIC_PI_WEB_SESSIONS_GLOBAL=1
NEXT_PUBLIC_PI_WEB_SESSIONS_SLOT=header

10. Logging

Logging is handled by the isomorphic package packages/logger, which parses the following env config (parsing logic in packages/logger/src/config.ts:48); the client and server share the same variable names.

VariableDefaultDescription
PI_WEB_LOG_ENABLED(enabled)When set to false, disables log output; any other value is treated as enabled
PI_WEB_LOG_LEVELinfoLog level: debug / info / warn / error
PI_WEB_LOG_NAMESPACES(all)Comma-separated namespace allowlist (e.g. agent,ext); only the listed namespaces are enabled
PI_WEB_LOG_FILE(unset, no file written)Absolute path of the log file; setting it enables file output
PI_WEB_LOG_FILE_MAXSIZE10Per-file rotation threshold (MB)
PI_WEB_LOG_FILE_MAXFILES5Maximum number of rotated backup files
PI_WEB_LOG_LEVEL=debug
PI_WEB_LOG_NAMESPACES=agent,ext
PI_WEB_LOG_FILE=/var/log/pi-web/app.log

For the level semantics, namespace layering, and the Node/browser differences of the logging system, see 16 · Logging.


~/.pi/agent directory structure and priority

~/.pi/agent/
├── auth.json        # API key / OAuth token (written by pi login)
├── settings.json    # default provider / model, installed packages, theme
├── models.json      # custom provider model list (non-built-in providers go through this file)
├── attachments/     # default attachment persistence directory (when PI_WEB_ATTACHMENT_DIR is unset)
└── sessions/        # session history (when SESSION_STORE=fs)

Priority rules:

  1. auth.json — read directly by the agent process; env keys (ANTHROPIC_API_KEY, etc.) are layered on top of it.
  2. settings.json — read by the agent process; PI_WEB_DEFAULT_PROVIDER / PI_WEB_DEFAULT_MODEL can override at the env layer.
  3. models.json — model registration for non-built-in providers; the format must include baseUrl + apiKey, and setting the api field to openai-completions lets you connect to gateways such as NewAPI.
  4. PI_WEB_AGENT_DIR / PI_CODING_AGENT_DIR — pointing them at different directories enables multi-tenant isolation.

Minimal .env.local example

# Simplest: rely on the key already present in ~/.pi/agent/auth.json
# No need to set any ANTHROPIC_API_KEY or similar
 
# If you need to force a model (optional)
PI_WEB_DEFAULT_PROVIDER=openrouter
PI_WEB_DEFAULT_MODEL=anthropic/claude-sonnet-4.6
 
# Attachment system (recommended to set explicitly when enabling attachments)
PI_WEB_ATTACHMENT_DIR=/data/my-attachments
PI_WEB_ATTACHMENT_SECRET=stable-secret-at-least-32-chars-here