Skip to content

feat(rx-coverage): Uplink layer, did the mesh hear the companion - #21

Merged
efiten merged 1 commit into
masterfrom
feat/uplink-coverage
Oct 16, 2026
Merged

efiten merged 1 commit into
masterfrom
feat/uplink-coverage

Conversation

@efiten

@efiten efiten commented Oct 16, 2026

Copy link
Copy Markdown
Owner

Summary

Map side of uplink pings (ingestor side: #20). The Mobile RX coverage page gets an Uplink layer that shows, per hex, whether the mesh heard the companions that pinged there.

  • GET /api/uplink-coverage (cmd/server/uplink_coverage.go), gated on clientUplink.enabled. Each ping gets a class:

    • both: a repeater re-broadcast was heard by the companion, and an observer received the ping
    • observed: an observer received it, no echo
    • echo: echo, but no observer received it
    • none: neither

    "Observer received it" means transmissions.hash matches the ping's hash. That hash comes from the echo, or from the ingestor's own decryption (client_uplink_tx). Per cell the endpoint returns the counts per class, the best class, the first-hop repeaters, the most observers reached, the longest path and the time of the last ping.

  • /api/config/client exposes clientUplink; roles.js sets MC_CLIENT_UPLINK.

  • rx-coverage.js:

    • an "Uplink" layer button, also shown without clientRfSamples
    • a four-class legend, with the caveat that "not received by an observer" can mean no observer is in range
    • a tooltip per cell
    • a layer=uplink deep link
    • theme-token colours (--nq-up-*), light and dark

Performance

One bbox query on client_uplink_pings (indexed on lat/lon and sent_at), plus one arrivals query per 500 distinct hashes over transmissions(hash) joined to observations(transmission_id), both indexed. Pings are rare (paced to at most 50 per hour per companion), so the row counts are small. Not benchmarked.

Test plan

  • Go: class mapping, aggregation, endpoint (echo hash and tx hash, days window, no-position pings, 404 when off, 400 on a bad bbox, empty when the tables are absent). Full server suite passes.
  • test-all.sh: all unit suites pass. New: test-rx-coverage-uplink.js.
  • E2E test-rx-coverage-uplink-e2e.js against a static server with a mocked API: 3/3.
  • XSS gate on rx-coverage.js and roles.js.
  • Live, in the browser: the cell from the field ping cdrx ebd842fb (3 echoes, 17 observers) should show "both directions".

🤖 Generated with Claude Code

The ingestor stores CoreDrive RX uplink pings since b146a87 (#20). This
serves and draws them.

- GET /api/uplink-coverage (cmd/server/uplink_coverage.go), gated on
  clientUplink.enabled. Per ping the class is:
    both      the companion heard a repeater re-broadcast it AND an
              observer received it (transmissions.hash = the ping's hash)
    observed  an observer received it, no echo
    echo      echo, no observer received it
    none      neither
  The hash comes from the ping (echo) or from client_uplink_tx (the
  ingestor's own decryption). Per hex cell: counts per class, best class,
  first-hop repeaters (max 12), most observers reached, longest path,
  last ping. One arrivals query per 500 hashes; empty collection while
  the tables do not exist yet. Documented in openapi.go.
- /api/config/client exposes clientUplink; roles.js sets MC_CLIENT_UPLINK.
- rx-coverage.js: "Uplink" button in the layer bar (also without
  clientRfSamples), legend with the four classes and the caveat that
  "not received by an observer" can mean no observer in range, tooltip
  per cell, layer=uplink deep link. Colours as theme tokens
  (--nq-up-*, light and dark).

Tests: Go (class mapping, per-cell aggregation, endpoint with echo-hash
and tx-hash pings, days window, no-position pings left out, 404 when off,
400 on bad bbox, empty when tables absent), unit test for colour and
tooltip, E2E test-rx-coverage-uplink-e2e.js (no button when off, cells and
legend, deep link). Full server suite and all test-all.sh unit suites
pass; XSS gate passes on rx-coverage.js and roles.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@efiten
efiten merged commit 08afb72 into master Oct 16, 2026
12 checks 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