# Methodology — how this was verified Repeatable procedure (executed Oct 11–12, 2026). Tools: `curl`, `node` (packages `ws`, `mqtt`), `jq`. ## Steps 1. **Map the target:** fetch the site's HTML + JS bundles with curl; read the CSP header (`connect-src` names allowed WebSocket endpoints); grep bundles for protocol strings → found oyenza's own WS (`/api/live`) and its message types (`hello/feed/snap/delta`). 2. **Find static-data citations:** `static.json` referenced the official GTFS zip URL → static layer source identified. 3. **Find the upstream:** search GitHub for projects in the same ecosystem (`tj-live`, `open-tj`) → they document the REST + MQTT + GTFS stack. 4. **Simultaneous cross-capture:** - `oyenza-ws.mjs` — WS client on `wss://transit.oyenza.com/api/live` - `mqtt-cap.mjs` / `mqtt-long.mjs` — MQTT client on `wss://mqtt.tj.co.id:8084/mqtt`, subscribing `/mobile_armada/#` - Analysis with `compare.mjs` / `compare2.mjs` (ID sets, field agreement, position deltas), `seg1.mjs` (topic segments), `mqtt-rate.mjs` (rate profile). 5. **Decode the topic grid:** four `POST /v1/bus` probes at ±0.1° offsets → index-shift mapping (≈0.01°/unit). 6. **Cadence check:** re-connect to oyenza's WS and record delta intervals (`oy2.mjs`) → exactly ~3.00 s. ## Key results - 1,794 common buses; 100% trip/route ID match; median position delta 8.9 m; p90 42 m; 97.8% fleet overlap (75 s window). - Broker: ~2,000+ retained messages instantly on subscribe; per-bus refresh ~8–75 s in samples. - oyenza deltas: 3000–3013 ms gaps, ~1,000 position records each. ## Artifacts - All scripts + raw captures archived at `workspace/transjakarta-live-captures/` (4.6 MB): oyenza page + bundles, oyenza WS captures, MQTT captures, comparison outputs, rate profiles, tije REST responses (guest token redacted). - Scratch copies live under `~/.hermes/profiles/research/cache/scratch/oyenza/` (auto-pruned after ~24 h idle — use the archive instead). - Reusable snapshot tool: [[transjakarta-live-data/scripts/tj-snapshot.mjs|scripts/tj-snapshot.mjs]]