Developer portal · Architecture
Trackser Live runs entirely on Cloudflare's edge — one Worker, one Durable Object per line, and object storage. No origin servers, no databases to babysit.
The system is a single Cloudflare Worker (trackser-live)
with one Durable Object class, LineBuilderDO. There is no
Node server, no container, and no relational database. Everything —
ingestion, processing, storage and serving — happens within Workers.
Each Underground line is owned by its own LineBuilderDO
instance, addressed by the line ID. That DO holds the line's
persistent state — direction hints, vehicle-ID memory, stall history —
and is solely responsible for building that line's snapshots. The DO is
SQLite-backed, so its state survives restarts and deploys.
Isolating state per line means a problem on one line can't corrupt another, and each line rebuilds independently.
Every build cycle, each line's DO runs the same pipeline:
cron / alarm
│
▼
LineBuilderDO.buildOnce()
├─ fetchTrackerNet() → per-station TfL TrackerNet (XML)
├─ fetchUnified() → TfL Unified /Line/{ids}/Arrivals (JSON)
├─ mergeTrains() → collapse many sightings → one record per train
├─ normalizeAndEnrich()→ direction, vehicle ID, stall state (uses DO hints)
└─ applyMetStoppingPattern() → Metropolitan working-timetable overlay
│
▼
publish gzip JSON → R2 + archive a snapshot
The result is a compact, enriched snapshot written to R2, ready to be served. Clients never trigger this work — they only read the latest published snapshot.
buildNowAndHalf action out to every line's DO.An incoming API request passes through these stages:
202 warming and triggers a build.State is spread across R2 buckets and KV namespaces, each with a clear role:
| Binding | Type | Role |
|---|---|---|
R2_BUCKET | R2 | Live snapshots and the replay archive. |
COLD_BUCKET | R2 | Aged-out replay archive (cold storage). |
TIMETABLES_R2 | R2 | Working-timetable files. |
API_USAGE_R2 | R2 | Daily per-key usage records. |
API_STATUS_R2 | R2 | Structured logs (JSONL streams). |
API_KEYS_KV | KV | API key store. |
API_USAGE | KV | Usage counters and rate-limit windows. |
TIMETABLE_SCHEDULE | KV | Per-line, per-date timetable overrides. |
ADMIN_NOTICE | KV | The status-page notice banner. |
RELEASES_KV | KV | Release history behind /releases. |
Upstream TfL credentials and per-line heartbeat tokens live in Cloudflare Secrets Store, never as plaintext variables.
Every map snapshot is archived. Full-resolution data is kept for
14 days in the primary bucket, then downsampled and
moved to cold storage beyond that. Per-day journey records are retained
separately. A companion trackser-cleanup Worker applies the
retention policy; this system only exposes its policy controls.
Cache behaviour depends on the data class:
| Data class | Cache-Control |
|---|---|
| Live snapshots, replay reads | no-store, max-age=0 |
| Historical & timetable data | public, max-age=3600 |
| Station registry | public, max-age=86400 |
| Stats & logs | public, max-age=300 |
X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin and a one-year Strict-Transport-Security header.Vary: Origin; other origins receive *. Accepted methods are GET, POST, PUT, DELETE, OPTIONS.
Two environments run side by side — production
(trackser.app) and preview
(preview.trackser.app) — with separate R2 buckets and
shared KV where appropriate.
Want to see the pipeline's live health? The status page reports per-line build freshness and independent uptime monitoring.