# Architecture

How the daemon, extension, and MCP server relate.

```
  Chrome extension ──HTTP (Bearer token)──▶ Daemon ──▶ SQLite  ─┐
   (side panel)                            :7331    ──▶ Chroma  │
                                                                │  shared
  AI client ──stdio──▶ MCP server ─────────────────▶ SQLite  ───┤  on-disk
              (spawned per session)        (direct) ─▶ Chroma  ─┘  stores
```

## Two readers, two stores, no coupling

- **SQLite** (`bookmarks.db`) — collections, bookmarks, chunk text, saved page HTML, index status.
- **Chroma** (`chroma/`) — one vector collection per bookmark collection.

The **daemon** owns writes: it runs the indexing pipeline and exposes the REST
API the extension uses. It is the only process that fetches pages.

The **MCP server** is read-only and reaches SQLite and Chroma directly. It never
calls the daemon, needs no API token, and works even when the daemon is stopped
(as long as something has already indexed the collection). Editors spawn one MCP
process per session, so several run at once — see [Tracing](/docs/guides/tracing/).

## Why the split

The extension talks to a long-lived local server because saving a page needs a
server-side fetch and a background job queue. The MCP server is short-lived and
latency-sensitive, so it skips HTTP and reads the stores it needs. Keeping them
decoupled means an MCP query never waits on the daemon, and the daemon never
blocks on an editor.

## Next

- [Data model](/docs/how-it-works/data-model/) — collections, bookmarks, chunks
- [Indexing pipeline](/docs/how-it-works/indexing-pipeline/) — from save to searchable
- [Security model](/docs/how-it-works/security/) — API token, SSRF, trust boundaries
