Latin nidus, a nest
Find the right passage, by meaning or by the exact words.
nidus is a search engine you run as a single file, and it works two ways: by
meaning (this is vector search, if that is the term you came here for) or by the
exact words (full-text search). It is a pure-Rust vector store with full-text
search that runs anywhere Rust runs: in process as a library, behind
nidus serve over HTTP, as an MCP server, or in a browser on wasm.
Give it documents, notes, a support archive or a codebase, and it finds the
passages that answer a question. It is the retrieval half of a RAG app,
the memory behind an agent, and a plain search box, all out of one folder.
curl -fsSL https://nidus.duckedup.org/install.sh | sh No Rust toolchain needed. It also works as a Rust library, if you would rather keep the index inside your own program.
# read a folder. no key, no model, no network call.
$ nidus ingest ./docs --collection docs --dir ./store \
--strategy markdown --fts-only nidus.text
# ask it something the way you would ask a colleague.
$ nidus text-search --dir ./store nidus.text -k 3 \
"how do we avoid losing data when the process crashes" [
{ "id": "guides/storage.md#3",
"score": 11.89682,
"attrs": { "nidus.source_path":
{ "Str": "guides/storage.md" } } },
{ "id": "guides/storage.md#0",
"score": 11.86455, "attrs": { … } },
{ "id": "guides/command-line.md#19",
"score": 9.776753, "attrs": { … } }
] 34 markdown files became 796 searchable passages, with no API key, and the question landed on the durability guide.
Ask what you meant, not what you typed.
Ask "how do we keep data safe if the process dies" and the right file comes back, even though it uses none of those words. That is nidus comparing meaning rather than spelling (this is vector search, if that is the phrase you came here for).
It checks everything by default, which is quick at the size of a project on your laptop and always exactly right. When you outgrow that, one flag switches to a faster index that gives up a sliver of accuracy for a lot of speed, and another scans a compressed copy first and then double-checks the winners at full precision.
Filters run before anything is ranked, so narrowing a search makes it cheaper rather than more expensive.
$ echo '[0.9,0.1,0.0]' | nidus search --dir ./store docs \
-k 3 --include-attr title [
{ "id": "wal-commit", "score": 1.0,
"attrs": { "title": { "Str": "Write-ahead log" } } },
{ "id": "compaction", "score": 0.32151973,
"attrs": { "title": { "Str": "Compaction" } } },
{ "id": "hnsw", "score": 0.13098952,
"attrs": { "title": { "Str": "HNSW index" } } }
] # trade a little accuracy for a lot of speed
$ echo '[0.9,0.1,0.0]' | nidus search --dir ./store docs --ann hnsw -k 5
# scan a compressed copy, then check the winners exactly
$ echo '[0.9,0.1,0.0]' | nidus search --dir ./store docs \
--quantization int8
# narrow it down before anything is ranked
$ echo '[0.9,0.1,0.0]' | nidus search --dir ./store docs \
--where '[{"Eq":["lang",{"Str":"rust"}]}]' Sometimes you know the exact words.
An error code. A ticket number. A function name someone said in standup. Searching by meaning is bad at those, because there is no meaning to match, only a string.
So nidus does ordinary keyword search too, ranked the way a good search box ranks things (BM25, the same scoring Elasticsearch uses). Same data, same command, no model and no API key anywhere near it. Ask it which words matched and it hands back the fragment with them marked.
$ nidus set-fts-schema --dir ./store docs --field body --field title
$ nidus text-search --dir ./store body "crash fsync" \
-k 3 --include-attr title [
{
"id": "wal-commit",
"score": 1.876369,
"attrs": { "title": { "Str": "Write-ahead log" } }
}
] $ nidus text-search --dir ./store body "crash" \
--highlight --fragment-chars 60 "fragments": [
{ "text": "before commit returns, so a crash loses at most the in",
"spans": [[28, 33]] }
] Character positions, so you can wrap the match in a <mark> without guessing.
Two searches, one ranking.
Vector search ranks by meaning. Full-text search ranks by exact words. Hybrid search runs both against one query and merges the two rankings into a single list with reciprocal rank fusion (a standard way to combine two rankings without needing their scores on the same scale).
One flag tips the balance toward the text leg or the vector leg, for when you already know which one matters more.
$ echo '[0.9,0.1,0.0]' | nidus hybrid-search --dir ./store \
body "write ahead log" --text-weight 3 -k 5 Reciprocal rank fusion merges the vector ranking and the text ranking into one list. --text-weight tips the balance toward the text leg.
Stop your model guessing. Hand it your data.
A model knows what it was trained on and whatever you paste in. Ask it about your product, your runbooks, or last quarter's tickets and it will invent something that reads perfectly and is wrong.
The fix is to look the answer up first and hand the model what you found. That lookup is the hard half, and it is the half nidus is: index your content once, ask in plain English, get back the few passages that actually answer it, each with the file it came from.
It works with any model and any framework, because what comes back is JSON from a command or an HTTP call. It also remembers: save something worth keeping, find it again weeks later by meaning rather than by filename.
$ nidus ingest ./handbook --collection docs --dir ./store \
--embed-provider voyage Markdown, PDFs exported to text, notes, transcripts, a support archive. Run it again and it skips every file you have not touched.
$ nidus recall docs "what is our refund window" \
--dir ./store --rollup 1 --neighbours 1 --rollup keeps the best passage per document and stitches its neighbours back on, so the model gets something readable instead of three overlapping fragments.
$ nidus serve --dir ./store --embed-provider voyage
$ curl localhost:7700/collections/docs/recall \
-H 'content-type: application/json' \
-d '{"query": "what is our refund window"}' These three need an embedding provider (Voyage, OpenAI, Ollama and others). The run at the top of this page needed none.
It also reads code as code, not as text.
Point it at a repository and it splits by function, struct and trait instead of by character count, so a coding assistant gets back a whole symbol and the file it lives in, not a fragment near it.
Try it before you install it.
The terminal beside this is nidus itself, compiled to WebAssembly and
running in this tab. Type search and a question, or
help to see what else it takes.
One honest caveat: no browser ships a language model, so in here the words are turned into numbers by a crude stand-in rather than a real one. The index, the ranking and the scores are the actual thing.
search how do I keep data safe if the process dies
A real nidus, compiled to wasm and running in this tab. No browser ships an embedding model, so text becomes a vector by character trigrams plus a small concept map; the store, the ranking and the scores are the real thing. Data persists in OPFS, private to this origin.
Where it fits, and where it doesn’t.
Good fit
- Search across documents, notes, a support archive, or a codebase, by meaning or by exact words.
- The retrieval half of a RAG app, or the memory an agent keeps between sessions.
- Giving a coding assistant a way to look through your code.
- A side project or an internal tool that needs real search.
- One service that owns its own index and does not want a second thing to operate.
Look elsewhere
- Billions of records spread across a cluster.
- A managed service with somebody else carrying the pager.
- Anything that should be your primary database. This sits beside one.
It is still one binary.
Everything on this page is in the same download. There are no editions, no
paid tier and no build flags to get right: the install script, the prebuilt
binaries and cargo install nidus all hand you the same file.
It is written in Rust with no C libraries underneath, which is why
installing it is a download rather than an afternoon.
Start it on a port and talk to it with fetch. Everything the library does has a URL, and there are JavaScript, Go and Python clients that wrap them.
nidus mcp Plug it into your editorOne line registers it with Claude Code, Cursor, or anything else that speaks MCP. Several editors can share one running copy.
--persistence s3://… Start local, move it laterThe same index works from a folder, from S3, or from Google Cloud Storage. Changing where it lives means changing one URL, not running a migration.
nidus backup Hard to breakKill it mid-write and it comes back clean: it loses at most the write that was in flight, and repairs itself when you reopen it. Backing up is copying a folder.
--rerank Squeeze the top of the listCast a wider net, then have a reranker read the query and each candidate together and reorder them. Better answers without scanning more.
wasm32 Runs in a browser tabThe whole thing compiles to WebAssembly and saves to browser storage. That is what the demo further up this page is running.
One command, and you’re searching.
No account, nothing to sign up for, nothing left running afterwards. Install it, index a folder, and start asking it questions.
curl -fsSL https://nidus.duckedup.org/install.sh | sh