← Back to portfolio
L
Case Study6 min read

A vault that agents
read by query, not grep.

An all-Rust engine that indexes a plain-markdown vault into SQLite and serves it three ways: an MCP server for Claude, a CLI, and a native desktop app for humans. Open source, shipped, signed and notarized. It is indexing this site’s repository right now.

15
MCP tools
3
Surfaces
1
Rust core
v0.2
Signed release
Scroll to read
00The Problem

An agent can’t see the shape of a repo.

The studio runs on a markdown vault: hundreds of notes, specs, plans, and memory files linked with [[wikilinks]] and YAML frontmatter. When an agent needs to find something in it, the only tools it has are find and grep. Both return noise, and both burn context on a repository whose structure the model can’t see.

Obsidian solves the human side with a graph and backlinks, but that is built for a person clicking, and it is a third-party app with no machine API. I wanted one engine serving both audiences: a query API for agents, and a native reader and editor for me, over the exact same plain-markdown files.

01The Engine

One Rust core, a graph derived from the files.

Lattice is a single Rust core crate with no I/O of its own. It walks a vault, parses every markdown file, and builds a graph: each file is a node (a README is an index, everything else a note), and three kinds of edge connect them. Wikilinks, frontmatter references, and the contains tree from a directory’s README down to the files beneath it.

The graph lives in SQLite with an FTS5 full-text index, but that database is only a derived cache. The .md files are the source of truth. Delete the index and Lattice rebuilds it from disk.

Tech Stack
Rustrusqlite (SQLite + FTS5)comrakserde_yamlignoreblake3
How it works
Walk the vault, parse frontmatter, links and headings, upsert nodes. Link resolution is a second pass, since a target can be defined after the note that references it.
Why it matters
The index is rebuildable and disposable. Nothing about your knowledge is locked inside Lattice. It stays plain markdown on disk.
In practice
An .aiignore (gitignore syntax) keeps system files out of the graph entirely.
02The AI Surface

Agents query the graph instead of grepping.

Agents don’t browse a vault, they query it. Lattice exposes the same core engine as an MCP server (over stdio, drops straight into Claude Code) and a matching CLI, both returning one JSON shape. An agent asks for backlinks, outbound links, a frontmatter filter, full-text search, orphans, or broken links, and gets structure back instead of a wall of grep output.

The headline tool is vault_context_bundle: give it a note and a token budget, and it returns that note plus its most relevant neighbors, deduped and ranked, packed to fit. Budgets are soft, and a manifest lists exactly what was included and what was dropped. No silent truncation.

Tech Stack
rmcp (Rust MCP SDK)claptiktoken-rsstdio transport
How it works
15 tools over one JSON contract shared by the MCP server and the CLI. Navigation is read-only; structured edits (rename a note and fix every backlink, patch frontmatter) are separate and dry-run by default.
Why it matters
An agent spends its context on reasoning, not on grepping a repo it can’t see the shape of. It asks for the slice it needs and gets it.
In practice
These are the vault_* tools running in the session that wrote this page.
03The Human Surface

A native app over the very same files.

Humans read and edit the same vault through Lattice’s own native window: a Rust backend (the core crate, exposed as Tauri commands) with a SvelteKit webview. No Electron, no bundled runtime, one binary.

It browses notes with backlinks and outbound-link panels, turns the graph queries (orphans, broken links, frontmatter filters, search) into first-class views, and edits markdown in a CodeMirror editor that writes straight back to the .md file. Markdown is rendered once, by the Rust comrak path, so the app and the agents see the same content.

Tech Stack
Tauri 2SvelteKitCodeMirror 6comrak → HTMLammonia
How it works
Saving invokes a Tauri command that writes the file; the index re-syncs it like any other change. If the file changed on disk since load, the editor warns and offers a diff before overwriting.
Why it matters
One vault, two audiences. The human gets an Obsidian-shaped reader and editor; the agent gets a query API. Neither is a second-class citizen.
In practice
Light and dark, shipped as a signed .app and .dmg.
04Engineering For Trust

Never a stale answer, never a corrupted note.

A tool whose whole value is freshness can never serve a silently-stale answer, and a tool that writes to your notes can never corrupt them. Two decisions carry that weight.

The first index design rebuilt on every call (about 9 seconds) or watched the filesystem with notify. The watcher proved unreliable inside a piped MCP server: the spawned thread never received filesystem events, and a silently-stale index is the worst possible failure. Lattice holds the index in memory and does a cheap incremental re-sync on each call instead: walk, compare mtimes, reparse only what changed. Six calls plus startup dropped from about 9 seconds to about 1.3, and the answer is always current.

The second: the frontmatter patcher refuses to touch what it can’t safely round-trip. Hand it a block-style YAML list or CRLF it can’t parse cleanly, and it returns the file unchanged with a warning rather than risk corrupting it.

Tech Stack
Arc<Mutex<Vault>>lazy revalidationblake3 hashingdry-run default
How it works
Every write is structure-only, dry-run by default, and never touches git. A blake3 body hash skips no-op writes; queries warn when the index lags the disk.
Why it matters
Data safety beats features. The contracts (structure-only, dry-run, never manufacture rot) are load-bearing, and every new write feature is weighed against them.
In practice
Both decisions came out of dogfooding plus an external code review.
05Shipped

Public, tested, signed and notarized.

Lattice is a public repository, not a private experiment. GitHub Actions runs cargo test, cargo clippy, cargo fmt, and the Svelte build on every push, and the green checks are part of the point.

v0.2.0 ships as a real macOS app: Developer ID signed, Apple-notarized, and stapled, so it opens with a clean double-click and no quarantine warning. And it earns its keep by dogfooding. The vault it indexes is this studio, the same repository that holds the page you are reading.

Tech Stack
GitHub ActionsDeveloper ID signingApple notarizationDMG
How it works
A release script runs the full sign, notarize, and staple chain; the app and dmg both verify offline.
Why it matters
Open source, shipped, and signed is a different claim than a demo. The whole thing is buildable and inspectable.
In practice
github.com/anrasch/lattice, v0.2.0, 15 MCP tools.
The Bigger Picture

The studio’s
knowledge layer.

The harness operates the studio; Lattice is how it remembers and navigates. One is the hands, the other is the memory. Both are tools I built because the ones I needed didn’t exist yet.