<!-- nornir:class: history -->
# Airgapped-agent guide
How to drive a **local, offline model** over a large Rust workspace using nornir
as the ground-truth provider — "go through the source and crates, find
dependencies / function calls" without the model ever walking raw source (where
it hallucinates edges). This is the practical companion to the *Explore* and
*Architecture* chapters in [design.md](design.md).
## The principle: tooling-first, model-second
A small local model is good at *summarizing and navigating* structured facts and
bad at *deriving* them from raw code. So don't ask it to "read the repo and work
out the call graph" — point it at nornir's MCP server and let it **query**. nornir
already holds every projection (deps, calls, symbols, full-text, vectors) keyed by
git SHA, served over MCP for exactly this. Reserve a frontier model (e.g. Opus)
for judgement; offload "index it, list it, find it" to the local one.
## Model + harness (as of mid-2026)
- **Model (24 GB VRAM, agentic tool-calling):** Qwen3-Coder-Next (MoE, ~256K
context, fits a single 24 GB GPU) or Qwen3.6-27B (Apache-2.0, first-class
tool-calling). Both are strong on Rust for mechanical work.
- **Harness:** Aider / opencode / Cline over Ollama or vLLM. (Claude Code is
Anthropic-only.) Point the harness's MCP client at a running `nornir-mcp`.
- nornir stays **embedded or client, never both on one warehouse** — see the lock
rule in [design.md](design.md). For a shared/served setup, run `nornir-server`
and let the agent host talk to it.
## The query surface (what the model calls)
Start every session — and after any context reset — with orientation, then drill
in. None of these require a checked-out binary except where noted.
**Orient**
- `repo_overview <repo>` — one call: internal `depends_on` + `dependents` (with the
crates that justify each edge), build-order position, and a knowledge digest
(symbol + call-edge counts, sample symbol names). The cheapest "where am I".
**Crate dependencies (Mímir)**
- `deps_of` / `dependents_of` — direct neighbours.
- `affected_by_change <repos>` — the invalidation set (changed + transitive
dependents) in build order — the re-check set for a release.
- `build_order` · `dep_path <from> <to>` (why does A depend on B?) ·
`external_dep_users`.
**Function calls & symbols**
- From **syn** (source, always available once scanned): `knowledge_callers` /
`knowledge_callees` / `knowledge_call_path` / `knowledge_symbol_lookup` /
`knowledge_defined_in`.
- From **DWARF** (binary truth, persisted — see below): `dwarf_callers` /
`dwarf_callees` / `dwarf_call_path` / `dwarf_symbol_lookup` / `dwarf_defined_in`.
Each takes an optional `sha` for time-travel. Inlined edges only.
**Search**
- `search` (BM25 lexical) and `vector_search` (semantic; hybrid = RRF of both).
## Populating the facts (operator, once per repo/release)
The query tools read what's been persisted. Seed it with:
```sh
nornir map <repo> # syn scan + workspace index build + snapshot
nornir introspect symbols --persist \
--repo <repo> target/debug/<bin> # DWARF symbols + callgraph → dwarf_* tables
```
`nornir map` is the one-shot for the syn/knowledge + full-text side;
`introspect symbols --persist` adds the DWARF side from a debug-info binary (keyed
to the member's HEAD SHA). Both write to the warehouse, so every later query — and
every past SHA — is a warehouse read, no re-scan.
## Why this beats raw-source RAG
- **No hallucinated edges.** Call/dep graphs come from `cargo metadata`, `syn`, and
DWARF — not the model's guess.
- **Fits the context budget.** The model pulls a few hundred bytes of answer per
query instead of dumping files; a 24 GB model with a modest KV cache is plenty.
- **Time-travel for free.** Every fact is SHA-keyed, so "as of release X" is just a
`sha` argument.
- **Airgapped.** No code leaves the machine; the model only ever sees query results.