One file, read in ranges
A reader fetches a file's headers and the small sections it needs to plan queries, then reads only the byte ranges a query touches. The file can live on local disk, behind any HTTP server that answers Range requests, or in object storage such as R2 or S3. There is no database and no server-side index to load.
Parts
A .glx file starts with a 256-byte file header: the magic GLX0, the version 1, the number of parts and a parts table. Each entry in the table names a part's kind, its byte range in the file and an XXH3-64 checksum of its bytes. A file holds at most seven parts, and at most one of each kind:
| Kind | Part |
|---|---|
SRCH | The search part: the geocoding index these pages describe |
ROUT | The routing part: a road graph for geolith Routing, in development |
- Each part starts with its own header. The search part starts with a 256-byte search header (its document count, ranking constants, feature bits and the table of its thirteen sections). The routing part starts with a 64-byte routing header.
- Offsets are counted from the start of the part. Every offset inside a part is relative to that part, so a part's bytes are the same in any file that holds it. The first part starts at byte
256; a search part, when present, comes first, so its header is in the first 512 bytes. - A reader checks the parts table. It refuses a file with the wrong magic or version, no parts or more than seven, a kind listed twice, or parts that overlap. A search reader finds the
SRCHentry and ignores the rest; a file without a search part is refused, with a message that names the kinds the file does hold. - Checksums are checked when parts are copied. A reader serving queries over HTTP reads ranges, not whole parts, so it does not check the checksum. A tool that copies a part does.
- Parts can be split and merged.
glx-parts info <file>lists a file's parts and checks their checksums.glx-parts merge <out> <in>...copies the parts of several files into one, checking each part's checksum first. See Command line.
The search file the hosted API serves, planet-search-2026-11-05.glx, has this layout, and @geolith/glx reads it from version 0.2.0. The format is still version 1: the parts table changed the version 1 specification in place, and the version field stays 1.
What the search part holds
- Records. Each place is stored once, in zstd-compressed blocks of documents: names, address parts, layer, country, coordinates and, when the builder wrote them, the place's outline.
- A term dictionary and posting lists. A posting holds the document number, the term frequency (always 1; its high bit marks a word of the name) and the place's length, importance, layer and country: enough to rank and filter a candidate against every query word without reading its record.
- Prefixes for autocomplete. Posting lists for word prefixes, so a query can be answered while the user types.
- A spatial section. Places in Hilbert order, for reverse geocoding and nearby queries.
- An id section. Ids sorted in blocks, so a place is found by its id in three reads.
The specification defines every section byte by byte.
Ranking
Ranking is BM25 over the folded words of each place's search text, each word counted once per place, plus a weight for the share of query words found in the place's names, plus the place's importance times a weight. A word in the name counts more than a word found only in the address, as in Pelias and Photon. The ranking lives in the reader, so every consumer of the same file (the Search API, a Cloudflare Worker, the browser) gives the same answers.
Readers
- Rust: the
geolith-glxcrate (imported asglx), with the writer and the command-line tools. - WebAssembly and JavaScript:
@geolith/glx, the same Rust reader compiled to WebAssembly, reading from R2, from HTTP range requests or from a localFile. It ships two builds:glx.wasm, andglx-parser.wasmwith a trained query parser for kind-of-place queries such as "dhabas at Whitefield". - HTTP: geolith Search serves a Pelias-compatible API from one file.
Measured
India, OpenStreetMap and Overture places, 5,466,755 records, file on local disk, 200 queries per class:
| Class | Top-1 within 1 km | Read rounds p50 / p95 | Bytes read p50 |
|---|---|---|---|
| Cities and towns | 95.5% | 2 / 2 | 1.4 MB |
| Addresses | 72.0% | 2 / 3 | 1.2 MB |
| OSM places of interest | 72.5% | 2 / 3 | 0.9 MB |
| Overture places | 89.5% | 2 / 3 | 1.0 MB |
| Autocomplete | 74.5% | 2 / 4 | 0.8 MB |
The India file is 871 MB. The planet file the hosted API serves, built on 8 Nov 2026, holds 686,829,301 places in 36.35 GB.