Hybrid search over my Obsidian vault, always on and always current
niko is a persistent hybrid search service that watches my Obsidian vault so my agents and I always have accurate, up to date context.
As an Obsidian user I needed fast, accurate, always current search across my vault. I’d used qmd a lot at work, but my vault changes all day and gets searched all day by me and my agents, so I wanted something always up and watching the files.
qmd is a really good local search tool and I’ve borrowed a lot of ideas from it for my own build. But it suffers from a cold start. Local models need to be reloaded every launch, so it takes 15 seconds or so. The only way to avoid this is to have a server process that keeps the models in memory.
It’s called niko. This is an homage to a dog named Niko I had when I was growing up. He was an incredible black Labrador retriever. He always fetched brilliantly.
I have it running as a persistent service that watches my vault and reindexes, re-embeds, and keeps up with my ever changing vault so myself and my agents have instant accurate and up to date context when needed (which is often!) I expose a CLI with vs and an API both (via MCP or direct API) and I have my Claude Code wired up with a skill to it. I have plans to integrate future agentic projects with it too.
On each query, it performs keyword and fuzzy-title searches across 7,000+ notes, runs vector search, and merges the results into a single ranking. Keywords are handled by SQLite FTS5, and vectors by sqlite-vec. The caller here is the Memento agent, which asks niko for JSON responses so it can take action.
I wanted search and retrieval over my knowledge base that works close to the way I remember things: hand me the snippet I need, or point me (or an agent) at the whole file when that’s what it takes.
Fast and always on is only half the battle. I also wanted vault awareness. I wanted features that let me slice my corpus into buckets and provide dedicated instructions for each bucket of documents and files.
Collections are sections of the vault (Research, my Reddit saves, etc.) and each one carries a weight and a context description. Largely inspired by qmd. That context shows up on every result as a Context: line, so a positive hit from my Reddit saves gets flagged as third-party opinion, so an agent won’t mistake it for a decision I actually made or something I actually wrote. Filters on scope, tags, frontmatter and dates are soft boosts (nudging the ranking without hiding anything), and --strict makes them hard filters.
My Reddit saves collection is content that Stash imports into my vault. This is content that the agent should consider another person’s opinion, not my own notes.
The daily restarts… The reranker was blocking everything else, the health check failed and autoheal kept restarting it, and I only noticed by chance. Moving the reranker off the main thread fixed it, and it’s why I added alerting for things that fail quietly.
I assumed the model library was already doing inference off the main thread, but a small repro harness proved otherwise. Disabling reranking was an option, but I wanted to keep it, so I moved the reranking to a worker thread. Now the health check answers 300 of 300 requests while a three-minute rerank happens, so autoheal has no reason to intervene.
The resulting alerting covers two quiet failures: one for container CPU, and another that pages me when the file watcher goes quiet and stops logging file events. Both pages are delivered via my single alert relay.

Part 2 is how I get it more accurate: testing, measuring, and the model swaps that came out of it. After that, more of my agent projects plug into it, NanoClaw first.