Skip to content
Mark Garrigan

Work › Bibsee

Bibsee

Race timing for where the signal ends.

Live bibsee.com ↗

Role
Owner: the product, its architecture and its review, across the app, the appliance, the installer and the site.
Built
2025-09 → now
History
244 commits across 2 repos
Stack
Bun · SQLite · Go · Docker · Raspberry Pi
[x] Interface[x] API[x] Data[x] Infra[x] Ops

Story

Bibsee started as the timing system for my own race, the Black River Trail Classic. Trail races run where phones lose signal, so Bibsee assumes there is no internet at all.

A Raspberry Pi 500, or any Debian or Ubuntu machine, runs the whole system in Docker and serves it over local HTTPS. iPads on the same network record bib passes. Nothing needs an internet connection on race day.

It timed the Black River Trail Classic in 2025 and 2026. Now I'm turning it into a product for other trail race directors.

Results

  • Timed the Black River Trail Classic in 2025 and 2026.
  • Version 0.27.1 as of October 2026, after 237 commits to the app.
  • bibsee.com is live, hosted on boxx, with a demo and waitlist for other race directors.

System, layer by layer

[x] Interface
Edge templates, Vite, Tailwind 4 and vanilla JS on the laptop and iPads. A Go console (Bubble Tea, Lip Gloss) on the appliance itself.
[x] API
Bun.serve with a JSON API for sync, devices, passes and backups, and server-sent events for live updates.
[x] Data
SQLite with numbered migrations. Passes are an append-only records log.
[x] Infra
One multi-arch Docker image carrying the app and the appliance tools. Local DNS (dnsmasq), a local certificate authority (mkcert) and chrony for time.
[x] Ops
A one-line installer (get.bibsee.com), USB backup and restore, Bats tests for the appliance and Bun tests for the app.

Screens

Screenshot: bibsee.com, the marketing site.
bibsee.com, the marketing site.[Screenshot]
Illustration: Race day: a clock per distance.
Race day: a clock per distance.[Illustration]
Illustration: Import preview: entrants matched to distances.
Import preview: entrants matched to distances.[Illustration]
Illustration: The appliance console on the Pi itself.
The appliance console on the Pi itself.[Illustration]

Decisions

  1. 01

    One image for everything

    The app and the appliance tools ship as one image, so “the management layer cannot drift from the app” and version skew “is not representable.”

  2. 02

    Corrections are new records

    Every pass is appended to a log, and fixing a mistake adds a new record on top of the old one.

  3. 03

    The clock is a risk

    “A Raspberry Pi has no battery-backed RTC… could silently record wrong results.” Bibsee runs three guards, and the T key sets the Pi's clock from a paired iPad.

  4. 04

    Holding the box is the password

    The console shows a rotating PIN: “possession of the physical box is what grants access, rather than a shared secret that leaks over time.”

  5. 05

    A guest on someone else's network

    “Bibsee is a client on someone else's network, not the network itself.” It runs its own DNS and certificate authority on the box.

In the box

  • Race dashboard with a clock per distance
  • Entrants with custom fields
  • Five-step import from a registration export or a spreadsheet
  • Pass editing and a who-finishes flag
  • iPad pairing and time checks
  • USB backup and restore
  • One-file race export
  • Runner results site on a guest network, with a QR brochure
  • Appliance console: updates, Wi-Fi, clock and time zone