github Kpa-clawbot/CoreScope v3.10.0
CoreScope v3.10.0

3 hours ago

CoreScope v3.10.0

Upgrade urgency: High for anyone running a map. CARTO began requiring an API key on its raster basemaps in August 2026, so map tiles on v3.9.2 come back watermarked "API KEY REQUIRED" with an HTTP 200. Nothing errors and no healthcheck fires. This release supports the key; you have to add one. It also restores relay last_seen, which has been a no-op since the read-only DB refactor.

111 commits since v3.9.2 (2026-06-13). No auto-generated coverage bumps in this range, so all 111 are substantive. Every bullet ends with a commit SHA: git show <sha> to verify.

Highlights

  • Map tiles work again, once you add a CARTO key. Since 2026-08 CARTO answers unauthenticated tile requests with a watermarked image and HTTP 200, so this failed silently on every instance. map.tiles.providers.carto.key is now applied on every map surface rather than only the main map. Free keys need no account and no card. Verify by looking at a tile, not at a status code. (#1919, 7aa60c0; #1926, 8ce5291)
  • Relay last_seen is written again. The server-side touch had been a no-op since the mode=ro refactor, so a repeater's last_seen had quietly degraded into "when did it last advertise". The write moved to the ingestor. Measured on a live instance: for repeaters that relayed within the last hour, the gap between last_relayed and last_seen drops from a median of 12,062 s to 193 s, and the share more than 5 minutes behind falls from 96% to 39%. (#1854, 6528c7b)
  • RF telemetry from mobile clients, opt-in. Two independent streams: full-packet RF observations, and RF environment samples (noise floor, RX/TX airtime, CRC errors, packet counters) paired with a GPS track. Both default OFF, each independently gated, each with its own retention window. (#1905, 9e13e0b; #1906, 9ae3387)
  • A path-trust threshold for hop attribution. A 1-byte hop prefix has only 256 possible values, which is weak evidence on a large mesh. pathTrust.minHashBytesForMapping now gates neighbour-edge creation and every display consumer. It ships at default 1, which is exactly the existing behaviour: raising it is an explicit operator decision, not something an upgrade does to you. (#1841, 8c48f2b; #1863, e8f32df; #1929, 176bb53)
  • The E2E suite tells the truth again. Three separate "flaky tests" turned out to be real product bugs, each one something operable before its own setup had finished: the Live view toggles are inert for about 100 ms after paint, the colour picker's deferred focus undid arrow-key navigation so Enter assigned the wrong colour, and an analytics theme-refresh silently discarded the filter you had just applied. All three are fixed, each with a regression test that fails on the previous commit. (#1940, ab62e86; #1945, 5d2e14a; #1944, e2df9bb)

What is new

Mobile client RF telemetry (opt-in, default OFF)

  • Full-packet RF observations from mobile clients, stored in client_rx_observations. (#1905, 9e13e0b)
  • RF environment samples on meshcore/client/<pubkey>/rf, stored in client_rf_samples: noise floor, RX and TX airtime, CRC errors and packet totals, paired with a GPS point. Absolutes only; deltas are derived at query time so a lost sample costs one interval instead of corrupting a running total. (#1906, 9ae3387)
  • Crowdsourced client-RX coverage, plus /api/nodes/resolve. (#1728, 22fe929)

Map

Nodes and export

  • Export the visible node list as MeshCore companion contacts JSON. (#1889, aabbd50)
  • flood_advert_count_7d on the node detail endpoint. (#1831, d2ef624)
  • unscoped_relay_count_24h per node. (#1823, bd0a58e)
  • Transported region scopes in the repeater sidebar. (#1752, fc26fb6)
  • Transport region scope on Packets: a detail-pane row and a sortable Scope column. (#1894, 376c3e9)

Analytics

  • "My Repeaters" favourites monitoring dashboard. (#1761, 4654ce3)
  • Repeater metric scatter tab. (#1760, 7402e8d)
  • The four-axis repeater usefulness score from #672 is complete. (#1762, 3efa37c)
  • Per-node usefulness metrics documented in OpenAPI. (#1769, 17654dd)

Live and packets

  • Optional "Multibyte only" view filter. (#1780, 5c0de8f)
  • payload.destHash and payload.srcHash in filter autocomplete. (#1774, b3b8bec)
  • A routed_through filter, clearer path semantics, and a fixed hex lexer error. (#1800, 5e09614)
  • Group Data (payload type 6) added to the type filter, with its channel hash and inner fields decoded. (#1791, 770749a; #1792, d5ceb27)
  • CONTROL DISCOVER_REQ and DISCOVER_RESP subtypes decoded. (#1802, b3189c6)

Retention

  • observerPurgeDays: hard-delete observers that are already inactive and unseen for N days, but only when no observation, metric or dropped-packet row still references them. Disabled by default. (#1886, d821d9a)

Fixes

Correctness

  • A retained MQTT status message is not observer liveness. Without this, last_seen was measuring broker restarts. (#1885, 9bd5f5a)
  • Do not attribute a transported scope from a 1-byte hop prefix. (#1902, 4c45dec)
  • Key the zero-hop advert skip on the path byte rather than the route type. (#1913, 97b6090)
  • Keep resolved full-pubkey hops across a path-hop index rebuild. (#1904, f081f91)
  • Decode the ANON_REQ source pubkey instead of treating it like REQUEST. (#1864, 0d6f59a)
  • Restrict per-node clock skew to self-originated adverts. (#1816, #1818, 0352c9a)
  • Count only live observers in the store's /api/stats query. (#1888, b3a306b)
  • Preserve the firmware-default Public channel (0x11) in analytics. (#1729, 6a32ec2)
  • detectSchema fails loud instead of caching the wrong schema mode. (#1901, 1441734)
  • Relay Airtime Share uses preamble-aware LoRa time-on-air. Partial fix. (#1768, 5795671)
  • Clear a stale "varies" hash size once a node settles. (#1726, f0763ae)

MQTT and ingest stability

  • Decouple the watchdog emit from blocking I/O. This is the root cause behind #1749. (#1749, 52d0821)
  • Escalate a persistent paho disconnect, recover from an emit panic, expose the watchdog tick. (#1749, 242c7c6)
  • Stop the watchdog force-reconnect from racing paho's own retry loop. (#1897, 647841c)

Security

  • WebSocket CheckOrigin allowlist, blocking cross-origin scrapers. (#1793, ec0ebed)
  • Drop the hardcoded og:url so shared links stay on your instance. (#1890, c5a71b3)

Interface

  • Relay-aware staleness for infrastructure nodes: dim rather than delete. (#1598, 4fc42d3)
  • Restore the Live map viewport from the lat, lon and zoom hash parameters. (#1709, 4d2033d)
  • Surface the Coverage route in the mobile navigation when it is enabled. (#1783, 55e203a)
  • Cross-navigation links between observer and node detail pages. (#1825, ba68069)
  • Dark-theme role swatches via per-theme CSS tokens. (#1715, 735d9eb)
  • Do not re-render node dots while scrubbing the Live timeline. (#1754, 1adb011)
  • Firmware and Client columns on the observers table. (#1789, 9757178)
  • Accessibility: WCAG AA contrast repairs and expanded axe route coverage across the analytics tabs. (#1719, a344ae0; #1705, 293efdb; #1706, cbe6e94)

Performance

  • Prepared statements for frequently-called server DB queries. (#1878, 2f711eb)
  • Index, cache and deflake the /api/channels queries. (#1887, 89544b1)
  • Use the cached ParsedDecoded() instead of repeated json.Unmarshal. (#1871, f49e3fc)
  • Avoid a per-observation SQL fetch in the handleObserverAnalytics hot loop. (#1827, 1720060)
  • Reuse the ctx buffer in resolvePathForObs and cache ReadMemStats per store. (#1873, ac6fbaf)
  • Replace idx_tx_last_seen with a partial index on last_seen = 0. (#1740, e465e1c)
  • Cap the Live animation canvas at DPR 1.5 and redraw at about 60 fps. (#1737, c03f2eb)

CI

  • Unblock the master pipeline: gate the staging deploy and detach the badges job. A hung self-hosted job had been holding every master run open. (#1938, 859173f)
  • Pin the packets time window in the slide-over E2E. The fixture aged out of the 15-minute default window before the flake gate ran, which had been failing unrelated PRs for weeks. (#1923, 589fa98)
  • Report the correct version on fast-path retagged images. (#1807, 9ef4179)

Operator notes

The first one needs action; the rest are defaults you may want to reconsider.

  1. Add a CARTO key. Set map.tiles.providers.carto.key in your config. Without it every tile is served watermarked with HTTP 200, so no error and no failing healthcheck will tell you. The query parameter is key; api_key is silently ignored and still returns the watermarked tile. The key is sent to the browser, so restrict it in the CARTO dashboard if your deployment allows that.

  2. pathTrust.minHashBytesForMapping defaults to 1, which is the behaviour you have today, so an upgrade changes nothing on its own. Raising it to 2 makes hop attribution stricter and stops counting 1-byte prefix hops as mapping evidence, including legacy persisted edges with prefixBytes == 0. That is not a small change: on one live instance 56% of path-hop observations are 1-byte and 41% of repeaters use a 1-byte hash. There is no UI to undo it, which is why the default was left where it was.

  3. New opt-in tables, all default OFF, each with its own retention window: clientRxCoverage, clientRxObservations and clientRfSamples, bounded by retention.clientRxDays, retention.clientRxObsDays and retention.clientRfDays. Enabling a stream without setting its retention window means unbounded growth.

  4. observerPurgeDays is new and disabled by default. Set it above both observerDays and packetDays: below those, the reference guards keep every row anyway.

  5. WebSocket origin allowlist. If you embed CoreScope from another origin, allowlist it or the socket is refused.

Contributors

115 pull requests merged since v3.9.2, from 13 people.

PRs
Kpa-clawbot 46
efiten 26
ArcanConsulting 15 includes #1771, merged as #1928
SaarMesh-Bot 7 includes #1863, merged as #1930
Joel-Claw 4 includes #1878, merged as #1934
Jonher937 4 includes #1887, merged as #1936
Saarlandpower 4
TeTeHacko 3
nullrouten0 2 #1916 and #1917, merged as #1926 and #1935
Bjorkan 1
MarekWo 1
djkazic 1
h4badger 1

Six of these were rebased onto master by someone other than their author, because the original branch had gone stale while the queue was stalled. The rebase preserved authorship on the branch, but this repository squash-merges, and a squash takes the pull request author as the commit author. So git log credits the rebaser for all six. The table above corrects that, and the third column names which PR the work actually came from.

That matters most for nullrouten0, who has zero merged pull requests under their own name in this release, yet wrote the CARTO basemap fix that this release leads with. The same applies in smaller measure to ArcanConsulting, SaarMesh-Bot, Joel-Claw and Jonher937.

Counted by pull request, not by commit, and restricted to what merged after v3.9.2 was tagged on 2026-06-13.

Don't miss a new CoreScope release

NewReleases is sending notifications on new releases.