TL;DR: RuVector — an open-source Rust vector database with HNSW indexing, WASM bindings, and Postgres integration — just shipped local (no-API-key) embedding generation via Lattice on both the Rust crate (ruvector-core@2.3.0) and the npm/WASM package (ruvector-extensions@0.1.2), fixed 3 real RustSec CVEs, and turned cargo audit + cargo deny fully green after they'd been failing on main. Full release notes: ruvector-core-v2.3.0.
Most vector-search tutorials assume you're calling OpenAI or Cohere for embeddings. That's fine for a demo, but it means every insert and every query round-trips to a third party, costs money per call, and leaks your data off-box. RuVector's new Lattice integration gives you a CPU-native, pure-Rust embedding provider with no external API dependency — useful for offline pipelines, edge deployment, air-gapped environments, or just not wanting to pay per-embedding at scale.
It ships in two places that now agree with each other:
ruvector-core(Rust) — an optionallattice-embeddingsCargo feature wrapping thelattice-embedcrate. Fully opt-in:cargo treewith default features confirms zerolattice-*dependencies leak into a normal build.ruvector-extensions(npm/WASM) — aLatticeWasmEmbeddingsprovider for Node.js and browser environments, following the package's existingEmbeddingProviderinterface.
# Rust
cargo add ruvector-core --features lattice-embeddings
# npm / WASM
npm install ruvector-extensions@0.1.2Shipping the same capability in two languages in parallel surfaced a subtle, silent bug: the Rust and WASM providers disagreed on whether the bge-small embedding model needs a query-side instruction prefix (a common pattern for asymmetric retrieval models — the query and the passage get embedded differently on purpose). One provider added the prefix, the other didn't. Same model name, same 384 dimensions, incompatible vector spaces — index your corpus with one provider, query with the other, and you'd get quietly degraded retrieval with no error message anywhere.
It's fixed now, and — more interestingly — it's fixed in a way that can't silently regress again: both the Rust and TypeScript test suites assert against the same shared fixture file (fixtures/lattice-embed/query-prefixes.json), so if the two implementations ever drift apart on prefix handling, CI catches it instead of a user's recall metrics quietly dropping.
If you're building a multi-language SDK around any embedding model with query/passage asymmetry (BGE, E5, GTE, and friends all do this), this is worth stealing as a pattern.
Ran cargo audit and cargo deny check against main before this release and both were failing:
| Advisory | Crate | Type | Resolution |
|---|---|---|---|
| RUSTSEC-2026-0204 | crossbeam-epoch 0.9.18 |
Invalid pointer deref in Debug/Pointer fmt |
Fixed — bumped to patched 0.9.20 |
| RUSTSEC-2026-0194, -0195 | quick-xml 0.26.0 |
Two DoS advisories (severity 7.5/high) | Documented ignore — blocked upstream |
| RUSTSEC-2026-0192 | ttf-parser |
Unmaintained, no fix exists | Documented ignore |
The quick-xml case is a good illustration of what "blocked upstream" actually means in practice, not just a hand-wave: it's pulled transitively via inferno <- pprof's optional flamegraph-profiling feature. Traced the whole chain — even pprof's newest release still pins inferno = "^0.11", and inferno 0.11.x pins quick-xml = "^0.26". No cargo update can cross either constraint without a [patch] override, and inferno only ever parses profiling data our own benchmark runs generate locally — no untrusted or network-controlled XML ever reaches it. That's the difference between a real vulnerability and a real ignorable one, and it's why "there's a CVE" and "you're at risk" are not the same sentence.
Bonus find while auditing every existing ignore entry against the RustSec advisory database directly: three advisory IDs had their human-readable justification comments cyclically swapped between the project's deny.toml and .cargo/audit.toml — e.g. an entry that actually covers rusttype was commented as rand_os. The ignored IDs themselves were always correct (never a real security bug), but the descriptions would have misled the next person doing a security review. Fixed, plus removed 5 dead ignore entries that cargo deny itself flagged as no-longer-applicable — the underlying crates had already been patched away by earlier transitive bumps and nobody cleaned up the config.
cargo audit and cargo deny check both exit 0 on main now.
- Llama 3.2 1B/3B now load correctly in the
ruvllmcandle backend — a hardcodedtie_word_embeddings = falsewas breaking any checkpoint that ties its input/output embedding weights, which Llama 3.2's smaller models do. - Postgres row-level-security tenant isolation, made faster without changing its fail-closed guarantees —
current_setting()calls in the generated RLS policy are now wrapped for per-statement (InitPlan) evaluation instead of re-evaluating per row. - A GitHub Actions reusable-workflow permissions fix for the native-binary build pipeline (a classic gotcha: the called workflow declared
permissions: contents: writeon itself, but the calling job wasn't granting it, so the permission silently never propagated).
- Release notes: https://github.com/ruvnet/RuVector/releases/tag/ruvector-core-v2.3.0
ruvector-coreon crates.io: https://crates.io/crates/ruvector-coreruvector-extensionson npm: https://www.npmjs.com/package/ruvector-extensions- Repo: https://github.com/ruvnet/RuVector
Contributors this cycle: @ohdearquant (the full Lattice embeddings feature across both packages, plus the Llama 3.2 fix), @dmitrymaranik (the RLS tenant-isolation performance fix), @FritzHeider (the CI permissions fix).