Migrating Rockbox Search to Typesense
Rockbox Zig's search just got a significant overhaul. I replaced the embedded Tantivy full-text search engine with Typesense, an open-source, self-hosted search server. This post covers what changed, why we made the switch, and how the new system works end-to-end.
The Old Approach: Tantivy
The previous search was powered by Tantivy, a Rust full-text search library that runs embedded inside the Rockbox process. The implementation lived in a dedicated /crates/search/ crate and maintained separate on-disk indexes for each entity type:
Indexes were stored on the filesystem under ~/.config/rockbox.org/indexes/ using Tantivy's MmapDirectory. Each entity type had its own schema definition, and search queries ran against these local indexes synchronously inside the Rockbox server process.
The problems:
The New Approach: Typesense
Typesense is a standalone search server written in C++ that exposes a clean REST API. Instead of embedding search logic in the Rockbox process, we now run Typesense as a sidecar process and talk to it over HTTP.
How Typesense is provisioned
The new /crates/typesense/ crate handles setup. On startup, it:
Both macOS (darwin) and Linux are supported. The whole provisioning step is transparent — users don't need to install or configure anything separately.
Collection schemas
Three Typesense collections mirror the main entity types:
Tracks
path : string, unique document ID
title, artist, album, album_artist: string, searchable
genre, composer: string
bitrate, frequency, filesize, length: int
track_number, disc_number, year: int
md5: string, content hash
album_art, artist_id, album_id, genre_id: string, optional
Default sort: title. Query fields: title, artist, album, path.
Albums
title, artist: string, searchable
year: int
md5, artist_id: string
album_art, label: string, optional
Artists
| Field | Type | Notes |
|---|---|---|
| name | string | searchable |
| bio, image | string | optional |
Data Flow
Library scan and indexing
When Rockbox scans your music library it:
POST /collections/{collection}/documents/import?action=upsert
X-TYPESENSE-API-KEY: <key>
The upsert action means re-scanning the library is idempotent — existing documents are updated in place rather than duplicated.
Search queries
The HTTP search handler sits at:
GET /search?q={query}
It fans out to three parallel Typesense requests, one per collection, using query_by=title,artist,album,path for tracks, query_by=title,artist,label for albums, and query_by=name for artists. The responses are merged and returned as a single JSON payload:
{
"tracks": [...],
"albums": [...],
"artists": [...]
}
The same data is also available through the GraphQL API as a SearchResults type with tracks, albums, artists, liked_tracks, and liked_albums fields.
What Changed in the Client
The most visible sign of the improvement is what we were able to remove from the macOS app. With Tantivy, searches were slow enough to require explicit loading UI:
// Before
@Published var isLoading: Bool = false
Typesense returns results fast enough that the loading state and spinner are simply gone. The macOS SearchManager now fires a 300 ms debounced query and the results appear — no spinner, no progress view.
Configuration Reference
RB_TYPESENSE_API_KEY: auto-generated UUID, Authentication key RB_TYPESENSE_PORT: 8109, Port for the Typesense server RB_TYPESENSE_VERSION: 30.1, Version to download if not in PATH ROCKBOX_UPDATE_LIBRARY: Set to 1 to force a library rescan on startup
You can also trigger a rescan with index rebuild at any time via the HTTP API:
POST /system/scan_library?rebuild_index=true
Summary
| | Tantivy (before) | Typesense (after) |
|---------------|----------------|-------------------------|
| Deployment | Embedded in process | Sidecar server (auto-provisioned) |
| Storage | ~/.config/rockbox.org/indexes/ | ~/.config/rockbox.org/typesense/ |
| Index format | Tantivy segments (mmap) | Typesense native |
| Import format | Tantivy documents | JSONL batch via REST |
| Search API | In-process function call | HTTP REST |
| Client loading UI | Required | Removed |
| Platform | Rust only | OS binary (macOS + Linux) |
The migration removes an entire crate from the project, replaces it with a thin HTTP client, and delivers noticeably faster results. Typesense stays self-hosted and open-source, so there are no external dependencies or data leaving your machine.
The full implementation is available at github.com/tsirysndr/rockbox-zig.
Discussion in the ATmosphere