Local-first multi-hop RAG: Chroma + an entity graph, zero tokens per query Hubmesh, a new open-source project, introduces a local-first multi-hop retrieval system that combines Chroma vector search with an entity graph and Personalized PageRank, achieving 75.2% supporting-fact recall@10 on HotpotQA dev without using LLM tokens per query. The system, built by developer DemigodDSK, runs offline in about 100ms on a 30K-doc corpus and is bit-identical across runs, offering a deterministic alternative to LLM-in-the-loop retrieval. Chroma's sweet spot is local-first: embed your documents, query them on your own machine, no infrastructure ceremony. But local-first RAG hits a wall on multi-hop questions: "Which university did the founder of the company that acquired Polar Metrics study at?" The answering passage shares almost no vocabulary with the question. It's connected to it — through an acquisition, a founder, a biography — and similarity search can't follow connections. The usual fix is to put an LLM in the retrieval loop to decompose the question, which breaks exactly what makes local-first attractive: now every query costs tokens, takes seconds, and returns something different each run. hubmesh https://github.com/DemigodDSK/hubmesh takes the other road: keep retrieval as pure math. At index time it builds an entity–document graph from your corpus with spaCy NER no LLM, no tokens . At query time it runs Personalized PageRank from the question's entities over that graph and fuses the result with Chroma's cosine scores. The whole query path is numpy and scipy: ~100ms on a 30K-doc corpus, offline, and bit-identical across runs — three properties an LLM-in-the-loop retriever cannot offer at any price. pip install "hubmesh chroma,kg " python -m spacy download en core web sm python from hubmesh import Planner from hubmesh.adapters import ChromaStore from hubmesh.kg import build entity kg import spacy embed = ... your embedding callable store = ChromaStore.from documents docs ephemeral store = ChromaStore.from documents docs, persist directory="./chroma" store = ChromaStore.from documents docs, host="localhost", port=8000 nlp = spacy.load "en core web sm" kg = build entity kg store.get many store.all ids , nlp=nlp planner = Planner store=store, kg=kg, nlp=nlp, embed=embed result = planner.retrieve "Which university did the founder of the " "company that acquired Polar Metrics study at?", top k=10 print s.doc.id for s in result.sources :3 for path in result.reasoning: the graph route, not a rationalization print " - ".join path.node ids The scoring composite has three parts — cosine relevance, PPR diffusion, and a convergence term that scores each document by the geometric mean of diffusion from every question entity separately. A multi-hop answer sits at the intersection of the question's anchors; pooled similarity computes a union. Intersections are where bridge documents live. The formula lineage is a network-topology paper, NNSI, ICOMP'25 — same idea, different graph. Full HotpotQA dev 7,405 questions : 75.2% supporting-fact recall@10 vs 69.3% naive cosine over identical embeddings. MuSiQue 2/3/4-hop: +6.0/+3.2/+5.0 points. Honest trade: recall@2 dips 0.75 pts under the convergence term flag documented to disable for top-2 workloads . Harness and raw JSONs ship in the repo. pip install "hubmesh mcp " adds an MCP server in the official MCP Registry as io.github.DemigodDSK/hubmesh with agent-steerable retrieval: pass seed entities to aim the next hop at what you just read, exclude docs to explore new ground. Local Chroma + local graph Repo: github.com/DemigodDSK/hubmesh · MIT · numbers reproducible via benchmarks/