Skip to content

fix(gps): coarse live broadcast precision by default, full is opt-in - #16

Merged
clorth0 merged 1 commit into
mainfrom
fix/geotag-precision-default
Oct 18, 2026
Merged

clorth0 merged 1 commit into
mainfrom
fix/geotag-precision-default

Conversation

@clorth0

@clorth0 clorth0 commented Oct 18, 2026

Copy link
Copy Markdown
Owner

⚠️ User-visible change (flagged first)

With GPS on, the live map/GPS pill and any location in socket payloads now show ~1 km coarse position by default, not your exact position. Captures on disk are not degraded (see below). To restore exact live position, set Diagnostics → "Live broadcast precision" → "Full — shares exact location live". That full setting shares your exact location with everyone who can connect (any local process on loopback with no token, or any token holder).

Addresses parked SECURITY question #4 only. #5 (capture routes / absolute paths) is untouched — no path exposure changed here.

Evidence (stored vs broadcast — they were already two paths)

  • GPS source: backend/gps.py — local gpsd (127.0.0.1:2947, AETHERSCOPE_GPSD_HOST/PORT), opt-in gps_enabled (default off, AETHERSCOPE_GPS=0 hard-off). No config-file lat/lon receiver position in the GPS path (ADS-B/scan rx_lat/rx_lon inputs are separate, operator-typed).
  • Levels (gps.py, _PRECISION_DECIMALS): full = exact, 100m = round 3dp (~0.001°), 1km = round 2dp (~0.01°). (Note: task brief said config.py:125; in this tree the default lives in app.py get_setting("gps_precision", "full") — config.py holds only the demod vocabulary.)
  • Stored: _receiver_geotag() (app.py, IQ captures + ISM-contact receiver location) and audio-record start both applied coarsen_geolocation(geo, gps_precision default "full") into sidecar/SigMF (sigmf.py core:geolocation) and the contacts DB. Per-capture redaction already exists.
  • Broadcast (the leak): gps_status was emitted raw full precision from three sites — GpsClient(on_status=…) on every TPV/SKY, _emit_gps_status(), and the connect snapshot — to all connected sockets (socketio.emit, no room scoping). Frontend used it for the ADS-B map receiver pin (aether-adsb.js), GPS-pill hover coords (aether-core.js, toFixed(6)), and "Use GPS location". The captures list also emitted the stored geolocation, and ISM contacts emitted receiver lat/lon; ADS-B contacts/aircraft carry the aircraft's position, not the receiver's.
  • Setting today: Diagnostics <select id="gps-precision"> via set_setting (allowlisted), persisted in store.py settings; its label honestly said stored-only ("The live map shows full precision") — that live full precision is exactly Publish one current job with a required status label #4.
  • fix(security): give Aetherscope an explicit auth boundary #15 auth interaction: token set → connect is gated; loopback with no token still trusts any local process — stated honestly, that is why broadcast, not just storage, needs a safe default.

Decision

  • Split at the emission seam (gps.py is the deep module; broadcast_position/broadcast_geolocation are its interface, unknown values fail closed to coarse):
    • Broadcast/live (gps_status, emitted capture geolocation, ISM-contact receiver lat/lon): new gps_broadcast_precision, default 1km (the coarsest existing level), 100m/full explicit opt-ins. All three gps_status sites unified through _broadcast_gps_position().
    • Stored (capture sidecar/SigMF, contacts DB): unchanged — gps_precision default full, operator's configured choice preserved. Recordings are not silently degraded.
    • ADS-B aircraft positions are not receiver location and are not coarsened.
  • Migration: no persisted-config migration. Existing gps_precision values keep their exact stored meaning. Existing installs have no gps_broadcast_precision, so they get coarse broadcast (the security fix) until they explicitly opt into full; new installs same default.

Tests (TDD, failing-first)

  • New tests/test_geotag_broadcast.py (7 tests, written first — ImportError red): default emitted payload rounds 38.723298→38.72 and ≠ raw, stored coarsen…("full") unchanged, explicit full opt-in emits exact, 100m rounds 3dp, unknown value fails closed, no-fix passes through, and a source guard that no raw gps_status emission remains + allowlist contains the new key.
  • Mutant: forcing _broadcast_precision to "full" → 4/7 red; restored → 7/7.
  • Guard update: tests/test_waterfall_split.py intentional drift noted inline (new gps-broadcast-precision DOM id, socket.emit sites 47→48 for the new control; event-name sets unchanged).

Verification (CI-way, local snapshot)

  • All tests/test_*.py pass (incl. gps 13/13, broadcast 7/7, waterfall 7/7 after guard note, auth 12/12).
  • check_html_templates 0 untagged, check_architecture_names pass, node --check all frontend JS pass.
  • GitHub CI: see checks on this PR.

Self-review (pocock-code-review, Standards/Spec separately): Standards — emission logic in one place in gps.py, callers only name the setting, no new module (architecture-names intact, architecture.md updated), frontend is one select + restore wiring. Spec — #4 only, stored preserved, no #5/path changes, absolute-path fields untouched.

Split stored from broadcast geotag precision at the gps.py emission
seam. Stored captures keep gps_precision (default full, operator's own
files); gps_status, emitted capture geolocations and ISM-contact
receiver locations now cross to all sockets at gps_broadcast_precision
(default ~1km, fail-closed on unknown), full only on explicit opt-in.
Existing gps_precision choices are untouched; existing installs get
coarse broadcast until they opt in.
@clorth0
clorth0 merged commit 0777d12 into main Oct 18, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant