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.

the whole thing, start to finish
# 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"
output
[
  { "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.

nothing running in the background no database to set up no API key to start one folder to back up
nidus search

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.

closest matches first
$ echo '[0.9,0.1,0.0]' | nidus search --dir ./store docs \
    -k 3 --include-attr title
output
[
  { "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" } } }
]
faster, smaller, or narrower: one flag each
# 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"}]}]'
nidus text-search

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.

say which fields are searchable, then search them
$ nidus set-fts-schema --dir ./store docs --field body --field title

$ nidus text-search --dir ./store body "crash fsync" \
    -k 3 --include-attr title
output
[
  {
    "id": "wal-commit",
    "score": 1.876369,
    "attrs": { "title": { "Str": "Write-ahead log" } }
  }
]
show me what matched
$ nidus text-search --dir ./store body "crash" \
    --highlight --fragment-chars 60
output
"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.

nidus hybrid-search

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.

one query, both legs, one ranking
$ 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.

nidus recall

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.

index anything you have
$ 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.

ask, and get passages back
$ 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.

or let it be your app's search endpoint
$ 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.

also good at code

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.

no install required

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.

nidus, in your browser

search how do I keep data safe if the process dies

durability 0.848
recovery 0.651
storage 0.284

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.

before you commit to it

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.
nidus --help

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.

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