Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ruvnet/cf3877071a2b1ab9252785673340ab95 to your computer and use it in GitHub Desktop.

Select an option

Save ruvnet/cf3877071a2b1ab9252785673340ab95 to your computer and use it in GitHub Desktop.
RuVector 2.3.0: local Rust & npm embeddings via Lattice, plus a full cargo-audit/cargo-deny CVE cleanup

RuVector 2.3.0: Local Rust & npm Embeddings with Lattice, Plus a Full CVE / Supply-Chain Cleanup

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.

Why local embeddings matter for a vector database

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 optional lattice-embeddings Cargo feature wrapping the lattice-embed crate. Fully opt-in: cargo tree with default features confirms zero lattice-* dependencies leak into a normal build.
  • ruvector-extensions (npm/WASM) — a LatticeWasmEmbeddings provider for Node.js and browser environments, following the package's existing EmbeddingProvider interface.
# Rust
cargo add ruvector-core --features lattice-embeddings

# npm / WASM
npm install ruvector-extensions@0.1.2

The bug you'd never notice until your search results got worse

Shipping 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.

Full CVE scan: cargo-audit + cargo-deny, and what "no safe upgrade available" actually means

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.

Also in this release

  • Llama 3.2 1B/3B now load correctly in the ruvllm candle backend — a hardcoded tie_word_embeddings = false was 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: write on itself, but the calling job wasn't granting it, so the permission silently never propagated).

Links


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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment