{"id":50366875,"url":"https://github.com/rishiskhare/lexa","last_synced_at":"2026-05-30T04:31:01.432Z","repository":{"id":355122817,"uuid":"1226592474","full_name":"rishiskhare/lexa","owner":"rishiskhare","description":"Pareto-optimal local search for agents — inspired by Exa's hybrid retrieval. Single Rust binary.","archived":false,"fork":false,"pushed_at":"2026-05-02T00:06:15.000Z","size":229,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-02T01:21:23.091Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rishiskhare.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-01T15:42:23.000Z","updated_at":"2026-05-02T00:59:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rishiskhare/lexa","commit_stats":null,"previous_names":["rishiskhare/lexa"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/rishiskhare/lexa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rishiskhare%2Flexa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rishiskhare%2Flexa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rishiskhare%2Flexa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rishiskhare%2Flexa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rishiskhare","download_url":"https://codeload.github.com/rishiskhare/lexa/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rishiskhare%2Flexa/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33680522,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T04:31:00.680Z","updated_at":"2026-05-30T04:31:01.426Z","avatar_url":"https://github.com/rishiskhare.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lexa\n\n\u003e **Local-first hybrid retrieval for your Obsidian vault and code.** A\n\u003e single static Rust binary plus an MCP server, so Codex / Claude\n\u003e Desktop / Cursor / Claude Code can answer questions from your notes\n\u003e without anything leaving your machine.\n\n[![Crates.io](https://img.shields.io/crates/v/lexa-obsidian?label=lexa-obsidian)](https://crates.io/crates/lexa-obsidian)\n[![Crates.io](https://img.shields.io/crates/v/lexa-core?label=lexa-core)](https://crates.io/crates/lexa-core)\n[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)](#license)\n[![CI](https://github.com/rishiskhare/lexa/actions/workflows/ci.yml/badge.svg)](https://github.com/rishiskhare/lexa/actions/workflows/ci.yml)\n[![Release](https://github.com/rishiskhare/lexa/actions/workflows/release.yml/badge.svg)](https://github.com/rishiskhare/lexa/actions/workflows/release.yml)\n\n---\n\n## Quick start\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/rishiskhare/lexa/main/scripts/install.sh | sh\nlexa-obsidian setup\n# restart Codex / Claude Desktop / Cursor, then ask:\n#   \u003e what did I write about \u003ctopic\u003e?\n```\n\nThat's it. `setup` is interactive: it picks up your vault, optionally\npre-indexes it, writes the right MCP config block into\n`~/.codex/config.toml` (and Claude Desktop / Claude Code if you opt\nin), and drops an `AGENTS.md` in your vault root so agents route note\nquestions through Lexa **without** the \"Use lexa-obsidian.\" prefix.\n\n---\n\n## Demo\n\n```text\n\u003e what did I write about the rate limiter redis fallback last quarter?\n```\n\n```json\n[\n  {\n    \"path\": \"Daily/2026-04-30.md\",\n    \"title\": \"Daily 2026-04-30\",\n    \"score\": 0.7141,\n    \"heading\": \"Followups\",\n    \"excerpt\": \"redis down, switching to in-memory backoff in `acquire`. The reranker latency was acceptable at p95 of 261 ms.\",\n    \"line_start\": 12,\n    \"line_end\": 18,\n    \"tags\": [\"daily\", \"project/lexa\"],\n    \"breakdown\": { \"routed_to\": \"fast\" }\n  }\n]\n```\n\nWant to try it without your own vault? Point Lexa at the bundled\n[`demo-vault/`](demo-vault/):\n\n```bash\nlexa-obsidian --vault ./demo-vault setup --no-codex --no-agents-md\nlexa-obsidian --vault ./demo-vault --hash-embeddings search \"reranker latency\"\n```\n\n---\n\n## Features\n\n| Feature                                  | What it gives you                                                                  |\n|------------------------------------------|------------------------------------------------------------------------------------|\n| **Hybrid retrieval**                     | BM25 (FTS5) + binary-quantized 768-d Matryoshka KNN (sqlite-vec) + cross-encoder rerank, fused with RRF (k=60). |\n| **Five tiers**                           | `instant` / `dense` / `fast` / `deep` / `auto` — mirroring Exa's API. p50 ~9 ms on the fast tier with real Nomic v1.5-Q. |\n| **Auto-routing**                         | Single-identifier queries → `instant` (BM25); long question-form → `deep`; default → `fast`. No \"Use lexa-obsidian.\" needed. |\n| **Obsidian-native**                      | Frontmatter stripped before embedding. Wiki-links, inline `#tags`, `^block-ids`, and `![[embeds]]` parsed into sidecar tables. |\n| **MCP-first**                            | Eight tools — `search_notes`, `find_backlinks`, `list_tags`, `get_note`, `get_similar`, `index_vault`, `purge_vault`, `vault_status`. |\n| **Background indexing**                  | The MCP server indexes in the background; content calls return `{indexing: true, ...}` while in-flight, so Codex never hangs. |\n| **Live re-indexing**                     | Built-in `notify-debouncer-mini` watcher: notes you add, edit, or delete in Obsidian appear in (or vanish from) search within ~500 ms. Idempotent on every event. |\n| **One static binary**                    | No daemon, no Python, no Docker. SQLite + `sqlite-vec` is the entire backend.      |\n| **100% local**                           | First run downloads Nomic + BGE ONNX (~390 MB). After that, zero network calls. No telemetry, no API keys. |\n| **`--offline` flag**                     | Sets `HF_HUB_OFFLINE=1` so fastembed refuses every network fetch — hard offline guarantee after `models prefetch`. |\n\n---\n\n## Use cases\n\n| Want to...                                  | Do this                                                                |\n|---------------------------------------------|------------------------------------------------------------------------|\n| Ask Codex / Claude / Cursor about your vault| `lexa-obsidian setup` and ask in plain English.                        |\n| Search a code repo from the CLI             | `lexa index ~/repo \u0026\u0026 lexa search \"rate limiter fallback\"`.            |\n| Wire MCP search into a custom agent         | Use `lexa-obsidian-mcp` (rmcp stdio) or `lexa-mcp` (file-shaped).      |\n| Embed retrieval into your own Rust app      | `lexa_obsidian::LexaObsidianDb::open(...)` — see the [crate docs](https://docs.rs/lexa-obsidian). |\n| Try it without your own data                | `lexa-obsidian --vault ./demo-vault setup --no-codex --no-agents-md`.  |\n\n---\n\n## Subcommands\n\n```text\nlexa-obsidian setup            # interactive bootstrap (most users only need this)\nlexa-obsidian doctor           # diagnose every common failure mode\nlexa-obsidian models prefetch  # download retrieval models (~390 MB) ahead of time\nlexa-obsidian --vault \u003cpath\u003e index\nlexa-obsidian --vault \u003cpath\u003e status\nlexa-obsidian --vault \u003cpath\u003e tags [--prefix X] [--limit N]\nlexa-obsidian --vault \u003cpath\u003e backlinks \u003cnote\u003e\nlexa-obsidian --vault \u003cpath\u003e search \u003cquery\u003e [--tier auto|fast|deep] [--tag X] [--folder Y] [--json]\nlexa-obsidian --vault \u003cpath\u003e watch\n```\n\n`--vault` falls back to `LEXA_OBSIDIAN_VAULT`. The DB defaults to\n`~/.lexa/obsidian-\u003csha-of-vault\u003e.sqlite` — two distinct vaults never\nshare an index.\n\n---\n\n## Other ways to install\n\n```bash\n# Cargo (any Rust target, including Linux ARM64):\ncargo install lexa-obsidian\n\n# Homebrew (macOS) — coming soon, file an issue if you want it sooner.\n\n# Manual: grab a tarball from\n# https://github.com/rishiskhare/lexa/releases/latest\n```\n\n---\n\n## Project layout\n\n```text\nlexa/\n├── crates/\n│   ├── lexa-core/        # Library: chunking, embedding, hybrid retrieval, fusion.\n│   ├── lexa-cli/         # `lexa` CLI for any file tree (code, docs).\n│   ├── lexa-mcp/         # `lexa-mcp` rmcp stdio server (file-shaped tools).\n│   ├── lexa-obsidian/    # `lexa-obsidian` CLI + `lexa-obsidian-mcp` server.\n│   └── lexa-bench/       # Five reproducible benchmark harnesses.\n├── docs/\n│   ├── ARCHITECTURE.md   # Crate map, schema, retrieval pipeline.\n│   ├── BENCHMARKS.md     # Latency / BEIR / agent / SimpleQA numbers.\n│   ├── FAQ.md            # First-run latency, vault switching, uninstall, privacy.\n│   ├── THREAT_MODEL.md   # Read-only-on-vault guarantee, network footprint.\n│   └── adr/              # Six one-page architecture decision records.\n├── bench/                # Hand-curated query sets for the agent + SimpleQA harnesses.\n├── bench-results/        # Committed JSON artifacts so every published number is reproducible.\n├── demo-vault/           # 6-note synthetic vault — try Lexa without your own data.\n├── templates/AGENTS.md   # Dropped into your vault root by `setup`.\n└── scripts/install.sh    # `curl | sh` installer.\n```\n\n---\n\n## Documentation\n\n- **Architecture** — [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)\n- **Benchmarks** — [`docs/BENCHMARKS.md`](docs/BENCHMARKS.md): real numbers\n  with hardware, exact corpus URL, and the command that produces them.\n- **FAQ** — [`docs/FAQ.md`](docs/FAQ.md): first-run delay, vault\n  switching, uninstall, model swap, privacy verification with `tcpdump`.\n- **Threat model** — [`docs/THREAT_MODEL.md`](docs/THREAT_MODEL.md):\n  what Lexa does, what it does *not* do, and how to verify each claim.\n- **Decision log** — [`docs/adr/`](docs/adr/): six ADRs covering name,\n  storage, embeddings, chunking, search tiers, MCP posture, Obsidian\n  adapter.\n\n---\n\n## How Lexa maps to Exa\n\nLexa is local-first, but the architecture follows Exa's: tiered\nsearch, hybrid retrieval, Matryoshka prefixes, binary quantization,\nquery-aware highlights.\n\n| Exa concept                      | Lexa equivalent                                                                                       |\n|----------------------------------|-------------------------------------------------------------------------------------------------------|\n| Instant tier (\u003c200 ms, BM25)     | `--tier instant` — FTS5 BM25 only, p50 ~250 µs.                                                       |\n| Fast tier (neural / hybrid)      | `--tier dense` (KNN-only) or `--tier fast` (hybrid + RRF), p50 ~9 ms.                                 |\n| Auto tier                        | `--tier auto` — query router in `classify_query`. Default.                                            |\n| Deep tier (agentic)              | `--tier deep` + `SearchOptions::additional_queries` for [`additionalQueries`-style](https://exa.ai/blog/exa-deep) fan-out.    |\n| Hybrid retrieval (BM25 + dense)  | RRF (k=60) over FTS5 BM25 and binary-quantized vector KNN, run concurrently. See [Exa: Composing a Search Engine](https://exa.ai/blog/composing-a-search-engine). |\n| Matryoshka prefix                | Nomic v1.5-Q (768d, MRL-trained at `{64, 128, 256, 512, 768}`) — `vectors_bin_preview bit[256]` table. See [Exa 2.0](https://exa.ai/blog/exa-api-2-0). |\n| Binary quantization              | `sqlite-vec`'s `vec_quantize_binary()` and `bit[N]` columns; SIMD Hamming distance.                   |\n| Cross-encoder reranking          | `BAAI/bge-reranker-base` over top-15 fused candidates, sigmoid-blended at α = 0.7 with the RRF score.|\n| Highlights / contents API        | `search.rs::highlight` — query-token-overlap-scored sentence span.                                    |\n| LLM-as-judge eval                | `lexa-bench simpleqa` — Harness E. Five-dim rubric. Default judge `qwen3:8b` via local Ollama.        |\n\n---\n\n## Privacy\n\nLexa runs **entirely locally**. The only outbound network call is the\nfirst-run download of two ONNX models (Nomic v1.5-Q ~110 MB, BGE\nreranker ~280 MB) from Hugging Face. After that — zero network. No\ntelemetry, no analytics, no API keys.\n\nFor a hard offline guarantee, run `lexa-obsidian models prefetch` once\non a connected machine, then use `--offline` (or `LEXA_OFFLINE=1`) to\nmake fastembed refuse every network call. See\n[`docs/THREAT_MODEL.md`](docs/THREAT_MODEL.md) for the verification\nrecipe (`tcpdump`-style proof) and the full posture.\n\n---\n\n## Development\n\n```bash\ngit clone https://github.com/rishiskhare/lexa\ncd lexa\ncargo build --workspace --release\ncargo test --workspace --release\ncargo clippy --workspace --all-targets --release -- -D warnings\ncargo fmt --all -- --check\n```\n\n47 tests across 11 suites, no `unsafe` outside the SQLite extension\nloader, all hot paths covered by Criterion benches in\n[`crates/lexa-bench/benches/`](crates/lexa-bench/benches/).\n\n---\n\n## Contributing\n\nIssues and PRs welcome. The decision log under\n[`docs/adr/`](docs/adr/) is the best place to start if you want to\nunderstand the design before changing it. For larger changes, open an\nissue first so the architectural fit can be discussed.\n\n---\n\n## License\n\nDual-licensed under either of:\n\n- Apache License, Version 2.0 ([`LICENSE-APACHE`](LICENSE-APACHE))\n- MIT license ([`LICENSE-MIT`](LICENSE-MIT))\n\nat your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frishiskhare%2Flexa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frishiskhare%2Flexa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frishiskhare%2Flexa/lists"}