Skip to main content
Everything base knows sits in one graph. Not four stores with four query languages, one graph, so a decision can point at the project it was made for, that project can point at the folder it lives in, and the folder’s code map can point back. The connections are the reason a question about one thing can return the thing next to it.

Four kinds of thing

Your code. Every function, struct, class, import and call relationship, extracted by tree-sitter across more than thirty-five languages. “What calls this?” becomes a query instead of a search across files. Your work. Projects with their milestones and tasks. Decisions with the reasoning that produced them. Rules that fire when the situation matches. This is the material that otherwise lives in somebody’s head, or in a wiki that was last accurate in March. Your documents. Markdown with frontmatter becomes connected nodes, and headings, links, wikilinks and tags all become edges. Your operations. Which domains are active, what changed since the last session, what has gone stale, what is still open.

What it is made of

The graph is a set of RDF quads in NQuads format, stored in a plain-text file called graph.nq. Queries are SPARQL, run through an embedded Oxigraph store that base loads from that file on each invocation and writes back atomically. Three consequences follow from that choice, and they are the reason it was made:
  • It diffs. graph.nq is text in your repository. A commit that changes what base knows shows up in review like any other change.
  • Nothing is behind a network call. There is no server to start, no database to keep running, and no request that can time out mid-turn.
  • base is the only writer. Every write goes through the same path, which is what makes the change log below complete rather than best-effort.
Never hand-edit graph.nq. The maintenance commands are atomic and snapshot before they write; a text editor is neither, and a half-written quad file fails to parse rather than failing gracefully. Use base graph compact, base graph purge and base doctor --repair instead.

The change log

Every successful write is appended to a log that sits beside the graph it describes, as JSON.
The cursor is a byte offset into the log, not a sequence number. That is a deliberate choice with a practical payoff: it needs no separate counter to stay in step, several writers appending at once do not break it, and a reader resumes from exactly the value it was handed. Take the cursor, do your work, then ask for everything since.

The code map is separate

Code entities do not go into graph.nq. Each app gets its own map at <app>/.base-ast/ast.ttl, registered in the workspace graph but stored on its own. That separation is worth understanding, because it explains behaviour you will otherwise find surprising. A workspace with three apps has three maps rather than one shared index, which is why base ast query needs --target to reach a map other than the one where you are standing. And .base-ast/ writes its own .gitignore to exclude itself, so a code map does not travel with the repository. Whoever clones it regenerates their own with base sync --ast.

Asking it things directly

The graph is queryable on its own terms, not only through what arrives automatically.
The first two search one kind of thing each. base graph query retrieves a subgraph and synthesizes an answer over it. The last three walk the structure directly, which is what you want when you would rather see how things connect than read a summary of it.

Next

Domains and triggers

How base decides which slice of the graph is relevant right now.

Where things live

Every file base writes, and which of it travels with a repository.