3.2 KiB
3.2 KiB
Methodology — how this was verified
Repeatable procedure (executed Oct 13, 2026). Tools: curl, a bash probe script, Python parsing (via Hermes execute_code).
Steps
- Map the target: fetch
https://transit.oyenza.com/cctv+ its JS bundles with curl (Chrome UA). Identify: catalog asset (/assets/asset-cctv-<hash>.json, fetched at runtime), player (hls.js chunk), map (MapLibre,/tiles). - Extract the catalog: download the JSON — 214 cams / 310 views with
[label, hls, ownerPage]per view and acheckedAttimestamp. Group views bysrc(dki/tol/bks/bgr). - Fetch each portal and extract its camera identifiers:
- DKI:
jakcctv.jakarta.go.id/publik/data?q=&page=Nfor pages 1–7; regex thecardsHTML for iframesrcs. - Toll:
bpjt.pu.go.id/cctv/; unescape\/; extract the inline JSON records (streamhls,bujt, lat/lon, status) by position-pairing fields; also raw-regex allhttps://….m3u8matches and strip\r\nartifacts. - Bekasi:
eofficev2.bekasikota.go.id/backupcctv/m3/list.json. - Bogor: BSW
/cctvlist +/cctv/<id>/detail. Geo-blocked from our network → fetched through a public reader proxy (https://r.jina.ai/<url>); the vantage change is noted in findings. A sibling host (restreamer2.kotabogor.go.id) was reachable directly and fingerprinted.
- DKI:
- Cross-match programmatically: set comparisons of view URLs / camera IDs / filenames; compute overlaps (46/46, 190/190, 23/23, 51/51). Where a comparison failed initially, re-check for parsing artifacts before concluding (two "missing" DKI cams and one "missing" toll URL turned out to be regex/escaping artifacts, not real gaps).
- Probe the streams: one request per distinct stream host (12 hosts) with browser-like headers,
Origin: https://transit.oyenza.com; save status + full headers + playlist bytes (probes/). Then hotlink test: repeat a request with noOrigin/Referer→ still 200. Identify server software from headers (mediamtx,Streamer,cloudflare,nginx) and URL shapes (LiveApp/,memfs/,tracks-v1/). - Read the player path in the bundle: locate
fetch(<catalog>), theviewsmapping, the hls.js dynamic import,loadSource, the Safari fallback, and the owner-page fullscreen handler.
Boundaries
- Public, unauthenticated content only; single targeted fetches; no crawling (the site's robots.txt says
Disallow: /), no account creation, no auth/payment bypass, nothing beyond what a visitor's browser does. - Geo-blocked portals were read through a public reader proxy for verification only — labeled as such.
Key results
- 100% catalog↔portal agreement on all four sources (DKI 46/46 views, Toll 190/190 views, Bekasi 23/23, Bogor 51/51 detail IDs).
- 12/12 stream hosts live at capture; CORS
*; no Origin/Referer checks. - BPJT page: 1,530 records / 1,415 unique URLs; oyenza's toll subset = 9 operators, 154 cams.
Artifacts
- Archive:
workspace/cctv-jabodetabek-captures/(3.3 MB, 55 files): catalog JSON, page+bundle dumps, portal dumps (portals/), probe headers/bodies (probes/),probe.sh. - Scratch copies live under
~/.hermes/profiles/research/cache/scratch/cctv-audit/(auto-pruned after ~24 h idle — use the archive instead).