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

latest release: v3.9.1
6 hours ago

Upgrade urgency: Medium — fixes the post-restart "relay timelines empty" regression, surfaces silent /api/nodes truncation, and ships operator-controlled per-name hiding.

257 commits since v3.8.3 (72 substantive + 185 auto-generated coverage bumps). Every bullet ends with a commit SHA — git show <sha> to verify.

Highlights

  • Your relay timelines survive a restart. Before v3.9.0, every container restart left repeater nodes with empty hop histories until live traffic replayed enough adverts to re-attribute. Now the relay-hop index is rebuilt from path_json during cold load — per-relay timelines, hop counts, and route stats are intact the moment the server says it's ready. (#1643, 938153d)
  • /api/nodes stops silently truncating at 500 rows. The hard cap was hiding nodes from the map, analytics and packets pages on any mesh of meaningful size — without any warning. Now properly paginated across every consumer, with internal UI requests bypassing the per-page clamp. (#1607, 2610574; #1637, 9002b25; #1589, 7421ead)
  • Hide your own node from a public dashboard with a prefix rename. New hiddenNamePrefixes config (default ["🚫"]) drops matching nodes from /api/nodes* while keeping DB rows for analytics — same convention other MeshCore dashboards already follow, no DB surgery, no permanent loss of history. (#1655, 825b264)
  • Observer Compare is finally discoverable. The compare page existed before but was a hidden URL trick; now there are three entry points (header CTA, sticky selector strip, observer-table multi-select) leading into a Tufte-grade compare view with state-preserving selection. (#1642, 531bc8a; #1645, c93ae67; #1647, 167af54)
  • Per-node Reach. New /api/nodes/{pubkey}/reach + UI surfaces directional link quality per neighbor — answers "is my link to X any good in both directions" without staring at a topology graph. (#1627, e2212f5)

What's New

Observer Compare

  • Promote observer comparison to first-class: header CTA, sticky selector strip, observer-table multi-select. (#1642, 531bc8a)
  • Tufte-grade compare page with themed button vocabulary + state-preserving multi-select across navigations. (#1645, c93ae67)
  • Polish: tightened checkboxes, hierarchy, selector strip + mobile fixes. (#1647, 167af54)
  • Wire TableSort on the observers table with numeric/time column types so the sort affordance actually sorts. (#1641, d72ab69)

Reach & Nodes

  • Per-node Reach page + GET /api/nodes/{pubkey}/reach (directional link quality). (#1627, e2212f5)
  • Paginate /api/nodes across map/live/analytics/packets/area-map so the 500-row server cap stops silently truncating UIs. (#1607, 2610574; #1637, 9002b25)
  • Sortable First Seen column on the Nodes table. (#1587, 7533b3b)
  • Firmware repeat:on|off hint now excludes listener-only observers from the disambiguator. (#1624, a477655)
  • Link RTC-reset warnings on node detail to the offending packet hashes. (#1590, 1a2b8c4)

Analytics

  • Relay Airtime Share endpoint + dumbbell chart. (#1601, 3898688)
  • 5-minute rolling-baseline anomaly detection for Write Sources. (#1593, a26a412)
  • TRACE packets overlay per-hop SNR on the path graph. (#1622, e9aed64)
  • Multi-byte prefix repeaters now show up in the 1-byte hash-usage matrix view. (#1591, 3df8924)

Live & Map

  • Fullscreen toggle on the live map + controls collapsed by default. (#1572, d7bd9d5)
  • Colorblind simulation overlay (Brettel/Vienot) with reset-to-Wong button. (#1600, 571c960)
  • Path symbols legend disclosure on packets. (#1570, 5fd8900)
  • OSM / Stamen tile providers with per-provider Leaflet layer control. (#1533, d7cd920)
  • Operator-configurable liveMap.maxNodes (default 2000). (#1577, 1bdb92d)

Config & Operator Surfaces

  • hiddenNamePrefixes (default ["🚫"]) — drop matching nodes from /api/nodes* while preserving DB rows. (#1655, 825b264)
  • Config-driven disabled-tabs list in the customizer modal. (#1579, 7292d60)
  • branding.homeUrl override for embedded deployments. (#1576, 9b36b7c)
  • Configurable observer-health thresholds. (#1556, 65bd954)
  • Detect CDN-fronted deployment + document bypass requirement. (#1564, 63bfa3d)
  • Expose --nav-active-bg as a themeable token. (#1571, 892eb2c)

Performance

  • Chunked Load with early HTTP readiness — server accepts requests while heavy load completes in the background. (#1596, bc1822e)
  • Background subpath + pathHop index builds with ready gates. (#1604, df61660)
  • Lazy distance-index build on first request. (#1597, 5629a48)
  • neighbor_api: fold first_seen into cached map — fixes the #1627 r3 regression. (#1632, 078225a)
  • GOMEMLIMIT via runtime.maxMemoryMB in server + ingestor. (#1595, 1b112f0)
  • SQLite writer-lock wait/hold instrumentation per component. (#1594, 222bfdf)

Bug Fixes

Ingestor

  • Subscribe to MQTT before startup maintenance; buffer until the writer is free. (#1609, 18810b5)
  • Decode firmware 1.16.0 extended ACK (5/6-byte payloads). (#1618, 9612f08)
  • Write resolved_path on new observations (regression from #1289). (#1548, 3feb97f)
  • Defense-in-depth empty-scope guard in UpdateNodeDefaultScope. (#1575, cd19285)
  • Skip default_scope update when ScopeName is empty. (#1569, 05af6c6)
  • Address #1609 follow-up findings — config doc, receipt-time liveness, buffer stop/clamp warn. (#1623, 3d12266)

Reach

  • Bust response cache on blacklist change. (#1636, 8295c21)
  • scanReachRows DB errors must surface as 500, not 404. (#1635, 43be1bb)
  • Narrow-viewport CSS — no horizontal scroll, map no longer shrunken. (#1634, 59d6646)

Frontend / UI

  • Render analytics Channels group-header sprites as HTML, not escaped text. (#1658, fb6bb08)
  • Bump feed-detail-card z-index + make popup draggable. (#1620, f66ff40)
  • Theme-track .vcr-scope-btn.active + .copy-link-btn:hover backgrounds. (#1578, 16c7ea4)
  • Replay handoff no longer freezes the map (suppressLive flag). (#1603, 1f65d78)
  • Detach slide-over panel on close — architectural focus-restore fix + --repeat-each=20 CI gate. (#1617, 37a7a92)
  • getTileUrl() now invokes function-typed provider URLs + regression tests. (#1615, dc433e4)
  • Reliably restore row focus on panel close. (#1602, 1be0aec)
  • Gesture hints — edge-drawer mobile-only + row-swipe widening (re-fix). (#1586, 116efe4)
  • Honor time-window filter on Route Patterns analytics. (#1592, d6384c3)
  • Live corner-cycle button clears drag state. (#1568, 2b45f78)
  • Observers aggregate header shows "Last updated" timestamp. (#1563, a7ad2be)
  • Mirror Load's resolved_path indexing into loadChunk. (#1582, 9465949)
  • Remove dead server-side backfill flag (stuck backfilling=true). (#1583, f7571a2)
  • Additional follow-up fixes for #1532. (#1580, 373ee81)
  • Document writeStatsAtomic symlink-replace semantics + regression test. (#1588, af66943)

API

  • Bypass API limit clamps for internal UI requests (revisit of #1540). (#1589, 7421ead)
  • Emit Cache-Control: no-store on /api/* responses. (#1553, 0c908d2)

Performance

  • Chunked Load early-readiness, background index builds, lazy distance index, cached first_seen, GOMEMLIMIT honored, writer-lock instrumentation — see "Performance" under What's New.

Security

  • Detect CDN-fronted deployment and document the bypass requirement so rate limits and PoW gates can't be silently routed around. (#1564, 63bfa3d)

Breaking changes

None.

Operator-facing changes / config

New config (config.example.json, all opt-in, safe defaults):

  • hiddenNamePrefixes: ["🚫"] — node-name prefixes that hide a node from /api/nodes, /api/nodes/search, /api/nodes/{pubkey}. DB rows preserved; analytics history intact. Mirrors the convention used by other MeshCore map dashboards. Opt out with []. (#1655, 825b264)
  • liveMap.maxNodes: 2000 — cap nodes rendered on the live map; raise for big meshes, lower for low-power dashboards. (#1577, 1bdb92d)
  • runtime.maxMemoryMB — sets GOMEMLIMIT for server + ingestor; tune against container limits. (#1595, 1b112f0)
  • Observer-health thresholds — previously hard-coded, now configurable. (#1556, 65bd954)
  • branding.homeUrl — override the "Home" link target for embedded deployments. (#1576, 9b36b7c)
  • Customizer disabled-tabs — config-driven hide list. (#1579, 7292d60)

Behavior changes:

  • /api/nodes paginates instead of silently truncating at the 500-row cap; internal UI requests bypass the clamp. (#1607, 2610574; #1589, 7421ead)
  • /api/* responses now emit Cache-Control: no-store. (#1553, 0c908d2)
  • Per-node Reach available at GET /api/nodes/{pubkey}/reach. (#1627, e2212f5)
  • Hashtag channels from meshcore-channels catalogue appear in the channels list without manual config. (#1656, e04c711)

Behind the scenes

  • Emoji → Phosphor migration (six PRs). M1 top-nav/mobile-nav/Compare (#1649, 55e4d95) · M2 page headers + table chrome (#1650, 3062745) · M3 detail panes + badges (#1651, b812a98) · M4 map + route overlays (#1652, 2b6809c) · M5 settings + customize (#1653, 1116801) · M6 final sweep + lint gate + carry-forwards (#1654, 89eade6). Tracking: #1648.
  • CI: bump go test timeout 10m → 15m (suite grew past 10m post-#1655). (#1661, 0712c5f)
  • Test: tighten slideover row selector to avoid the virtual-scroll spacer race. (#1663, 037dc8c)
  • Test: subpaths_window tests wait for index readiness after the #1595 chunked load. (#1621, ad41b9b)
  • Test: mock /api/nodes/search in home-coverage E2E (closes #1313). (#1584, 6a027b0)
  • Refactor: extract pure helpers into route-view-utils.js. (#1581, 545013d)

Verification

Test plan: workspace-meshcore/test-plans/v3.9.0-cdp-test-plan.md (93 tests across 16 sections)

Initial run (master pre-#1665, 2026-06-12 00:45 UTC): 56 pass / 22 partial / 5 fail / 14 skipped. Two BLOCKER lint-gate breaches surfaced — .obs-clock-naive-chip (#v384-1.2, 14× ⚠️) and analytics Channels encrypted labels (#v384-12.18, 158× 🔒) — plus one API contract regression (/api/nodes/<bad>/reach returns 404, expected 500/200). 22 partials were plan selector drift (provider names, panel selectors) not code regressions.

Final run (post-#1665, 2026-06-12 01:35 UTC): v384-1.2 ✅ (11 chips, 11 sprites, 0 emoji). v384-12.18 ✅ (315 lock sprites, 0 🔒 emoji).

Known partials carried forward (recoverable, not blockers): plan selector drift in §§5, 8, 9, 12 — plan to be tightened in v3.8.5 cycle.

Open follow-up issues: #1659 (analytics warm-up data), #1660 (UI loading banner). Both are UX improvements, not regressions.

External API regression to investigate post-release: /api/nodes/<unknown-pubkey>/reach returns 404 instead of 500/200-empty per #1631 contract. Doc/code mismatch, low severity. (To file as issue.)

Acknowledgements

External contributors made this release:

  • @efiten — relay-attribution rebuild on cold-load (#1643), paginate /api/nodes (#1637), per-node Reach page (#1627), MQTT subscribe-before-maintenance (#1609), remove dead backfill flag (#1583), plus #1625/#1626 (per-node Reach relanding).
  • @EldoonNemar — OSM / Stamen tile provider support (#1533), Cache-Control: no-store follow-up (#1580), internal-bypass for API limit clamps (#1589), reliable row-focus restoration on panel close (#1602).

Tagging

git tag -a v3.9.0 e74e8607 -m "v3.9.0"

Don't miss a new CoreScope release

NewReleases is sending notifications on new releases.