Files
meshcore-analyzer/docs/release-notes/v3.9.0.md
T

161 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CoreScope v3.9.0
**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, 938153dd)
- **`/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, 26105748; #1637, 9002b25b; #1589, 7421ead9)
- **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, 825b2648)
- **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, 531bc8ac; #1645, c93ae67e; #1647, 167af54e)
- **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, e2212f50)
## What's New
### Observer Compare
- Promote observer comparison to first-class: header CTA, sticky selector strip, observer-table multi-select. (#1642, 531bc8ac)
- Tufte-grade compare page with themed button vocabulary + state-preserving multi-select across navigations. (#1645, c93ae67e)
- Polish: tightened checkboxes, hierarchy, selector strip + mobile fixes. (#1647, 167af54e)
- Wire `TableSort` on the observers table with numeric/time column types so the sort affordance actually sorts. (#1641, d72ab69f)
### Reach & Nodes
- Per-node Reach page + `GET /api/nodes/{pubkey}/reach` (directional link quality). (#1627, e2212f50)
- Paginate `/api/nodes` across map/live/analytics/packets/area-map so the 500-row server cap stops silently truncating UIs. (#1607, 26105748; #1637, 9002b25b)
- Sortable First Seen column on the Nodes table. (#1587, 7533b3b6)
- Firmware `repeat:on|off` hint now excludes listener-only observers from the disambiguator. (#1624, a4776557)
- Link RTC-reset warnings on node detail to the offending packet hashes. (#1590, 1a2b8c48)
### Analytics
- Relay Airtime Share endpoint + dumbbell chart. (#1601, 3898688d)
- 5-minute rolling-baseline anomaly detection for Write Sources. (#1593, a26a412c)
- TRACE packets overlay per-hop SNR on the path graph. (#1622, e9aed641)
- Multi-byte prefix repeaters now show up in the 1-byte hash-usage matrix view. (#1591, 3df89241)
### Live & Map
- Fullscreen toggle on the live map + controls collapsed by default. (#1572, d7bd9d57)
- Colorblind simulation overlay (Brettel/Vienot) with reset-to-Wong button. (#1600, 571c960c)
- Path symbols legend disclosure on packets. (#1570, 5fd8900c)
- OSM / Stamen tile providers with per-provider Leaflet layer control. (#1533, d7cd9203)
- Operator-configurable `liveMap.maxNodes` (default 2000). (#1577, 1bdb92de)
### Config & Operator Surfaces
- `hiddenNamePrefixes` (default `["🚫"]`) — drop matching nodes from `/api/nodes*` while preserving DB rows. (#1655, 825b2648)
- Config-driven disabled-tabs list in the customizer modal. (#1579, 7292d60f)
- `branding.homeUrl` override for embedded deployments. (#1576, 9b36b7c4)
- Configurable observer-health thresholds. (#1556, 65bd954b)
- Detect CDN-fronted deployment + document bypass requirement. (#1564, 63bfa3d9)
- Expose `--nav-active-bg` as a themeable token. (#1571, 892eb2c0)
### Performance
- Chunked `Load` with early HTTP readiness — server accepts requests while heavy load completes in the background. (#1596, bc1822e4)
- Background subpath + pathHop index builds with ready gates. (#1604, df61660a)
- Lazy distance-index build on first request. (#1597, 5629a489)
- `neighbor_api`: fold `first_seen` into cached map — fixes the #1627 r3 regression. (#1632, 078225a5)
- `GOMEMLIMIT` via `runtime.maxMemoryMB` in server + ingestor. (#1595, 1b112f0b)
- SQLite writer-lock wait/hold instrumentation per component. (#1594, 222bfdf6)
## Bug Fixes
### Ingestor
- Subscribe to MQTT before startup maintenance; buffer until the writer is free. (#1609, 18810b5c)
- Decode firmware 1.16.0 extended ACK (5/6-byte payloads). (#1618, 9612f08e)
- Write `resolved_path` on new observations (regression from #1289). (#1548, 3feb97f1)
- Defense-in-depth empty-scope guard in `UpdateNodeDefaultScope`. (#1575, cd19285f)
- Skip `default_scope` update when `ScopeName` is empty. (#1569, 05af6c6e)
- Address #1609 follow-up findings — config doc, receipt-time liveness, buffer stop/clamp warn. (#1623, 3d122665)
### Reach
- Bust response cache on blacklist change. (#1636, 8295c211)
- `scanReachRows` DB errors must surface as 500, not 404. (#1635, 43be1bb7)
- Narrow-viewport CSS — no horizontal scroll, map no longer shrunken. (#1634, 59d66469)
### Frontend / UI
- Render analytics Channels group-header sprites as HTML, not escaped text. (#1658, fb6bb085)
- Bump feed-detail-card z-index + make popup draggable. (#1620, f66ff40a)
- Theme-track `.vcr-scope-btn.active` + `.copy-link-btn:hover` backgrounds. (#1578, 16c7ea4b)
- Replay handoff no longer freezes the map (suppressLive flag). (#1603, 1f65d781)
- Detach slide-over panel on close — architectural focus-restore fix + `--repeat-each=20` CI gate. (#1617, 37a7a927)
- `getTileUrl()` now invokes function-typed provider URLs + regression tests. (#1615, dc433e41)
- Reliably restore row focus on panel close. (#1602, 1be0aec8)
- Gesture hints — edge-drawer mobile-only + row-swipe widening (re-fix). (#1586, 116efe4b)
- Honor time-window filter on Route Patterns analytics. (#1592, d6384c3c)
- Live corner-cycle button clears drag state. (#1568, 2b45f787)
- Observers aggregate header shows "Last updated" timestamp. (#1563, a7ad2be1)
- Mirror `Load`'s `resolved_path` indexing into `loadChunk`. (#1582, 9465949e)
- Remove dead server-side backfill flag (stuck `backfilling=true`). (#1583, f7571a26)
- Additional follow-up fixes for #1532. (#1580, 373ee816)
- Document `writeStatsAtomic` symlink-replace semantics + regression test. (#1588, af669438)
### API
- Bypass API limit clamps for internal UI requests (revisit of #1540). (#1589, 7421ead9)
- Emit `Cache-Control: no-store` on `/api/*` responses. (#1553, 0c908d2b)
## 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, 63bfa3d9)
## 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, 825b2648)
- `liveMap.maxNodes: 2000` — cap nodes rendered on the live map; raise for big meshes, lower for low-power dashboards. (#1577, 1bdb92de)
- `runtime.maxMemoryMB` — sets `GOMEMLIMIT` for server + ingestor; tune against container limits. (#1595, 1b112f0b)
- Observer-health thresholds — previously hard-coded, now configurable. (#1556, 65bd954b)
- `branding.homeUrl` — override the "Home" link target for embedded deployments. (#1576, 9b36b7c4)
- Customizer disabled-tabs — config-driven hide list. (#1579, 7292d60f)
**Behavior changes:**
- `/api/nodes` paginates instead of silently truncating at the 500-row cap; internal UI requests bypass the clamp. (#1607, 26105748; #1589, 7421ead9)
- `/api/*` responses now emit `Cache-Control: no-store`. (#1553, 0c908d2b)
- Per-node Reach available at `GET /api/nodes/{pubkey}/reach`. (#1627, e2212f50)
- Hashtag channels from `meshcore-channels` catalogue appear in the channels list without manual config. (#1656, e04c7113)
## Behind the scenes
- **Emoji → Phosphor migration (six PRs).** M1 top-nav/mobile-nav/Compare (#1649, 55e4d957) · M2 page headers + table chrome (#1650, 30627454) · M3 detail panes + badges (#1651, b812a98a) · M4 map + route overlays (#1652, 2b6809cd) · M5 settings + customize (#1653, 1116801b) · M6 final sweep + lint gate + carry-forwards (#1654, 89eade6e). Tracking: #1648.
- CI: bump go test timeout 10m → 15m (suite grew past 10m post-#1655). (#1661, 0712c5ff)
- Test: tighten slideover row selector to avoid the virtual-scroll spacer race. (#1663, 037dc8c4)
- Test: subpaths_window tests wait for index readiness after the #1595 chunked load. (#1621, ad41b9bb)
- Test: mock `/api/nodes/search` in home-coverage E2E (closes #1313). (#1584, 6a027b03)
- Refactor: extract pure helpers into `route-view-utils.js`. (#1581, 545013d3)
## 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"
```