One thread per owner
One conversation shared by web, Telegram and WhatsApp. Its ID keys the workspace and chat session. Background sessions have their own browsers and saved Pi transcripts.
This page shows how Nod's parts fit together and where each one lives in the code. Start here before you change Nod or evaluate how it works.
The short version: Nod is one Node process plus Docker containers on the computer running your assistant. That can be your desktop's private runtime or a self-hosted server reached by web and desktop clients.
The main chat session coordinates work. It answers short requests and starts a saved session for each independent task. Task questions, progress and results return to the same conversation. Task results do not need a second model call in the coordinator.
The tasks table is the task ledger. All sessions share the owner's workspace and memory, with separate saved histories and task folders. The server routes replies and delivers addressed owner updates to the correct session.
See Task sessions for the architecture diagram, records, context sharing and lifecycle.
One Node process serves the React client and runs the Fastify API, the agent runtime, the thread-drain worker and the schedulers. Docker containers run PostgreSQL, the workspace and the browser. While you pick a login from 1Password, a separate helper process talks to the 1Password app, so the server never loads 1Password's code.
flowchart TD web([Web client]):::accent --> api[API and server features] channels([Telegram · WhatsApp]) --> api api --> db[(PostgreSQL<br/>and pg-boss jobs)] db --> worker[Thread-drain worker] worker --> runtime[Agent runtime] runtime --> pi[Pi session]:::accent runtime --> browser[Browser container]:::muted runtime --> workspace[Workspace container]:::muted
Messages and schedule fires become turns in PostgreSQL with a thread-drain job. App events and source checks feed one observer through a saved source queue. The worker picks up the job and asks the runtime to run a turn. The runtime opens a Pi agent session, and Pi calls tools that act in the workspace, the browser and your connected apps.
One thread per owner
One conversation shared by web, Telegram and WhatsApp. Its ID keys the workspace and chat session. Background sessions have their own browsers and saved Pi transcripts.
Everything is a turn
Messages, schedule fires, event wakes and resumes each run as a turn. Turns are never retried.
Policy lives on the server
The server checks app actions, Vault use and payments. The model's instructions sit on top of those checks, not in place of them.
A sandboxed computer
Commands run in a locked-down container. Provider keys never enter it or the browser.
One observer reads connected apps and saves private work. It starts separate tasks for outside actions, questions, and approvals. Those tasks use normal tools and Trust. See Proactivity engine. Ordinary tasks and schedules keep their normal tools.
scripts/check-architecture.mjs runs as part of pnpm run check. It fails when an import crosses a boundary:
| Folder | Can import from |
|---|---|
web |
web, shared |
shared |
shared |
ports |
ports, shared |
runtime |
runtime, ports, shared |
providers |
providers, ports, shared |
server |
server, ports, shared. Only composition.ts also imports runtime and providers. |
The same check keeps vendor packages (Pi, Composio, chat adapters and agent-browser) inside providers. It also stops runtime, ports, shared and web from reading process.env. Configuration belongs in src/server/composition.ts.