How published snapshots reach browsers: the public R2 domain for bulk reads, and the transit-data-proxy Worker for same-origin snapshot access and the per-provider KPI summary.

Reference Value
Subject and boundary Routes, response semantics, CORS, edge caching and the /api/v1/kpis contract. The web Worker's own routes are on ‣.
Verified source worker.js, snapshot-response.js, kpis.js, wrangler.toml, r2-cors.json, configure-data-edge.mjs at main 9733eb26d92533f56cdc8bc46b81b93fbb5987c8, checked November 10, 2026.
Support state Implemented. No database connection and no secrets.

Two read paths

Path Used by Behavior
https://data.yesid.dev/v1/... Browser bulk reads and absolute URLs in manifests R2 custom domain. A Cloudflare cache rule caches /v1/*.json while respecting the publisher's Cache-Control
https://transit.yesid.dev/data/v1/... Same-origin reads and the web dev proxy transit-data-proxy reads the SNAPSHOTS binding (transit-snapshots)
https://transit.yesid.dev/api/v1/kpis KPI consumers Computed summary for one provider

R2 CORS allows any origin to GET and HEAD with Range, If-None-Match and If-Modified-Since, and exposes ETag, Content-Range, Cache-Control and related headers.

Proxy routing

Request Response
OPTIONS 204 preflight
Method other than GET, HEAD 405 with Allow
Undecodable path, path outside /data/v1/, a key containing .., missing object 404
/data/v1/sto/* 410 (provider unavailable for public access)
Unknown /api/v1/* Uncacheable 404
/data/v1/<key> Object from R2 with stored metadata, ETag, conditional (304, 412) and single-range (206, 416) support

KPI contract

GET /api/v1/kpis returns snapshotAt, freshnessS, vehicles, avgDelayS, coverage, routesLive, routesTotal and up to five topRoutes (route, vehicles, avgDelayS). HEAD returns the headers only. Breaking field changes need a new endpoint version.

?provider= selects the provider. A supplied value must match an id in v1/providers.json (memoized for one live TTL): an unknown provider returns an uncacheable 404, and an unreadable catalog returns the cold 503. A failed providers.json read is cached for 20 s, so ?provider= requests keep returning 503 for up to 20 s. A request without the parameter defaults to stm and does not read the catalog. Each provider has its own in-isolate state (core memo, in-flight rebuild, route totals, failure cooldown) and its own Cache API key, core-v3/&lt;provider&gt; (kpis.js).

Source under v1/&lt;provider&gt;/ Contributes
live/vehicles.json Vehicle count (vehicles.length, which includes vehicles with no trip, unlike network.json's vehicles in service), live route count, routes ranked by vehicles; its timestamp anchors snapshotAt
live/trips.json Mean of finite whole-minute trip delays (delay_min * 60), not second-level delays, network-wide and per top route
live/network.json coverage_pct as a fraction
static/routes_index.json Bus route count (route type 3), so rail lines without vehicle tracking are excluded; every route when the index carries no types

A source is unusable at age ≥60 seconds (three 20-second live TTLs) or more than 60 seconds in the future. An unusable vehicle anchor returns 503 with Retry-After: 20; unusable trip or network data nulls only the fields that depend on it. Means round once to whole seconds, half away from zero (−30.5 s becomes −31 s).

Rebuilds are shared per isolate and provider and memoized for one 20-second live TTL, kept across isolate recycles with the Cache API. Usable older entries serve while refreshing; failed rebuilds are throttled for 10 seconds; route totals are cached for an hour. Responses are Cache-Control: no-store because freshness is evaluated at serve time, and X-Kpis-Cache reports miss, hit or stale.

Develop and verify

bun run --cwd apps/data-proxy check
bun run --cwd apps/data-proxy test
bash apps/data-proxy/smoke.sh   # production route smoke; CANONICAL_BASE, FALLBACK_BASE, APEX_BASE override hosts

Tests use local R2 and Cache API substitutes. Deployment is on Deployment and release.

Executable v1 agreement

kpiSampleState and the agreement examples define additive v1 shape and hero eligibility. Counts are non-negative safe integers; delay seconds and route totals may be null, coverage is a fraction in [0,1] or null, and timestamps use UTC. Extra fields are allowed. A valid 503 carries error: pipeline_cold, a detail string and Retry-After: 20. Shape failure is distinct from a valid but incomplete 200.

The public probe counts a 200 as live-eligible only with delay, coverage and route total plus at least one top-route delay. A valid incomplete 200 or expected cold 503 is a demo sample; malformed responses are probe failures. This stateless check does not implement a browser hold policy.

Before a cached trip/network lane would newly become null because it crossed 60 seconds, the proxy joins a fresh single-flight R2 rebuild, including on a Cache API cold-isolate hit. On read failure it keeps a usable vehicle anchor, nulls only expired dependent values and respects the 10-second failure cooldown. Fresh vehicle data is required; X-Kpis-Cache is diagnostic, not a freshness guarantee. The cache key includes the current internal schema version and the provider.