Skip to content

Architecture

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 a few Docker containers on your computer.

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.

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

Every message, schedule fire and event becomes a row in PostgreSQL plus a thread-drain job. 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.

  • Directorysrc/
    • Directoryports/ capabilities as interfaces, without vendor types
      • …
    • Directoryproviders/ vendor SDKs and protocols, such as Pi, Docker, agent-browser and Composio
      • …
    • Directoryruntime/ the agent's tools, prompts and turn loop
      • …
    • Directoryserver/ policy, storage and HTTP
      • composition.ts the only place that selects providers and implementations
    • Directoryshared/ browser-safe types for the server and the client
      • …
    • Directoryweb/ the React client
      • …
  • Directoryscripts/
    • check-architecture.mjs enforces the import directions below

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.

  • Threads and turns: how messages, schedules and events become turns, and how they share one thread.
  • The agent runtime: Pi sessions, tools, codemode, the turn guard and the system prompt.
  • Sandbox and browser isolation: what the workspace and browser containers can and can't reach.
  • Memory: how nod learns and keeps what it knows about you.
  • Skills: the trusted task guides nod reads before it works.