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.keyis 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_seenis written again. The server-side touch had been a no-op since themode=rorefactor, so a repeater'slast_seenhad 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 betweenlast_relayedandlast_seendrops 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.minHashBytesForMappingnow 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 inclient_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
- Important Links overlay. (#1771, 0fd2203)
- Topographic map layers. (#1891, 34b41fd)
- The Esri labels overlay now renders the labels it was already named for. (#1917, 2509023)
Nodes and export
- Export the visible node list as MeshCore companion contacts JSON. (#1889, aabbd50)
flood_advert_count_7don the node detail endpoint. (#1831, d2ef624)unscoped_relay_count_24hper 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.destHashandpayload.srcHashin filter autocomplete. (#1774, b3b8bec)- A
routed_throughfilter, 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_REQandDISCOVER_RESPsubtypes 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_seenwas 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_REQsource pubkey instead of treating it likeREQUEST. (#1864, 0d6f59a) - Restrict per-node clock skew to self-originated adverts. (#1816, #1818, 0352c9a)
- Count only live observers in the store's
/api/statsquery. (#1888, b3a306b) - Preserve the firmware-default Public channel (0x11) in analytics. (#1729, 6a32ec2)
detectSchemafails 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
CheckOriginallowlist, blocking cross-origin scrapers. (#1793, ec0ebed) - Drop the hardcoded
og:urlso 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,lonandzoomhash 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/channelsqueries. (#1887, 89544b1) - Use the cached
ParsedDecoded()instead of repeatedjson.Unmarshal. (#1871, f49e3fc) - Avoid a per-observation SQL fetch in the
handleObserverAnalyticshot loop. (#1827, 1720060) - Reuse the ctx buffer in
resolvePathForObsand cacheReadMemStatsper store. (#1873, ac6fbaf) - Replace
idx_tx_last_seenwith a partial index onlast_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.
-
Add a CARTO key. Set
map.tiles.providers.carto.keyin 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 iskey;api_keyis 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. -
pathTrust.minHashBytesForMappingdefaults 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 withprefixBytes == 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. -
New opt-in tables, all default OFF, each with its own retention window:
clientRxCoverage,clientRxObservationsandclientRfSamples, bounded byretention.clientRxDays,retention.clientRxObsDaysandretention.clientRfDays. Enabling a stream without setting its retention window means unbounded growth. -
observerPurgeDaysis new and disabled by default. Set it above bothobserverDaysandpacketDays: below those, the reference guards keep every row anyway. -
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.