Files
leviathan-OVERWATCH/docs
KaraZajacandClaude Opus 5 46aa2e5591 v0.5.15 — second Waze backend: the app protocol, no API key (opt-in)
Until now Waze cost money. The public live-map/api/georss endpoint every
scraper used is fronted by Google's edge and returns HTTP 403 to automated
clients regardless of IP, headers, TLS fingerprint or headless-vs-headful
browser — Waze's own page requests included — so the only way in was OpenWeb
Ninja's hosted feed at roughly $0.005/request.

There is another way in: the protocol the Waze app itself speaks. OVERWATCH
can now register an anonymous Waze account (Waze mints the credentials on
request) and query alerts directly. Free, keyless, live, and polled every 60 s
instead of every 4 min.

It is off by default and stays off until the user picks it, because it is not
a free lunch: the app becomes a Waze client. It holds a Waze account and sends
a position — the protocol layer blurs it by up to 500 m, but it is still
roughly where you are — to a Google service on every poll. In an app whose
whole purpose is knowing who is watching you, that has to be an explicit
choice, so the Settings screen states it in those terms rather than burying it.
OpenWeb Ninja stays the default and is unchanged.

Shape:
- scan/WazeSource.kt is the backend interface. Both clients implement it and
  each sets its own poll cadence, so WazeScanner no longer knows or cares
  which one it is talking to, and neither leaks into scoring or the UI.
- scan/WazeClient.kt (OpenWeb Ninja) is behaviourally unchanged; its alert and
  result types simply moved to the interface.
- scan/wazert/WazeRtFetcher.java is ours: session lifecycle, the encrypted
  anonymous account, the per-day registration cap and the backoff.

Vendored, not written here: scan/wazert/*.java and app/src/main/proto/
waze.proto are the Waze RT protocol layer from highway-radar-sabre-plus (MIT),
kept with its licence beside the code. Changed only in the package name, an
OkHttp-to-HttpURLConnection swap so the app takes no new dependency, and the
removal of the report-submission path — OVERWATCH reads alerts and never
reports one, and the codec's report builder is deleted so it cannot.

Protocol behaviour worth recording, all verified live rather than read from
source (2026-09-21, from a Linux host and an Android 16 emulator):
- The session is stateful. /command sends each alert once, then a removal as
  an old_command string "RmAlert,<uuid>", not a message type. Treat a response
  as a snapshot and the feed goes empty after the first query, so responses
  merge into a cache with a 5-minute soft-delete.
- A fresh account's first /command almost always returns an in-band
  ServerError{504, "Retry"} inside an HTTP 200. It succeeds on retry, so it is
  absorbed rather than counted as a failure.
- Login sets a Waze-Session-Affinity cookie every later request must carry,
  and the session idles out after ~100 s.
- Longitude arrives as unsigned 32-bit micro-degrees and must be mapped back
  to signed, or the western hemisphere lands on the wrong side of the planet.
- Data volume inverts the usual instinct: ~195 KB for a session's first
  response, ~8 KB per query after. Polling faster (60 s, under the idle
  timeout) uses less data than polling slowly, because a re-login re-sends the
  whole viewport. Hence the cadence.
- Registration is capped per device per day, so the account is persisted
  encrypted via SecureStore and reused; a rejected one backs off 30 s → 10 min
  instead of spinning the register loop.

Build: applies com.google.protobuf 0.10.0, the first release compatible with
AGP 9 (earlier ones bind to the removed applicationVariants API), with protoc
and protobuf-javalite 4.35.0. On Android the plugin creates no "java" builtin,
so the lite generator is requested via maybeCreate. Costs ~0.6 MB of APK.

Verified end to end on an Android 16 emulator, not just in a harness:
registered an account, logged in, queried, and raised a real detection —
"Police report @ 250m, 16min ago", score 54, YELLOW. Cross-checked against a
live query: the one report inside the 2 km evaluation radius was 58 minutes
old, past the 45-minute cutoff, and the app correctly showed all-clear until
the position moved next to a fresh one.

Also: radio rows in Settings are tappable across their full width instead of
only on the 20 dp circle, which also fixes the theme picker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RsfSpnwuxCkcXKkD9XH8SR
2026-09-21 22:55:45 -04:00
..