# Concepts

> How a .glx file is laid out (a file header with a parts table, then a search part and an optional routing part) and how a reader answers search, autocomplete, reverse and id queries with a few HTTP range requests.

## 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](/routing), in [development](/routing/status) |

- **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](/routing/routing-part).
- **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 `SRCH` entry 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](/glx/cli#glx-parts).

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](/glx/spec) 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](/search), a Cloudflare Worker, the browser) gives the same answers.

## Readers

- **Rust:** the `geolith-glx` crate (imported as `glx`), 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 local `File`. It ships two builds: `glx.wasm`, and `glx-parser.wasm` with a trained query parser for kind-of-place queries such as "dhabas at Whitefield".
- **HTTP:** [geolith Search](/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.
