Architecture
Modular is three processes on your machine: the desktop app (Electron), a local server it starts, and the web UI shown in the app's window. The web UI can also run in a browser against a server you start yourself.
Electron main (apps/desktop) ──starts──▶ server (apps/server) ◀──HTTP / WebSocket── web UI (apps/web)
│ │
└── window, tray, updates, ├── SQLite database (packages/db, Drizzle)
CLI install, native helpers ├── terminals (node-pty), agent CLIs
└── plugins (server side)In the desktop app the web UI reaches the server through Electron main (an IPC fetch
broker), not through the browser's network stack; in a browser it uses HTTP directly.
Terminals stream over WebSockets; live updates (Spaces, attention, downloads, plugins)
come over one server-sent event stream per window (GET /events).
Repository#
| Path | What it is |
|---|---|
apps/desktop | Electron main and preload: window, tray, updates (Sparkle on macOS), CLI install, data folder, backups |
apps/server | The local server (Elysia, TypeScript): one folder per module in src/modules/ |
apps/web | The React UI: the canvas, studios, overlays, settings (src/features/) |
apps/relayd | A Go relay that connects Modular servers which cannot reach each other directly |
apps/landing | The website and blog |
apps/playground | A sandbox for UI experiments |
apps/zhi-slack-bridge | An MCP bridge that routes an agent's questions to Slack threads |
packages/cli | The modular command, mostly generated from the server's OpenAPI |
packages/db | The Drizzle schema and migrations |
packages/plugin-sdk | The plugin SDK and its developer guide |
packages/design-system | Design tokens and rules for the UI |
packages/ipc | Typed IPC between Electron main and the renderer |
packages/streamdown | The markdown renderer |
packages/download-center | The shared downloader |
packages/fabric-protocol | The protocol between connected Modular servers |
packages/model-api-simulator | A local stand-in for model APIs, used by tests |
plugins/* | The built-in plugins |
chronicle | A Rust daemon for local activity capture |
e2e | End-to-end scenarios (Cucumber + Playwright) |
resources | App icons, the brand, and skills shipped with the app |
Each module folder has a README.md that says what it owns and how it connects.
Start with apps/server,
apps/web/src/features and
apps/web/src/features/canvas.
Principles#
- Read across, write within. A module may read another owner's data, but writes only its own. Modular writes into another program's files only where the owner decided it must (for example, marking a Modular-made worktree as trusted for Claude Code).
- One owner per piece of data. Each table and each folder has one module that owns its meaning and lifecycle.
- The canvas owns its terminals. A terminal lives as long as its node; closing a window stops nothing, removing a node stops its process.
The full rules are in AGENTS.md; the decisions behind the current
design are in plans/.