Releases: Kpa-clawbot/CoreScope
v3.7.2
v3.7.1
Identical to v3.7.0 + live PSK decrypt fix (#1031). This is the tagged release with the Docker image on GHCR.
docker pull ghcr.io/kpa-clawbot/corescope:v3.7.1
See v3.7.0 release notes for full changelog.
v3.7.0 — Operator's Toolkit
CoreScope v3.7.0 — "Operator's Toolkit"
50 commits · 30 issues closed · 95 files changed · +7,710 / -293 LOC
Channel Decryption
Rebuilt from scratch. Pure-JS AES-128-ECB + SHA-256/HMAC — works on HTTP and HTTPS. PSK channels: add by key, name them, delete them, see decrypt count. Live WebSocket auto-decrypt with unread badges. Channel hash-byte dedup fixed.
Analytics
Selectable timeframes (1h/24h/7d/30d). Region attribution by repeater home, not observer. "All" filter fixed. Topology dedup (no more triple-counted repeaters). Timestamp format applied to chart axes.
New Surfaces
/#/roles— role distribution + per-role clock-skew/api/backup— API-key gated SQLite export via VACUUM INTO/api/healthz— readiness probe
Packets & Filters
Transport route type filter (transport == true, T_FLOOD, T_DIRECT). Clear filters button. Scroll position preserved. Null hash/timestamp packets cleaned. GRP_DATA type 6 support.
Map & Nodes
Short pubkey-prefix URLs (📡 Copy short URL). Multi-byte hash support indicator on markers. Channel color picker fixed.
MQTT & Ingestor
Async path_json backfill (no more startup blocks). Per-source region field. MQTT startup resilience (unreachable sources don't block). Configurable connect timeout. Observer IATA whitelist + blacklist. Per-hop TRACE SNR extraction in ingestor.
Observer & Node
Flood/direct filter on comparison page. TRACE per-hop SNR in byte breakdown. Clock skew evidence UI. Separate last-status vs last-packet timestamps. Soft-deleted observers excluded. Export/import includes favorites.
Infra
CORS allowlist. RW SQLite connection cache. GeoFilter Builder draft save/load/download. False-positive path exclusion.
Upgrade: New config fields in config.example.json: observerIATAWhitelist, observerBlacklist, per-source region/connectTimeoutSec, CORS allowedOrigins. One-time DB migration runs async on startup.
⚠️ Docker image not available for this tag (CI trigger issue). Use v3.7.1 for the container image:ghcr.io/kpa-clawbot/corescope:v3.7.1
v3.6.0 - The Forensics
v3.6.0 - The Forensics
CoreScope just got eyes everywhere. This release drops path inspection, color-by-hash markers, clock skew detection, full channel encryption, an observer graph, and a pile of robustness fixes that make your mesh network feel like it's being watched by someone who actually cares.
134 commits, 105 PRs merged, 18K+ lines added. Here's what shipped.
🚀 New Features
Path-Prefix Candidate Inspector (#944, #945)
The marquee feature. Click any path segment and CoreScope opens an interactive inspector showing every candidate node that could match that hop prefix - plotted on a map with scoring by neighbor-graph affinity and geographic centroid. Ambiguous hops? Now you can see why they're ambiguous and pick the right one.
Why you'll love it: No more guessing which 0xA3 is the real repeater. The inspector lays out every candidate, scores them, and lets you drill in visually.
Color-by-Hash Packet Markers (#948, #951)
Every packet type gets a vivid, hash-derived color - on the live feed, map polylines, and flying-packet animations. Bright fill with dark outline for contrast. No more monochrome blobs - you can visually track packet flows by color at a glance.
Node Filter on Live Page (#924, #771)
Filter the live packet stream to show only traffic flowing through a specific node. Pick a repeater, see exactly what it's carrying. That simple.
Clock Skew Detection (#746, #752, #828, #850)
Full pipeline: backend computes drift using Theil-Sen regression with outlier rejection (#828), the UI shows per-node badges, detail sparklines, and fleet-wide analytics (#752). Bimodal clock severity (#850) surfaces flaky-RTC nodes that toggle between accurate and drifted - instead of hiding them as "No Clock."
Why you'll love it: Nodes with bad clocks silently corrupt your timeline. Now they glow red before they ruin your analysis.
Observer Graph (M1+M2) (#774)
Observers are now first-class graph citizens. CoreScope builds a neighbor graph from observation overlaps, scores hop-resolver candidates by graph edges (#876), and uses geographic centroid for tiebreaking. The observer topology is visible and queryable.
Channel Encryption - Full Stack (#726, #733, #750, #760)
Three milestones landed as one: DB-backed channel message history (#726), client-side PSK decryption in the browser (#733), and PSK channel management with add/remove UX and message caching (#750). Add a channel key in the UI, and CoreScope decrypts messages client-side - no server-side key storage. The add-channel button (#760) makes it dead simple.
Why you'll love it: Encrypted channels are no longer black boxes. Add your PSK, see the messages, search history - all without exposing keys to the server.
Hash Collision Inspector (#758)
The Hash Usage Matrix now shows collision details for all hash sizes. When two nodes share a prefix, you see exactly who collides and at what size.
Geofilter Builder - In-App (#735, #900)
The geofilter polygon builder is now served directly from CoreScope with a full docs page (#900). No more hunting for external tools. Link from the customizer, draw your polygon, done.
Node Blacklist (#742)
nodeBlacklist in config hides abusive or troll nodes from all views. They're gone.
Observer Retention (#764)
Stale observers are automatically pruned after a configurable number of days. Your observer list stays clean without manual intervention.
Advert Signature Validation (#794)
Corrupt packets with invalid advert signatures are now rejected at ingest. Bad data never hits your store.
Bounded Cold Load (#790)
Load() now respects a memory budget - no more OOM on cold start with a fat database. Combined with retention-hours cutoff (#917), cold start is safe on constrained hardware.
Multi-Arch Docker Images (#869)
Official images now publish amd64 + arm64 in a single multi-arch manifest. Raspberry Pi operators: pull and run. No special tags needed.
/nodes Detail Panel + Search (#868)
The nodes detail panel ships with search improvements (#862) - find nodes fast, see their full detail in a slide-out panel.
Deduplicated Top Longest Hops (#848)
Longest hops are now deduplicated by pair with observation count and SNR cues. No more seeing the same link 47 times.
🔥 Performance Wins
StoreTx ResolvedPath Elimination (#806)
The per-transaction ResolvedPath computation is gone - replaced by a membership index with on-demand decode. This was one of the hottest paths in the ingestor.
Node Packet Queries (#803)
Raw JSON text search for node packets replaced with a proper byNode index (#673). Night and day.
Channel Query Performance (#762, #763)
New channel_hash column enables SQL-level channel filtering. No more full-table scan to find messages in a channel.
SQLite Auto-Vacuum (#919, #920)
Incremental auto-vacuum enabled - the database file actually shrinks after retention pruning. No more 2GB database holding 200MB of live data.
Retention-Hours Cutoff on Load (#917)
Load() now applies retentionHours at read time, preventing OOM when the DB has more history than memory allows.
🛡️ Security & Robustness
MQTT Reconnect with Bounded Backoff (#947, #949)
The ingestor now reconnects to MQTT brokers with exponential backoff, observability logging, and bounded retry. No more silent disconnects that kill your data stream.
🐛 Bugs Squashed
This release exterminates 40+ bugs — from protocol-level hash mismatches to pixel-level CSS breakage. Operators told us what hurt; we listened.
- Path inspector "Show on Map" missed origin and first hop (#950) - map view now includes all hops
- Content hash used full header byte (#787) - content hashing now uses payload type bits only, fixing hash collisions between packets that differ only in header flags
- Encrypted channel deep links showed broken UI (#825, #826, #815) - deep links to encrypted channels now show a lock message instead of broken UI when you don't have the key
- Geofilter longitude wrapping (#925) - geofilter builder wraps longitude to [-180, 180]; southern hemisphere polygons no longer invert
- Hash filter bypasses saved region filter (#939) - hash lookups now skip the geo filter as intended
- Companion-as-repeater excluded from path hops (#935, #936) - non-repeater nodes no longer pollute hop resolution
- Customize panel re-renders while typing (#927) - text fields keep focus during config changes
- Per-observation raw_hex (#881, #882) - each observer's hex dump now shows what that observer actually received
- Per-observation children in packet groups (#866, #880) - expanded groups show per-obs data, not cross-observer aggregates
- Full-page obs-switch (#866, #870) - switching observers updates hex, path, and direction correctly
- Packet detail shows wrong observation (#849, #851) - clicking a specific observation opens that observation
- Byte breakdown hop count (#844, #846) - derived from
path_len, not aggregated_parsedPath - Transport-route path_len offset (#852, #853) - correct offset calculation + CSS variable fix
- Packets/hour chart bars + x-axis (#858, #865) - bars render correctly, x-axis labels properly decimated
- Channel timeline capped to top 8 (#860, #864) - no more 47-channel chart spaghetti
- Reachability row opacity removed (#859, #863) - clean rows without misleading gradient
- Sticky table headers on mobile (#861, #867) - restored after regression
- Map popup 'Show Neighbors' on iOS Safari (#840, #841) - link actually works now
- Node detail Recent Packets invisible text (#829, #830) - CSS fix
- /api/packets/{hash} falls back to DB (#827, #831) - when in-memory store misses, DB catches it
- IATA filter bypass for status messages (#694, #802) - status packets no longer filtered out by airport codes
- Desktop node click URL hash (#676, #739) - clicking a node updates the URL for deep linking
- Filter params in URL hash (#682, #740) - all filter state serialized for shareable links
- Hide undecryptable channel messages (#727, #728) - clean default view
- TRACE path_json uses path_sz (#732) - correct field from flags byte, not header hash_size
- Multi-byte adopters (#754, #767) - all node types, role column, advert precedence
- Channel key case sensitivity (#761) - Public decode works correctly
- Transport route field offsets (#766) - correct offsets in field table
- Clock skew sanity checks (#769) - filter epoch-0, cap drift, require minimum samples
- Neighbor graph slider persistence (#776) - default 0.7, persisted to localStorage
- Node detail panel navigation (#779, #785) - Details/Analytics links actually navigate
- Channel key removal (#898) - user-added keys for server-known channels can be removed
- Side-panel Details on desktop (#892) - opens full-screen correctly
- Hex-dump byte ranges client-side (#891) - computed from per-obs raw_hex
- path_json derived from raw_hex at ingest (#886, #887) - single source of truth
- Path pill and byte breakdown hop agreement (#885) - they match now
- Mobile close button + toolbar scroll (#797, #805) - accessible and scrollable
- /health.recentPackets resolved_path fallback (#810, #821) - falls back to longest sibling observation
- Channel filter on Packets page (#812, #816) - UI and API both fixed
- Clock-skew section in side panel (#813, #814) - renders correctly
- Real RSS in /api/stats (#832, #835) - surface actual RSS alongside tracked store bytes
- Hash size detection for transport routes + zero-hop adverts (#747) - correct detection
- Repeater+observer merged map marker (#745) - single marker, not two overlapping
🎨 UI Polish
- QA findings applied across the board (#832, #833, #836, #837, #838) - dozens of small UX fixes from systematic QA pass
...
CoreScope v3.5.2
CoreScope v3.5.2
🔧 New: corescope-decrypt CLI
Standalone tool for retroactive channel message decryption. Decrypt hashtag channel messages from your SQLite database — even packets ingested before the channel key was configured.
corescope-decrypt --channel "#test" --db meshcore.db --format irc
corescope-decrypt --channel "#wardriving" --db meshcore.db --format json --output messages.json
corescope-decrypt --channel "#sf" --db meshcore.db --format html --output viewer.htmlThree output formats: JSON (full metadata), HTML (interactive viewer), IRC/log (greppable plain text). Included in the Docker image at /app/corescope-decrypt. Only works with hashtag channels (#name) — the public channel uses a pre-shared key, not hashtag derivation.
⚡ Eviction redesign
Replaced HeapAlloc-based eviction with self-accounting trackedBytes counter. No more cascading eviction on large databases. High/low watermark hysteresis + 25% safety cap.
📊 Cache invalidation tuning
Wired the invalidationDebounce config (was dead code). Default cooldown 10s → 300s. Collision cache only clears on new nodes. Expected cache hit rate: 7% → 50-80%.
🔬 Multi-byte capability
Repeater multi-byte detection integrated into Hash Adopters table. Filter buttons fixed. TRACE packets excluded from suspected detection (pre-1.14 repeaters can forward multi-byte TRACEs).
🐛 Fixes
- Relay-only nodes now appear as alive (path hop indexing + debounced DB touch)
- Live feed timestamps refresh every 10s (no more stale "10s ago")
- Perf page shows tracked memory, not heap
- SQLite busy_timeout on all write connections
- Dockerfile includes
internal/sigvalidate
Downloads
corescope-decrypt-linux-amd64— x86_64 static binarycorescope-decrypt-linux-arm64— ARM64 static binary (Raspberry Pi, etc.)
Upgrade
docker pull ghcr.io/kpa-clawbot/corescope:v3.5.2CoreScope v3.5.0 🚀
CoreScope v3.5.0 🚀
The "stop building from source and start analyzing your mesh" release. 95 commits.
🐳 Pre-built Docker Images
CoreScope now ships as a ready-to-run Docker image on GitHub Container Registry. No cloning, no building, no dependencies — just pull and run.
docker run -d --name corescope -p 80:80 -p 443:443 -p 1883:1883 \
-v corescope-data:/app/data \
ghcr.io/kpa-clawbot/corescope:v3.5.0Using HTTPS with a custom domain? Mount your Caddyfile and certs directory:
docker run -d --name corescope -p 80:80 -p 443:443 -p 1883:1883 \
-v /your/data:/app/data \
-v /your/Caddyfile:/etc/caddy/Caddyfile:ro \
-v /your/caddy-data:/data/caddy \
ghcr.io/kpa-clawbot/corescope:v3.5.0Caddy auto-provisions Let's Encrypt certs. Your Caddyfile just needs:
yourdomain.example.com {
reverse_proxy localhost:3000
}
That's it. Zero config required — MQTT broker, Caddy HTTPS, and SQLite are built in.
Already running CoreScope?
# 1. Find your running container name
docker ps --format '{{.Names}}'
# 2. Stop and remove it
docker stop <container-name> && docker rm <container-name>
# 3. Pull the pre-built image
docker pull ghcr.io/kpa-clawbot/corescope:v3.5.0
# 4. Run with your existing data directory
docker run -d --name corescope -p 80:80 -p 443:443 -p 1883:1883 \
-v /your/data:/app/data \
-v /your/Caddyfile:/etc/caddy/Caddyfile:ro \
-v /your/caddy-data:/data/caddy \
ghcr.io/kpa-clawbot/corescope:v3.5.0Your data volume stays. Nothing to migrate.
Tags: v3.5.0 (this release) · latest (latest tagged release) · edge (master tip, for testing). Env: DISABLE_CADDY=true / DISABLE_MOSQUITTO=true if you bring your own.
⚡ 83% Faster
35 performance commits. Packets endpoint p50 dropped from 16.7ms → 2.7ms. Server now serves HTTP within 2 minutes on any DB size — async background backfill means you're never staring at a loading screen. N+1 API calls killed everywhere. Prefix map memory cut 10x. WebSocket renders batched via rAF.
🔬 RF Health Dashboard
New Analytics tab. Per-observer noise floor as color-coded columns (green/yellow/red), airtime utilization, error rates, battery levels. Click any observer for the full breakdown. Region-filterable. This is the beginning of making CoreScope more than just a packet viewer.
🗺️ See Where Traces Actually Go
Send a trace → watch it on the live map. Solid animated line shows how far it got. Dashed ghost shows where it didn't reach. Finally know where your trace failed, not just that it failed.
📊 Things That Were Lying To You
- "By Repeaters" was counting companions. Fixed.
- Zero-hop adverts claimed "1 byte hash" when the hash size was unknowable. Fixed.
- "Packets through this node" showed packets through a different node with the same prefix. Fixed — now uses the neighbor affinity graph.
- Table sorting on nodes/neighbors/observers silently did nothing. Fixed.
🔗 Deep Links · 🎨 Channel Colors · 📱 Mobile · 🔑 Security
Deep links — every page state goes in the URL. Share a link to a specific node, filter, or analytics tab.
Channel colors — click the color dot next to any channel, pick from 8 colors, see it highlighted across the feed. Persists in localStorage.
Distance units — km, miles, or auto-detect from locale. Customizer → Display.
Mobile — 44px touch targets, ARIA labels, responsive breakpoints.
Security — weak API keys rejected at startup. License: GPL v3.
📡 Full API Documentation
Every endpoint is now documented with an auto-generated OpenAPI 3.0 spec — always in sync with the running server.
- Interactive Swagger UI: analyzer.00id.net/api/docs — browse and test all 40+ endpoints
- Machine-readable spec: analyzer.00id.net/api/spec — import into Postman, Insomnia, or use for bot/integration development
On your own instance: /api/docs and /api/spec.
🐛 14 Bugs Squashed
Live map crash, zero-hop hash lies, animation freezes, repeater miscounts, prefix collisions, dead channel picker, invisible buttons, broken sorting, memory leak, and more.
Upgrade
docker stop <container-name> && docker rm <container-name>
docker pull ghcr.io/kpa-clawbot/corescope:v3.5.0
# HTTP only:
docker run -d --name corescope -p 80:80 -p 1883:1883 \
-v /your/data:/app/data \
ghcr.io/kpa-clawbot/corescope:v3.5.0
# With HTTPS (custom domain):
docker run -d --name corescope -p 80:80 -p 443:443 -p 1883:1883 \
-v /your/data:/app/data \
-v /your/Caddyfile:/etc/caddy/Caddyfile:ro \
-v /your/caddy-data:/data/caddy \
ghcr.io/kpa-clawbot/corescope:v3.5.0First start backfills resolved_path in the background. No downtime. No breaking changes.
v3.4.1 — Server-Side Hop Resolution & Performance
v3.4.1 — Server-Side Hop Resolution & Performance
⭐ Headline: Resolved Paths
Hop prefixes are now resolved to full node identities at ingest time using a persisted neighbor affinity graph. No more guessing — the server knows which "D6" is which.
- Persisted neighbor graph (
neighbor_edgestable) — builds automatically on first run, loads instantly on restart. 4-tier resolution: affinity → geo → GPS → first match. resolved_pathon every observation — full 64-char pubkeys stored alongside rawpath_json. Ambiguous prefixes that the old client-side resolver got wrong are now correct.- Frontend prefers server-resolved paths — packets page, live map, Show Route, packet detail all use
resolved_pathwith automatic fallback for old data. - Zero-cost on subsequent startups — edges and resolved paths persist in SQLite. First run does a one-time backfill.
🚀 Performance
- Distance index rebuild debounced — was triggering on every ingest cycle, now at most every 30s. Eliminates CPU hot loop on busy meshes.
- Neighbor graph build optimized — cached
strings.ToLower, cached JSON parsing viasync.Once, TTL bumped 60s → 5min. Cuts cold start time. - O(n²) observation dedup → O(n) — map-based replacement.
- O(n²) selection sort → sort.Slice — standard library sort.
- Parallelized expanded group fetches — hashIndex Map lookup instead of linear scan.
- Advert pubkey tracking incremental — eliminates per-request JSON parsing.
- Rate-limited cache invalidation — prevents 0% hit rate under sustained ingest.
- VCR replay chunked — prevents UI freezes on large replays.
🐛 Fixes
hasResolvedPathflag race —detectSchema()ran before column was added, causing full re-backfill on every restart. Fixed.resolved_pathmissing from grouped packets —groupByHash=trueresponse wasn't including resolved paths. Fixed.- Memory leak in
pruneStaleNodes—nodeActivitymap entries never cleaned up. Fixed. - iOS tap-to-scroll broken — scroll container restructured for status bar tap.
- Observer filter dropped groups — grouped packets view lost groups when observer filter was active. Fixed.
- Null crash on ADVERT detail —
pathHopsnull guard added. - Mobile filter dropdown — CSS specificity prevented expansion. Fixed.
- Hash collision analysis — now only counts repeaters, not all nodes.
- Virtual scroll height — accounts for expanded group rows.
🔧 Infra
- CI build and deploy jobs pinned to
meshcore-vmrunner. - Deep linking rule added to AGENTS.md.
⚠️ Known Issues / Heads Up
- First restart after upgrade takes ~4 minutes on large DBs — The server builds the neighbor graph (~40s), then backfills
resolved_pathfor all existing observations (~2 min), plus normal data load (~35s). With 1M+ observations, expect heavy CPU and SQLite locking during this window. MQTT ingest may disconnect and reconnect. This is a one-time cost — subsequent restarts load from SQLite in ~15s. Do not restart the server during this process or it will start over.
📊 By the Numbers
- 22 commits, 13 files changed
- 1.1M observations backfilled with resolved paths
- 408K neighbor edges persisted
- Cold start: ~4 min first run → ~15s subsequent
CoreScope v3.4.0 — Neighbor Affinity
CoreScope v3.4 Release Notes
The neighbor affinity release. CoreScope now understands how nodes relate to each other — not just that they exist, but how strongly they're connected. This powers smarter hop resolution, richer node detail pages, and a new graph visualization in analytics.
🎯 Features
Neighbor Affinity System (7 milestones)
A complete neighbor relationship engine, from backend graph building to frontend visualization:
- Affinity graph builder — computes neighbor relationships and connection strength from packet traffic (#507)
- Affinity API endpoints — REST endpoints to query neighbor data (#508)
- Show Neighbors via affinity API — the existing Show Neighbors feature now uses real affinity data instead of raw packet heuristics (#512, fixes #484)
- Affinity-aware hop resolution — hop resolver uses neighbor affinity to pick better paths (#511)
- Node detail neighbors section — dedicated neighbors panel on the node detail page (#510)
- Affinity debugging tools — inspect and troubleshoot affinity calculations (#521)
- Neighbor graph visualization — interactive neighbor graph in the analytics tab (#513)
Customizer v2
- Event-driven state management replaces the old imperative approach — cleaner, more predictable theme/config updates (#503)
🐛 Bug Fixes
- Stale parsed cache on observation packets — observation packets now correctly invalidate the JSON parse cache (#505)
- Null-guard rAF callbacks — live page no longer crashes when
requestAnimationFramecallbacks fire after cleanup (#506) - Customizer v2 phantom overrides — fixed phantom config entries, missing defaults, and stale dark mode state (#520)
- Neighbor affinity empty results — fixed pubKey field name mismatch causing empty affinity graphs (#524)
- Home defaults in server theme — server-side theme config now includes home page defaults (#526)
- Neighbor UI crash + dark mode — fixed Show Neighbors crash and improved dark mode contrast (#527)
- Home page steps + FAQ — both steps AND FAQ now render correctly on the home page (#529)
⚡ Performance
- Cached JSON.parse for packet data — packet payloads are parsed once and cached, avoiding redundant
JSON.parsecalls on repeated access (#400)
Known Limitations
- Affinity graph scales with traffic volume — networks with very low packet rates may show weak or missing neighbor relationships until enough data accumulates
- Debugging tools are developer-facing — the affinity debug panel (#521) is functional but not polished for end-user consumption
- Customizer v2 migration — custom themes saved under v1 may need to be re-applied after upgrade
CoreScope v3.3.0 — Performance & Polish
CoreScope v3.3.0 — Performance & Polish
28 changes since v3.2.0. This release is all about making CoreScope faster, more accurate, and harder to break.
⚡ Performance Overhaul
- Virtual scroll for packets table — handles 10K+ packets without breaking a sweat (#402)
- Observer Map lookups — O(1) instead of linear scans on every render (#468)
- requestAnimationFrame animations — live page ditches setInterval, no more stacking when tab-switching (#470)
- Client-side My Nodes filter — instant toggle, zero server round-trips (#401)
- Server-side collision analysis — heavy computation moved off the browser (#415)
- In-place ADVERT upserts — node updates without full page reload (#461)
- Targeted cache invalidation — analytics refresh only what changed (#379)
- Faster /api/packets and /api/channels — query optimizations for large stores (#328)
🗺️ New Features
- Show Direct Neighbors — click any node on the map to filter to its 1-hop neighbors (#480)
- Auto cache busters —
__BUST__placeholders replaced at startup, no more merge conflicts over timestamps (#481) - Release tag pinning —
manage.sh update v3.3.0pins to exact versions (#456)
🔧 Fixes
- Haversine distances — hop distances now use proper great-circle math instead of flat-earth approximation (#478)
- Observer online status — packet ingestion updates last_seen, fixing false "offline" flags (#479)
- Hash collision region filter — analytics respects region selection (#477)
- Channel hash display — shows hex (0x1A) instead of confusing decimal (#471)
- VCR timezone — timeline and clock respect UTC/local toggle (#459)
- Graceful shutdown — WAL checkpoint on container stop (#453)
- PerfStats data race — mutex synchronization eliminates concurrent access bugs (#469)
- Multiple null-guard fixes — no more crashes on navigate-away (#454, #462)
- Score/direction extraction — MQTT fields properly parsed with unit stripping (#371)
- Config reset — no more SITE_CONFIG contamination after reset (#460)
- Staging config — always refreshes from prod (#467)
📊 By the Numbers
- 28 merged PRs
- 8 performance improvements
- 13 bug fixes
- 3 new features
- 2 CI/docs improvements
- ~1,400 lines added across 23 files
CoreScope v3.2.0 — Stability & Data Integrity
A stability and data integrity release — fixing critical crashes, broken observer data, and WebSocket storms, while adding transport code visibility, geo-filtering, and a fully responsive navigation bar.
15 commits · 35 files · +1,659 / -229 · 3 contributors · 9 issues closed
🐛 Critical Fixes
Packets page crash on mobile (#326) — Number(null) silently set the time window to "All time", loading 50K+ packets and crashing iOS Safari/Edge. Fixed with proper guard, 1K packet cap on mobile, and restricted time window options.
Observer metadata broken since Go migration (#320, #321) — Battery, uptime, noise floor read at wrong nesting level; SNR/RSSI case-sensitive with no fallback. All fields now work with 10 regression tests.
Hash stats frozen on historical data (#303) — Reconfigured repeaters now show updated hash size immediately instead of requiring dozens of adverts to shift the statistical mode.
WebSocket replaying entire database (#349) — Corrupted DB caused the poller to start from ID 0, broadcasting 76K packets at ~2,000/sec. Poller now cross-checks against the in-memory store.
✨ Features
Transport code badge (#241) — "T" badge on packets using T_FLOOD / T_DIRECT routing — instant visibility into transport adoption across the mesh.
Geo-filter enforcement (#215) — Server-side polygon boundary filtering, automatic DB pruning for out-of-bounds nodes, and a visual geofilter-builder tool.
Responsive navigation (#322) — Hamburger menu at ≤1023px with drawer scroll, outside-click close, Escape key, and aria-expanded. Priority+ pattern on tablets (768–1023px): 5 primary tabs inline + "More ▾" dropdown for the rest.
🎨 Improvements
Side pane contrast (#334) — Card-style backgrounds on node detail side pane sections, fixing poor dark mode contrast.
WebSocket time filtering — WS packets now respect the selected time window instead of showing data from days ago.
CI runner cleanup (#333) — Automatic Docker image pruning after deploy. Runner was 100% full (18.8GB of dangling images).
Full changelog: v3.1.0...v3.2.0