How it works
This page explains snowpea's local daemon, client connections, protocol flow, and storage locations.
On this page
- Overall flow
- Daemon-owned state
- Client connection
- Client choices
- Storage locations
Overall flow
The daemon, also called snowpea core, is a local process written in Python that owns all stateful data. The terminal UI (TUI), desktop app, and custom clients are clients attached to the daemon. Clients communicate with the daemon over the documented WebSocket JSON-RPC protocol.
┌──────────────┐ ┌──────────────────┐ ┌────────────────┐
│ terminal UI │ │ desktop app │ │ your own app │
└──────┬───────┘ └────────┬─────────┘ └───────┬────────┘
│ │ │
└──────── ws://127.0.0.1:<port>/ws ────────┘
│
┌─────────┴──────────┐
│ snowpea daemon │
│ sessions · tools │
│ permissions · │
│ memory · schedules │
└─────────┬──────────┘
│
LLM vendors · files · shell · webDaemon-owned state
The daemon stores the canonical state for sessions, tools, permissions, memory, schedules, and messenger bindings. Clients do not store that state separately.
| State | Description |
|---|---|
| Sessions | conversations and every event inside them |
| Tools | reading and editing files, the shell, web search, the browser, and the other work the agent performs |
| Permissions | which tools run without asking and which tools request approval before they run |
| Memory | data that persists across sessions. A named agent gets its own namespace |
| Schedules | jobs like "every day at 9am", run inside the daemon rather than a separate cron daemon |
| Messenger bindings | Telegram, Discord, and Slack connections |
Because the daemon owns schedules, closing the terminal does not stop a scheduled job. Because the terminal UI and desktop app display the same session, the same conversation can continue in either place.
The daemon starts when the first client attaches. It shuts down when there are no open sessions, enabled jobs, messenger bindings, or named agents.
To check daemon status, run this command.
bashsnowpea daemon statusThe output shows the sessions, jobs, bindings, or agents currently keeping the daemon alive.
Client connection
On startup, the daemon chooses a loopback-only port. It writes {port, pid, token, startedAt, protocolVersion} to $SNOWPEA_HOME/daemon.json. A client reads this file first.
| Surface | Endpoint | For |
|---|---|---|
| WebSocket | ws://127.0.0.1:<port>/ws | JSON-RPC 2.0, bidirectional |
HTTP GET | /health | is it alive |
HTTP GET | /protocol.json | the schema, as JSON |
HTTP GET | /version | server and protocol versions |
The daemon is loopback-only and is not exposed to the network. To work on code on another host, use a backend (docker, ssh) instead of opening a port.
A connected client immediately calls system.hello. The parameters are the token from $SNOWPEA_HOME/token, the client version, and the protocol version the client was built against. Any other method before that fails with unauthorized, and a major-version mismatch is refused with protocol_incompatible.
After system.hello, communication is bidirectional. The client calls methods such as session.*, turn.*, approval.*, schedule.*, and memory.*. The daemon sends events such as message.delta, tool.call, diff, and turn.done.
The exact schema is the protocol reference. The protocol overview explains how the reference and TypeScript bindings are generated together from the core source. CI fails the build when they drift apart.
Client choices
| Client | Usage |
|---|---|
| Terminal UI (TUI) | Run snowpea to start the daemon when needed and attach the UI |
| Desktop app (snowpea desktop) | A separate window attached to the same daemon. Installing it does not move sessions; it displays the same sessions |
| Headless CLI | snowpea -c "..." runs one turn with no UI and exits with a code. Add --json to output events as JSON Lines |
| Custom client | The TypeScript SDK uses the same protocol |
To inspect events from the headless CLI as JSON Lines, run this command.
bashsnowpea -c "summarise README.md" --jsonOne turn runs and protocol events are written as JSON Lines.
Storage locations
snowpea data is stored on the local machine.
| Path | What |
|---|---|
$SNOWPEA_HOME (default ~/.snowpea) | everything global |
$SNOWPEA_HOME/settings.json | providers, search and browser choice, tool categories, timeouts |
$SNOWPEA_HOME/credentials.json | bot tokens and secrets, mode 0600 |
$SNOWPEA_HOME/daemon.json | port, pid, token, start time, protocol version |
$SNOWPEA_HOME/state.db | sessions, events, memory, jobs, team tasks, named agents |
$SNOWPEA_HOME/logs/ | daemon.log, approvals.jsonl |
$SNOWPEA_HOME/plugins/, skills/, agents/, commands/ | installed and hand-written extensions |
<project>/.snowpea/settings.json | default mode, allowlist and backend for this repository |
<project>/.snowpea/{skills,agents,commands}/ | project-local extensions |
<project>/.claude/{skills,agents,commands}/ | read for Claude Code compatibility |
SNOWPEA_HOME is honoured everywhere.
To use a second, fully isolated installation, run this command.
bashSNOWPEA_HOME=/tmp/snowpea-scratch snowpea daemon statusThe daemon status is shown for the separate state under
/tmp/snowpea-scratch.
Only the prompt sent to the LLM vendor you chose leaves the machine. The only first-party service snowpea contacts on its own is registry.snowpea.ai, and only when you search for or install a skill. If you use a model running locally, the prompt also stays on the machine.