Architecture

Bun, SQLite, local GGUF models via node-llama-cpp, and one retrieval core shared by CLI, Web UI, SDK, REST, and MCP.

GNO is a TypeScript CLI package executed by Bun, with a SQLite-backed index, local model inference via node-llama-cpp, and a shared retrieval core that every surface (CLI, Web UI, SDK, REST API, MCP) plugs into.

Runtime

Storage layout

Surfaces

Every GNO surface speaks to the same shared retrieval core:

Data flow

  1. Ingestion — the file walker reads sources, parsers extract text and frontmatter, export adapters split containers into logical records, and the chunker splits documents into retrievable units. Code files get structural first-pass chunking for TypeScript, JavaScript, Python, Go, and Rust.
  2. Mirroring — every source is normalized into a canonical mirror: LF line endings, NFC text, stable line numbering. All coordinates in the system address the mirror, not the original bytes, which is what makes “lines 40 to 48” mean the same thing for a Markdown file and a converted PDF.
  3. Embedding — chunk text is encoded to dense vectors by the active embedding model, prefixed with the document title so the vector carries context beyond the chunk itself.
  4. Indexing — chunks land in SQLite FTS5 and sqlite-vec tables; documents, links, tags, and typed edges land in relational tables. Change detection is SHA-256 per file, so unchanged files are skipped.
  5. Retrieval — the query enters the pipeline, hits BM25 and vectors, optionally expands one hop through the graph, merges by reciprocal rank fusion, reranks with a cross-encoder, and returns.
  6. Compilation or answer — a Capsule request adds facet derivation, coverage-driven selection, and global budgeting on top of retrieval. ask sends the selected evidence to the generation model with a citation-preserving prompt, and --verify adds a claim-classification pass against the closed Capsule.

Identity and hashing

Three hashes exist for every piece of evidence, and the distinction runs through the whole system:

Keeping them separate is what lets verification say “the file changed but your cited paragraph did not”. Document IDs are derived from the source hash, so identity is content-addressed rather than path-addressed.

The resident runtime

gno serve and gno daemon are two front ends for one resident runtime: a single exclusive owner per data directory, holding the store, the watcher, the job queue, the model lifecycle, and a Streamable HTTP MCP gateway at 127.0.0.1:3000/mcp.

Because it is one runtime, several MCP clients share one warm store and one set of loaded models while keeping isolated sessions, cancellation, and configuration snapshots. The second agent to connect does not pay the first agent’s model load. Exclusivity is enforced: a second process against the same data directory is rejected rather than racing.

Shutdown is ordered rather than abrupt. New work is rejected, admitted requests are cancelled and awaited, background embedding and model downloads drain, MCP sessions close, then models, SQLite, and the ownership lock are disposed.

Cross-cutting gates

Three checks sit between a request and an effect, and they are independent by construction:

Ports and adapters

The core defines ports — converter, store, embedding, rerank, generation — and adapters implement them. That is why an OpenAI-compatible HTTP endpoint can substitute for a local GGUF per model role without the retrieval core knowing, and why a per-collection embedding override is a configuration change rather than a code path.

┌─────────────────────────────────────────────────┐
│      CLI / MCP / Web UI / REST / SDK / Desktop   │
├─────────────────────────────────────────────────┤
│  Ports: Converter, Store, Embedding, Rerank, Gen │
├─────────────────────────────────────────────────┤
│  Adapters: SQLite, FTS5, sqlite-vec, llama.cpp,  │
│            OpenAI-compatible HTTP                │
├─────────────────────────────────────────────────┤
│  Core: Identity, Mirrors, Chunking, Retrieval,   │
│        Capsules, Journal, Egress                 │
└─────────────────────────────────────────────────┘