{
  "markdown": "<img src=\"docs/assets/mark-animated.svg\" width=\"110\" align=\"right\" alt=\"\" />\n\n# mushroomdb\n\n[![stars](https://img.shields.io/github/stars/MatthewSherlin/mushroomdb?style=flat&logo=github)](https://github.com/MatthewSherlin/mushroomdb/stargazers)\n[![crates.io](https://img.shields.io/crates/v/mushroomdb-cli?logo=rust&label=crates.io)](https://crates.io/crates/mushroomdb-cli)\n[![npm](https://img.shields.io/npm/v/mushroomdb?logo=npm&label=npm)](https://www.npmjs.com/package/mushroomdb)\n[![PyPI](https://img.shields.io/pypi/v/mushroomdb?logo=python&logoColor=white&label=PyPI)](https://pypi.org/project/mushroomdb/)\n[![CI](https://img.shields.io/github/actions/workflow/status/MatthewSherlin/mushroomdb/ci.yml?branch=main&label=CI)](https://github.com/MatthewSherlin/mushroomdb/actions/workflows/ci.yml)\n[![license](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)](#license)\n\n**The graph that stays true — and knows who's allowed to see it.**\n\nAn embedded Rust graph database where edges are a schema declaration: write a rule once, and\nevery write creates, maintains, and retracts the matching edges. Ships a 24-tool MCP server and\na live graph of the repository it is pointed at.\n\n*Pre-1.0 alpha — APIs and formats may change between minor versions.*\n\n[Docs](docs/site/index.md) · [Changelog](CHANGELOG.md) · [Issues](https://github.com/MatthewSherlin/mushroomdb/issues)\n\n![Ingest this repository, query which files co-change, hand one file to a new owner, and watch the KNOWS edges follow in the same write](docs/assets/ingest-git-cascade.gif)\n\n## Quick start\n\nInstall the Claude Code plugin, from any directory:\n\n```sh\nclaude marketplace add MatthewSherlin/mushroomdb\nclaude plugin install mushroom@mushroomdb\n```\n\nOpen the repository you want graphed and type `/mushroom:mushroom`. The skill builds the graph on\nfirst use and answers with it from then on.\n\nOr install into one project (or your home directory) without the plugin — same skill, invoked\nbare as `/mushroom`:\n\n```sh\nnpx mushroomdb install    # /mushroom skill + MCP server + prompt, post-edit and git hooks\n```\n\nEither way, the first thing the assistant does is read the repository back to you. This is a real\nrun against this repository — `ingest-git` took 2.5 s, `map` 0.18 s:\n\n```text\nmushroomdb map — 431 files, 6,226 symbols, 652 commits, 2 authors · synced 3s ago at 94719fe\nclusters (co-change + imports)\n  1. <mixed> crates, tests  (86 files, cohesion 0.73)  crates/server/tests/http.rs, algo.rs, crates/server/src/http.rs\n  2. <mixed> crates, src  (45 files, cohesion 0.67)  pack.rs, lib.rs, types.rs\n  3. ui src, e2e  (26 files, cohesion 0.89)  api.ts, store.ts, classify.ts\n  4. crates/code-extract tests, fixtures  (21 files, cohesion 0.99)  lib.rs, extract.rs, mod.rs\n  5. ui fonts, public  (18 files, cohesion 0.89)  IBMPlexMono-Medium.woff2, IBMPlexMono-Regular.woff2, IBMPlexSans-Medium.woff2\n  6. crates/core-api src, repograph  (17 files, cohesion 0.72)  facts.rs, render.rs, context.rs\n  7. <mixed> crates, bindings  (16 files, cohesion 0.99)  crates/core-bench/Cargo.toml, package.json, crates/sim-harness/Cargo.toml\n  8. benchmarks adapters, results  (15 files, cohesion 1.00)  run_handrolled.py, datasets.py, handrolled.py\nkey files (most depended-on)\n  crates/code-extract/src/lib.rs 0.05 · crates/server/tests/http.rs 0.04 · crates/code-extract/tests/extract.rs 0.04 · crates/core-api/tests/algo.rs 0.04 · crates/server/src/http.rs 0.03\nowners\n  Matthew Michael Sherlin 431 files\nhot (last 90 days)\n  crates/core-api/src/db.rs 175 · README.md 109 · crates/core-rules/src/engine.rs 56 · crates/core-query/src/cypher/exec.rs 54 · crates/cli/src/lib.rs 51\nask me: why does lib.rs co-change with extract.rs? · who owns ui? · what imports http.rs?\n```\n\nFrom there: `context` for one file or symbol from every side, `impact` before an edit, `owners`,\n`why` with the commits that prove a link, `recall` and `remember` for durable notes.\nFull walkthrough: [`docs/site/code-graph.md`](docs/site/code-graph.md).\n\n- **Live, not a snapshot.** One `SET f.top_author_id = …` moves the `TOP_AUTHOR` edge *and*\n  re-derives that author's `KNOWS` edges before the write closes — the `SET` in the GIF above is\n  that one write. An editor hook does the same for your code: `touch` re-extracts an edited file\n  after every `Edit`, `Write` and `MultiEdit`, in about 180 ms on this repository's graph.\n- **Retracts instead of going stale.** Re-run `ingest-git` to sync: only new commits replay,\n  deleted files drop their derived edges, and renamed files carry their history to the new path.\n- **Explains any link.** `explain` names the rule and the score behind an edge, so *\"which files\n  change together with `src/api.rs`, and why?\"* has an answer your assistant can quote instead of\n  a guess.\n- **Knows who's allowed to see it.** Pass a `mask` with a query and the same graph answers\n  differently per caller; write statements are rejected on masked queries.\n- **Answers what it said last week.** `mushroomdb asof ./db --commit 5 --query \"…\"` replays the\n  WAL to a past commit, derived edges included.\n\n## Where it fits\n\n**What it is**\n\n- An embedded, single-binary graph database with a rule engine that maintains edges for you.\n- A 24-tool MCP server plus a `/mushroom` skill and a Claude Code plugin.\n- Safe for several processes at once: one writer at a time behind an advisory `LOCK` file, any\n  number of readers, and every handle picks up a peer's commits by `refresh()` rather than\n  reopening — so a running `serve`, an editor hook, a git hook and a CLI command can share one\n  store. [`docs/site/concurrency.md`](docs/site/concurrency.md)\n- Local-first: your data stays on disk, no cloud service, no LLM in the write path.\n\n**What it isn't**\n\n- Not a hosted memory service — there is no account, no endpoint, nothing to sign up for.\n- Not a vector database. Vector predicates and HNSW are built in; bring your own embeddings.\n- Not a Postgres replacement. Single writer, no interactive transactions, memory-first storage.\n\n---\n\n## The differentiator\n\nMost graph databases require you to create edges manually or run a batch similarity script after\neach load. mushroomdb makes edge creation a schema declaration. A rule like \"connect every Person\nto every Org whose `skills` list overlaps theirs by at least 50%\" is written once:\n\n```rust\ndb.create_rule(RuleDef {\n    name: \"skill_fit\".into(),\n    src_label: \"Person\".into(),\n    dst_label: \"Org\".into(),\n    predicate: Predicate::Overlap { field: \"skills\".into(), min: 0.5 },\n    edge_type: \"FIT\".into(),\n    weight_prop: Some(\"score\".into()),\n    max_edges: Some(5), // keep the 5 best-matching Orgs per Person (top-k per source)\n}).expect(\"rule\");\n```\n\nAfter that, every `insert_node` and `set_prop` evaluates the rule incrementally. The engine writes\nthe edge, stores the Jaccard score, and retracts the edge if the properties later diverge — without\nany manual work.\n\nWatch it live — a Cypher `SET` changes one property, the `founded_within` rule fires, and new\nscored edges appear in the bundled explorer:\n\n![A SET statement deriving new scored edges live](docs/assets/demo-set-derives.gif)\n\nOpen the Rules panel, and the Why slide-over shows the exact predicate arithmetic behind every\nderived edge:\n\n![Rules panel and Why slide-over showing overlap arithmetic](docs/assets/rules-why.gif)\n\n### Predicates\n\nSix predicate kinds ship today. They compose via `All(...)` (AND, score = min) and `Any(...)`\n(OR, score = max), nested up to depth 4.\n\n| Predicate | What it tests |\n|---|---|\n| `KeyMatch` | FK equality — source field matches destination key |\n| `FieldEqual` | Exact match on a named scalar field (string, int, float, bool) |\n| `Overlap` | Jaccard on list-valued fields, min threshold |\n| `NumericWithin` | Absolute numeric difference within a tolerance; score = `1 - |Δ|/tolerance` |\n| `GeoRadius` | Haversine distance on `[lat, lon]` fields within km; score = `1 - dist/radius` |\n| `VectorSimilar` | Cosine similarity on float arrays, min threshold |\n\nAuto-FK: fields ending in `_id` whose values match existing node keys get `KeyMatch` rules created\nautomatically at ingest time. `VectorSimilar` accepts `approximate: true` to switch candidate\nselection to in-tree HNSW (per-query recall min 0.90, mean 0.998 at 5k nodes / dim 1536,\nfixed-seed probe). Full reference: [`docs/site/rules.md`](docs/site/rules.md).\n\n### Built on the same engine\n\n- **Live subscriptions.** `subscribe_rule` (Rust) and `GET /subscribe` (WebSocket) stream\n  `EdgeFired` / `EdgeRetracted` the moment they hit the WAL — not polled, not batched. Bounded\n  65,536-event queue; slow consumers get a `Lagged { missed: N }` marker instead of a disconnect.\n  [`docs/site/subscriptions.md`](docs/site/subscriptions.md)\n- **Rule attribution across time.** Every derived edge writes a HISTORY-MARKER WAL record carrying\n  the rule name, so `edge_history`, `node_history`, and `was_linked` answer *which* rule created a\n  link and *at which commit*. `GraphDb::open_at(&dir, 5)` replays to a past commit, derived edges\n  included; out-of-range commits return `CommitOutOfRange`, never wrong data.\n  [`docs/site/timetravel.md`](docs/site/timetravel.md)\n- **Materialized views.** Degree counts and neighbor aggregates (sum/avg/min/max) maintained\n  incrementally on every edge change — no cron, no triggers, no stale caches.\n  [`docs/site/views.md`](docs/site/views.md)\n- **Rule suggestions.** `db.suggest_rules()` (or `mushroomdb suggest ./db`) profiles your data and\n  ranks candidate rules with estimated edge counts and rationale. Seeded sampling, so the same\n  database always returns the same suggestions. No rule is ever applied automatically.\n  [`docs/site/suggest.md`](docs/site/suggest.md)\n\n---\n\n## Agent memory\n\nGraph structure captures the shape of real knowledge — entities, associations, similarity, and\nlineage — and rule-derived edges keep those associations fresh as new facts arrive.\n\n- Entities map to nodes (`Person`, `Document`, `Project`, `Concept`, …).\n- Associations are edges derived from data: cosine similarity on embeddings, shared field values,\n  FK relationships, geographic proximity. Declare a rule once; every write maintains the matching\n  edges without agent-side bookkeeping.\n- Recall has three modes: `find_similar` by query vector (HNSW when available, brute force\n  otherwise); `find_similar` by key (neighbors along a rule-derived edge type); `query` for\n  structured Cypher recall. `hybrid_search` fuses fulltext and vector results via Reciprocal Rank\n  Fusion.\n- Explanations are built in: `explain_association` shows which rules and scores produced each\n  link, so an agent can cite evidence instead of asserting a conclusion.\n- Node masks are the ACL primitive: pass `mask: [key1, key2, …]` to `query` to restrict the visible\n  node set. Write statements are rejected on masked queries.\n  [`docs/site/masks.md`](docs/site/masks.md)\n- Schema-as-code: `mushroomdb schema apply <dir> <schema.json>` idempotently applies rules, views,\n  and fulltext indexes, printing a created/updated/unchanged diff.\n\n**Minimal workflow** (four tool calls):\n\n```text\nupsert_entity  →  create_rule  →  find_similar  →  explain_association\n  (store)           (link)           (recall)          (explain)\n```\n\n**Eight task tools** answer a question about the repository in one call. They are what the skill\nreaches for, and what `tools/list` shows first:\n\n| Tool | Purpose |\n|---|---|\n| `map` | The repository in one screen: size, last sync, clusters, key files, owners, hot files |\n| `context` | One file or symbol from every side: signature, source, callers, callees, importers, co-change partners, commits, notes |\n| `impact` | What changing these files reaches: partners with scores, importers, symbols other files call, owner. Defaults to the working tree's diff |\n| `owners` | Top author and share, who else knows the file, last touch, the split by quarter |\n| `why` | Every rule edge between two nodes with its evidence, or the shortest path when there is none |\n| `recall` | Notes, concepts, files, symbols and people nearest a topic, each with its strongest link |\n| `remember` | Write a note into the graph and return its key |\n| `sync` | Bring the store up to date: commits since the last sync, then the dirty working tree |\n\n**The sixteen graph tools** reach the store directly. Their descriptions are prefixed `Advanced:`\nin `tools/list`, so an assistant knows which surface is the front door:\n\n| Tool | Purpose |\n|---|---|\n| `upsert_entity` | Insert or update a node by key (no existence check needed) |\n| `ingest_json` | Batch-ingest nodes of one label from a JSON array |\n| `create_rule` | Declare a derivation rule; backfills existing nodes immediately |\n| `find_similar` | Find similar nodes by query vector (HNSW) or by derived edge traversal |\n| `hybrid_search` | RRF over fulltext + vector results |\n| `explain_association` | Show rules and scores that link two nodes |\n| `explain` | Alias for `explain_association` |\n| `query` | Cypher query (read or write); pass `mask` for ACL-scoped read |\n| `neighborhood` | Multi-hop neighborhood traversal with optional edge-type filter |\n| `node_info` | Return a node's key, label, and properties |\n| `node_edges` | Return all edges incident on a node |\n| `stats` | Live node, edge, and rule counts |\n| `node_history` | WAL change history for a node (since last truncating snapshot) |\n| `edge_history` | Add/retract lifecycle for edges between two nodes, with rule attribution |\n| `was_linked` | Point-in-time edge check: was an edge active at a given commit? |\n| `rename_node` | Rename a node's key; old_key, new_key |\n\nFull walkthrough, tool reference, and Claude Desktop setup: [`docs/site/mcp.md`](docs/site/mcp.md).\nSkill, plugin, and hook details: [`docs/site/skill.md`](docs/site/skill.md).\n\n---\n\n## Install options\n\n```sh\nclaude plugin install mushroom@mushroomdb   # after `claude marketplace add MatthewSherlin/mushroomdb`\nnpx mushroomdb install            # skill + MCP server + hooks, no toolchain needed\ncargo install mushroomdb-cli      # `mushroomdb` binary from crates.io (no embedded UI)\ncargo add mushroomdb              # embedded Rust library\npip install mushroomdb            # Python bindings\n```\n\n`install` writes an MCP entry that runs `npx -y mushroomdb@<version>`, so the assistant needs\nnothing installed globally and nothing is copied into your home directory. Point it at a local\nbuild with `--command <path>`. `mushroomdb doctor` verifies the result end to end — config entry,\nstore, lock, hooks, git hooks, and a real stdio handshake with the configured command.\n\nTo see the bundled explorer, write a demo graph and serve it:\n\n```sh\nmushroomdb demo ./db\nmushroomdb serve ./db\n```\n\nOpen `http://127.0.0.1:8080/`. The demo graph has 10 Orgs, 20 Projects, 30 People, and 334\nedges — 304 of them derived by seven rule sets. When a token is configured, open\n`http://host:8080/?token=…`. Building the binary with the UI embedded, Docker, and the\n`install.sh` script are covered in [CONTRIBUTING.md](CONTRIBUTING.md).\n\n**Role-bound tokens** limit a caller to a named subset of nodes. Define roles in `schema.json`\nunder the `roles` key (each role has a `label` selector list), then pass `--role-token TOKEN:ROLE`\n(repeatable) when starting the server, or set `MUSHROOMDB_ROLE_TOKENS=\"tok1:role1,tok2:role2\"`.\nA role token receives only the nodes matching its label selectors — read endpoints return rows\nfiltered to the visible set; write, subscription, and analytics endpoints return 403. Unknown token\nor role name: 401. The never-widen invariant is enforced in the server: a client-supplied mask is\nalways intersected with the role mask. The MCP interface (`mushroomdb mcp`) is a stdio JSON-RPC\nserver for local agent use and is not subject to bearer-token or role enforcement.\n\n---\n\n## CLI reference\n\n| Command | What it does |\n|---|---|\n| `mushroomdb install [--platform claude-code\\|cursor\\|codex\\|all] [--project\\|--user] [--db <path>] [--command <path>] [--no-git-hooks] [--no-prewarm]` | Write the `/mushroom` skill + MCP server entry + prompt, post-edit and git hooks. Auto-detects platform and scope |\n| `mushroomdb uninstall [--platform …] [--project] [--db <path>]` | Remove exactly what `install` wrote (manifest-driven; leaves user files) |\n| `mushroomdb doctor [--project\\|--user] [--platform …]` | Verify an install: config entry, npx reachability, store, lock, hooks, git hooks, a real stdio handshake, and duplicate-scope servers. Exit 1 on any `fail` |\n| `mushroomdb ingest-git <dir> <repo> [--exclude <pattern>]... [--prs] [--no-structure] [--no-docs] [--ensure-gitignore]` | Graph a git repository: `Author`, `Commit`, `File`, `Symbol` nodes plus `CO_CHANGED`, `KNOWS`, `IMPORTS`, `CALLS` and `MENTIONS` rules. Re-run to sync. See [`docs/site/ingest-git.md`](docs/site/ingest-git.md) |\n| `mushroomdb map <dir> [--json]` | The repository in one screen: clusters, key files, owners, hot files, and three questions worth asking |\n| `mushroomdb context <dir> <target>` | One file or symbol from every side. `<target>` is a path, a symbol key, or a bare symbol name |\n| `mushroomdb impact <dir> <file>...` | What changing these files reaches: co-change partners, importers, and the symbols other files call |\n| `mushroomdb owners <dir> <path>` | Top author and share, who else knows it, last touch, the last four quarters |\n| `mushroomdb why <dir> <a> <b>` | Every rule edge between two nodes with its evidence, or the shortest path between them |\n| `mushroomdb sync <dir> [--json]` | Re-sync the repository the store was built from: new commits, then the working tree where it differs from `HEAD`. Takes no repo argument — reads it off the graph. `--json` prints the counts as one object |\n| `mushroomdb touch <dir>\\|--auto [<file>...]` | Re-extract just these files. With no `<file>` reads them from a `PostToolUse` payload on stdin (hook body) |\n| `mushroomdb recall <dir>\\|--auto` | Hook body for the `/mushroom` skill's `UserPromptSubmit` recall hook: reads a prompt payload on stdin, prints related graph facts. Wired automatically by `install` |\n| `mushroomdb mcp <dir>\\|--auto` | Start a stdio MCP JSON-RPC server for agent tools |\n| `mushroomdb demo <dir>` | Write a deterministic demo graph (10 Orgs, 20 Projects, 30 People) |\n| `mushroomdb serve <dir>` | Start the HTTP server + optional UI (default `127.0.0.1:8080`; `--token` on non-loopback; `--role-token TOKEN:ROLE`) |\n| `mushroomdb query <dir> <cypher>` | Run a Cypher read or write (`--query` also accepted) |\n| `mushroomdb asof <dir> --commit N` | Read-only view at a WAL commit |\n| `mushroomdb stats <dir>` | Print node/edge/rule counts |\n| `mushroomdb suggest <dir>` | Rank candidate linking rules (scored top-k 32, KeyMatch 512) |\n| `mushroomdb schema apply <dir> <schema.json>` | Idempotently apply a schema file (rules, views, fulltext indexes); prints a diff |\n| `mushroomdb snapshot <dir> [--keep-wal]` | Write `snapshot.bin` (truncates WAL unless `--keep-wal`) |\n| `mushroomdb verify <dir>` | Audit snapshot integrity: CRC32 all 12 sections, exit 2 on any mismatch |\n| `mushroomdb migrate <dir>` | Migrate an older store format in place |\n| `mushroomdb backup <dir> <dest>` | Copy store files to `<dest>` and CRC-verify the copy. WARNING: unsafe against a running `serve` — use `POST /backup` for live-served stores |\n| `mushroomdb export <dir> <dest> [--format jsonl\\|parquet\\|graphml]` | Export nodes, edges, and rules. JSONL is byte-identical across runs; Parquet is not across library versions. GraphML exports nodes and edges only, as a single `.graphml` file, for import into generic graph viewers and analysis tools |\n| `mushroomdb algo pagerank\\|wcc\\|degree <dir> [--top N]` | PageRank, weakly-connected components, or degree centrality over manual + derived edges. `--weight-prop`/`--min-weight` weight or filter the edge set |\n| `mushroomdb algo communities <dir> [--edge-type T]... [--weight-prop P] [--min-weight X] [--top N]` | Louvain communities with per-community cohesion and overall modularity |\n| `mushroomdb --version` | Print the CLI's version and exit |\n\n**Concurrency:** every CLI write command, the hooks, and a running `mushroomdb serve` coordinate\nthrough one advisory `LOCK` file in the store directory, so they are safe to run against the same\nstore at the same time. A writer that cannot get the lock within two seconds exits 3 with\n`another mushroomdb process is writing; retry`, having written nothing. Readers never take the\nlock and never wait; `recall` opens read-only (`read_only: true`) so an unattended hook can never\ndelay a writer or fail because one is running. What the lock does *not* give you: cross-process\ntransactions, and subscription events for a peer's writes — a commit absorbed by `refresh()` is\nvisible on the next read but notifies nobody. Full model:\n[`docs/site/concurrency.md`](docs/site/concurrency.md).\n\nFull HTTP endpoint reference: [`docs/site/api.md`](docs/site/api.md).\n\n---\n\n## Known limitations\n\n| Limitation | Detail |\n|---|---|\n| Memory-first | The in-memory store is RAM-bound. Design target is 10M nodes (~5–15 GB with properties). mmap-backed storage is deferred. |\n| Single writer, no interactive transactions | One writer at a time, many readers — within a process via `RwLock`, across processes via the advisory `LOCK` file. `write_batch` commits all ops in one WAL frame (all-or-nothing on crash replay) but is **not isolated**: readers may observe intermediate states while a committed batch is applied in memory. Multi-statement `BEGIN`/`COMMIT` is not supported, and there are no cross-process transactions. |\n| Peer writes do not notify subscribers | Commits another process made are picked up by `refresh()` and are there on the next read, but they fire no `EdgeFired`/`EdgeRetracted` event, so `/watch` and `/subscribe` see only writes made through this process. Poll if you need to react to a hook's writes. |\n| Cold start without a snapshot re-fires all rules | Snapshots persist derived edges, ANN state, and view definitions. At 100k nodes / ~10M derived edges: **0.02 s** from a V8 snapshot vs **8.16 min** WAL-only (ANN re-fit dominates). Call `snapshot()` before close. See [`dogfood/results/scale-100k.md`](dogfood/results/scale-100k.md). |\n| Two-hop Cypher joins at scale | Dense patterns producing >1,000,000 intermediate rows error without `LIMIT`. Add `LIMIT n` — the pull-based executor stops early and never materializes the full binding table. |\n| Cypher write subset | CREATE, MATCH…SET, MATCH…DELETE, MATCH…DETACH DELETE, and MERGE (single-key, with `ON CREATE SET` / `ON MATCH SET`) are supported. Derived edges cannot be deleted manually. Variable-length paths are hard-capped at 10 hops; unbounded `*min..` is rejected at parse time. Full coverage table: [`docs/site/query.md`](docs/site/query.md). |\n| Approximate vector mode is opt-in | `approximate: true` enables HNSW candidate selection. Per-query recall min 0.90, mean 0.998 at 5k / dim 1536 (fixed-seed probe). Review the trade-off before using it in completeness-critical workloads. |\n| Demo refuses existing directories | `mushroomdb demo` exits 1 if the target directory is non-empty, including hidden files (`.DS_Store` counts). Use a fresh path. |\n| Python bindings return dicts | pandas/polars zero-copy is not wired yet. HTTP `POST /query` defaults to Arrow IPC; JSON via `?format=json`. |\n\n---\n\n## Benchmarks\n\n10,000-node graph (Apple M4 Pro, macOS 15.7.3, arm64), mushroomdb v0.1.1 release build, 2026-08-24.\nFull methodology and honesty notes:\n[`benchmarks/results/head-to-head-10k-v2.md`](benchmarks/results/head-to-head-10k-v2.md).\n\n| Workload | mushroomdb | Neo4j | KùzuDB | Memgraph |\n|---|---|---|---|---|\n| Bulk ingest | 784 ms | 13.2 s | 1.21 min | 12.5 s |\n| Neighborhood depth-1 (p50) | 0.4 µs | 1.22 ms | 99.6 µs | 1.34 ms |\n| Neighborhood depth-1 (p95) | 2.2 µs | 1.46 ms | 519 µs | 2.14 ms |\n| Neighborhood depth-2 (p50) | 0.2 µs | 7.18 ms | 1.08 ms | 9.22 ms |\n| Cypher scan-filter-project (1.4k rows) | 1.22 ms | 93.7 ms | 3.95 ms | 83.7 ms |\n| Cypher two-hop join (200 rows) | 261.6 µs ★ | 3.99 ms ★ | 1.59 ms ★ | 1.96 ms ★ |\n| Cold-start: V8 snapshot open | 0.02 s ▽ | — | — | — |\n| Cold-start: WAL-only open | 8.16 min ▽ | — | — | — |\n| Server boot-to-ready | n/a (embedded) | 6.6 s | n/a (embedded) | 4.3 s |\n\n**Honesty notes:**\n\n- mushroomdb numbers are **embedded** — no network round-trip, no serialization overhead. KùzuDB\n  is also embedded, so its numbers are directly comparable. Neo4j and Memgraph go over\n  bolt/localhost (~0.1–1 ms round-trip per query).\n- ★ Two-hop join: same dataset, same warmup policy, all four engines on **5,810,000\n  INDUSTRY_ALIGNMENT edges**. Fresh process → ingest + preload → 3 discarded warmups → median of 10\n  runs. mushroomdb derives the edges via `create_rule`; competitors were pre-loaded via UNWIND MERGE\n  or COPY FROM CSV. All engines return 200 rows.\n- ★ Earlier v2.1 two-hop values were **retracted** for cross-engine contamination; the v2\n  mushroomdb 307 µs figure was **retired** (measured on a smaller 1M-edge graph). Both are\n  documented in the methodology file rather than quietly dropped.\n- ▽ 100k cold-start measured 2026-08-28, warm file cache, cold process, `/usr/bin/time -l`:\n  V8 snapshot open 0.02 s at 31–41 MiB RSS; snapshot size 1.8 GiB; snapshot write ~35 s. Cold-cache\n  was not measured. See [`dogfood/results/scale-100k.md`](dogfood/results/scale-100k.md).\n- Rule engine vs hand-rolled maintenance (10k nodes, 1,000 specialty updates, drift = 0 for all\n  three): per-op expert-written **64.93 min**, batched expert-written **24.98 s**, rule engine\n  **17.58 s**. Both hand-rolled variants were written by the engine team with full knowledge of\n  retraction semantics — drift = 0 is a property of that, not of hand-rolling in general.\n  [`benchmarks/results/handrolled-vs-rules.md`](benchmarks/results/handrolled-vs-rules.md)\n\n---\n\n## Architecture\n\n```text\ngraph-db/\n├── crates/\n│   ├── core-storage      # Packed adjacency topology + columnar property store + WAL + snapshots\n│   ├── core-rules        # linking rules, per-rule indexes, incremental maintenance\n│   ├── core-query        # pull-based interpreter; traversal ops + Cypher subset\n│   ├── core-api          # the one public Rust interface; typed error enums\n│   ├── code-extract      # tree-sitter symbol/import/call extraction; bytes in, facts out\n│   ├── arrow-bridge      # results ↔ Arrow buffers\n│   ├── server            # axum HTTP + WebSocket; serves UI\n│   ├── cli               # mushroomdb binary\n│   └── sim-harness       # DST: virtual clock, fault-injecting IO, seeded runner\n├── ui/                   # TypeScript + Vite graph explorer\n├── bindings/python/      # PyO3 / maturin\n└── clients/typescript/   # HTTP + WebSocket client\n```\n\nDependency rule (inward only):\n`bindings/server/cli → core-api → {core-query, core-rules} → core-storage`\n\nStorage uses a dense-id WAL with per-commit fsync (configurable via `FsyncPolicy`), plus mmap-able\nV8 rkyv snapshots (12 sections: CSR topology, columnar properties, HNSW blobs, provenance, IVF\nstate, per-node last-change index, and more — zero-copy, no heap allocation on open). V5/V6/V7\nstores are auto-migrated to V8 on `GraphDb::open`. Derived edges are not WAL-logged; they are\nrestored directly from the mmap'd sections. See [`docs/format-stability.md`](docs/format-stability.md)\nfor the format evolution contract.\n\n---\n\n## Roadmap\n\nPhases 1–4 and Plan 18 all landed. What remains:\n\n| Priority | Item |\n|---|---|\n| Medium | mmap snapshots; lock-free epoch readers |\n| Medium | v1.0 format stability (snapshot + WAL semver guarantee) |\n| Low | `CASE` in a write-statement `RETURN`; subqueries; napi-rs; WASM |\n| Low | Multi-statement `BEGIN/COMMIT` interactive transactions |\n\n---\n\n## Docs\n\n- [Quickstart](docs/site/quickstart.md) · [Rules](docs/site/rules.md) · [Cypher reference](docs/site/query.md) · [HTTP + MCP API](docs/site/api.md)\n- [The live code graph](docs/site/code-graph.md) · [Concurrency](docs/site/concurrency.md) · [Codebase graph](docs/site/ingest-git.md)\n- [Install, plugin and hooks](docs/site/skill.md) · [MCP tools](docs/site/mcp.md)\n- [Time travel](docs/site/timetravel.md) · [Subscriptions](docs/site/subscriptions.md) · [Views](docs/site/views.md) · [Rule suggestions](docs/site/suggest.md)\n- [Masks and access control](docs/site/masks.md) · [Full-text search](docs/site/fulltext.md) · [Property indexes](docs/site/indexes.md) · [Graph algorithms](docs/site/algorithms.md)\n- [Durability and recovery](docs/site/durability.md) · [Panic policy](docs/site/panic-policy.md) · [Testing](docs/site/testing.md) · [Format stability](docs/format-stability.md)\n- [Design spec](docs/design.md) · [Moat roadmap](docs/site/roadmap-moat.md) · [Case study](docs/dogfood-report.md)\n\nBuilding from source, Docker, packaging, and the test gates are in\n[CONTRIBUTING.md](CONTRIBUTING.md).\n\n---\n\n## License\n\nCopyright 2026 Matthew Sherlin.\n\nDual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at your option.\n",
  "bytes": 29048,
  "sha": "d94a2d5b1cb08ff9745794ea1454927b5bb77aac4fd97a9a28ae2f18780861c7",
  "repo_slug": "matthewsherlin/mushroomdb",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_matthewsherlin_mushroomdb_b9ad17ec/readme"
}