GET terms/packs/0007.bin · Range: bytes=52880–118512 → 206

Search, without the server.

Rangefind packs your content into a static index — plain files on any host or CDN — and the browser reads only the byte ranges it needs to answer a query. Full-text relevance, typo correction, facets, autocomplete, geo, and vectors. Nothing to run, nothing metered.

This box searches this site's own index — static files, range requests, hybrid semantic ranking: on first focus it loads a small embedding model in your browser and fuses meaning with keywords. Try describing what you want.

7.2M
Wikipedia pages in one index
128 ms
cold two-word query, top-10
2.3 MiB
fetched for it — not 26 GB
$0/mo
servers, licenses, per-query fees
manifest.min.json → terms/ → docs/

How it works

A search index is mostly reads. Rangefind does all the writing at build time — postings, documents, facets, and geo trees packed into content-addressed files — so query time is nothing but a few small, cacheable HTTP range requests against your own static hosting.

01 · build

Index your content

Point the crawler at any built static site, or feed JSONL through a schema with weighted fields, facets, numbers, geo points, and vectors.

npx rangefind build ./dist
02 · publish

Deploy plain files

The index is a directory of immutable packs plus one small manifest. Any static host or CDN that supports range requests will do — most do.

rsync -r dist/ your-host:
# or: netlify / pages / s3 / r2…
03 · query

Search from the browser

The runtime fetches the manifest, then range-reads exactly the term postings and documents a query touches. Warm queries are often a single request — or zero.

const rf = await createSearch({ baseUrl: "/rangefind/" });
await rf.search({ q: "byte ranges" });
features/

A complete engine, not a toy

Everything below ships in the same static index and the same zero-dependency runtime — no plugins to bolt relevance on later.

terms/packs/

Real relevance

BM25F field weighting with phrase and proximity signals, impact-ordered postings, and early termination for fast top-k.

typo/

Typo correction

Bounded vocabulary probes rescue misspelled queries only when first-page results are empty or weak — no fuzzy tax on good queries.

facets/ · filter-bitmaps/

Facets & filters

Keyword facets with exact-or-flagged counts, plus typed numeric, date, and boolean doc-values for filtering and sorting.

authority/

Search-as-you-type

Diacritic-folded prefix autocomplete with popularity weights; the first keystroke costs one small fetch from a precomputed hot list.

geo/tree/

Geo search

Static KD trees with bounding-box and radius filters, exact nearest-neighbor sort, and distance boosts — proven on 33M OSM places.

vectors/

Semantic & hybrid

An int8 IVF vector index with cosine top-k and reciprocal-rank fusion against the text lane. No vector database.

analysis

20+ languages

Per-document language detection, light stemmers and stopwords, script-aware folding, and deterministic CJK bigrams — frozen into the manifest.

gen-0001/

Incremental updates

Publish small delta generations over an existing index; unchanged packs keep their names and their CDN cache entries.

debug/build-telemetry.json

Measured, not promised

corpusdocsindexcold query
French Wikipedia2.75M5.3 GB7–66 ms
English Wikipedia7.19M9.1 GB128 ms · 2.3 MiB
OSM Québec (geo)6.1M9.5 GB5–13 ms nearest
OSM United States32.8M11 GB7–16 ms nearest

Every number comes from committed benchmark scripts you can rerun, with quality checked against Lucene and Tantivy oracles. See the methodology →

Planet-scale, still static

Geographic sharding splits a corpus into independently built and updated region indexes that merge into one engine at query time — with rankings proven identical to a single monolithic build. All of OpenStreetMap fits in ~310 shards, updates nightly as kilobyte-sized deltas, and serves from object storage for the price of a coffee.

How sharding works →
Try the live map demo →

integrations/

Drops into your stack

Eleventy

eleventy-plugin-rangefind indexes the built site and registers a search shortcode. This site uses it.

Astro

rangefind-astro hooks astro:build:done and ships a <RangefindSearch /> component.

Docusaurus

docusaurus-plugin-rangefind replaces hosted search on docs sites with your own static index.

MkDocs

mkdocs-rangefind on PyPI builds the index after mkdocs build and injects the widget.

Hugo & anything else

Copy-in layouts for Hugo — or run npx rangefind build ./public after any generator. If it makes HTML, it's indexable.

Node / MCP / SSR

rangefind/node runs the same engine server-side over local directories or remote indexes, with disk caching.

$ npm install rangefind

Two ways to start

Have a built site?

Index the HTML you already generate, then mount the web component. Titles, headings, and body text are extracted automatically; data-rangefind-* attributes refine scoping, facets, and metadata.

npx rangefind build ./dist
<script type="module" src="/_rangefind/rangefind-search.js"></script>
<rangefind-search src="/rangefind/" hotkey></rangefind-search>

Have structured data?

Describe fields, weights, facets, and geo points in a config; feed JSONL; query with the full API — filters, sorting, geo, vectors, and pagination included.

npx rangefind build --config rangefind.config.json
import { createSearch } from "rangefind";
const rf = await createSearch({ baseUrl: "/rangefind/" });
const hits = await rf.search({
  q: "harbor",
  facets: ["topic"],
  geo: { near: { lat: 46.8, lon: -71.2, radiusMeters: 5000 } }
});

Read the documentation Star on GitHub