cairn
Local, symbol-aware code index. A daemon-backed index for fast,
structural code search across the repos you've registered — definitions,
references, impls, imports, source bodies — with no external service.
Status: 0.1.0. Wire schemas (JSON-RPC + MCP), on-disk format,
and CLI flags follow SemVer 0.x rules — minor releases may break
compatibility. 1.0 will tag once these surfaces stabilize.
Why
Editors and AI agents need to look up code by structure (the function
foo, callers of bar, what Display is implemented for), not by
fuzzy text. Cairn keeps that index always-current on a local daemon so
each lookup is sub-100 ms — no IDE waking up, no network hop, no
service to subscribe to. The wire surface is JSON-RPC over a Unix
domain socket; a stdio MCP front-end ships in the same binary so
Claude Code and friends can use it directly.
Architecture
Storage is content-addressed, modelled on git's object store:
| layer | identity | what it holds |
|---|
| blob | (blob_sha, parser_id) | parsed symbols / refs / imports / impls of one file's bytes |
| manifest | manifest_id | {(path, blob_sha)} at one point in time |
| anchor | name (HEAD, branch/<n>, tag/<n>, tentative/<id>) | named pointer to a manifest |
blob_sha is git's blob hash, so the same file content parses once
and is shared across every branch / tag / worktree that references
it. Switching branches re-binds anchors instead of accreting per-branch
databases. Detached HEAD checkouts don't create snapshot rows.
branch/<n> and tag/<n> anchors track live git refs: a ref that
disappears from git for-each-ref (deleted branch, expired tag) is
pruned from the anchor table on the next register / reindex pass,
so cairn ctl status and list_repos don't keep stale labels.
HEAD and tentative/<id> are not subject to that prune.
Substrate sources:
anchor.rs,
manifest.rs,
cas/blob.rs,
cas/schema.rs.
Installation
Homebrew (macOS)
brew tap naoto256/cairn
brew install cairn
Debian / Ubuntu
Download cairn_<version>-1_amd64.deb from the
latest release,
then:
sudo apt install ./cairn_*.deb
Prebuilt binary (any OS / target in the matrix)
Download the tarball for your target from
Releases, extract,
and put cairn on your PATH:
tar -xzf cairn-v<version>-<target>.tar.gz
install cairn-v<version>-<target>/cairn ~/.local/bin/
From source (Rust 1.85+, working git)
cargo install --git https://github.com/naoto256/cairn cairn
Optional runtime dependencies for Tier-3 cross-file resolution:
rust-analyzer, pyright-langserver, gopls (see Languages
section).
Why no crates.io?
Cairn is intentionally not published to crates.io. The names cairn,
cairn-cli (the historical workspace crate name, now renamed to
cairn), and cairn-core are already held by unrelated projects (a
placeholder, an autonomous-settlement CLI, and a knowledge-provenance
index, respectively), and crates.io's flat, first-come-first-served
namespace makes a clean, brand-aligned publish impractical.
Distribution through Homebrew and prebuilt binaries serves users better
in this case.
Use
Daemon
cairn daemon
Runs in the foreground. Data dir defaults to
~/Library/Application Support/cairn/ on macOS and
$XDG_DATA_HOME/cairn/ on Linux. Sockets land in
~/Library/Caches/cairn/ (macOS) or $XDG_RUNTIME_DIR/cairn/ (Linux),
both clamped to mode 0700.
Run it in a separate terminal (or via brew services start cairn on
macOS, or the systemd user unit in dist/deb/systemd/cairn.service on
Linux) before any cairn ctl or cairn query command.
Sample LaunchAgent plist and systemd user unit live in
contrib/.
Register a repo
cairn ctl register-repo --alias my-proj /path/to/repo
cairn ctl status
A repo can carry more than one alias; removing one keeps the on-disk
store alive while any other label still references it.
Query
cairn query find <name> [--repo <alias>] # symbol by name
cairn query refs <name> [--repo <alias>] # callers / use sites
cairn query source <qualified-name> --repo <alias> # source body
cairn query outline <alias> <file> # per-file outline
cairn query impls --type <T> [--repo <alias>] # what T implements
cairn query impls --trait <T> [--repo <alias>] # what implements T
cairn query imports --file <path> [--repo <alias>] # use / import edges
cairn query repos # registered repos