Commit Graph

22 Commits

Author SHA1 Message Date
Kpa-clawbot
5bb5bea444 fix(go): channels null arrays + hash size enrichment on nodes
- Fix #148: channels endpoint returned null for msgLengths when no
  decrypted messages exist. Initialize msgLengths as make([]int, 0)
  in store path and guard channels slice in DB fallback path.

- Fix #149: nodes endpoint always returned hash_size=null and
  hash_size_inconsistent=false. Add GetNodeHashSizeInfo() to
  PacketStore that scans advert packets to compute per-node hash
  size, flip-flop detection, and sizes_seen. Enrich nodes in both
  handleNodes and handleNodeDetail with computed hash data.

fixes #148, fixes #149

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 11:21:41 -07:00
Kpa-clawbot
407c49e017 fix(go): add eventLoop to /api/health with GC pause percentiles, fixes #147
Go's /api/health was missing the eventLoop object that Node.js provides.
The perf.js frontend reads health.eventLoop.p95Ms which crashed with
'Cannot read properties of undefined' when served by the Go server.

Adds eventLoop field using GC pause data from runtime.MemStats.PauseNs
(last 256 pauses) to compute p50Ms, p95Ms, p99Ms, currentLagMs, maxLagMs
— matching the Node.js response shape exactly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 11:04:43 -07:00
Kpa-clawbot
8a0f731452 fix: topology uniqueNodes counts only real nodes, not hop prefixes
The Go analytics topology endpoint was counting every unique hop string
from packet paths (including unresolved 1-byte hex prefixes) as a unique
node, inflating the count from ~540 to 6502. Now resolves each hop via
the prefix map and deduplicates by public key, matching the Node.js
behavior.

fixes #146

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:50:32 -07:00
Kpa-clawbot
93dbe0e909 fix(go): add runtime stats to /api/perf and /api/health, fixes #143
- /api/perf: add goRuntime (heap, GC, goroutines, CPU), packetStore
  stats (totalLoaded, observations, index sizes, estimatedMB),
  sqlite stats (dbSizeMB, walSizeMB, row counts), real RF cache
  hit/miss tracking, and endpoint sorting by total time spent
- /api/health: add memory.heapMB, goRuntime (goroutines, gcPauses,
  numCPU), real packetStore packet count and estimatedMB, real
  cache stats from RF cache; remove hardcoded-zero eventLoop
- store.go: add cacheHits/cacheMisses tracking in GetAnalyticsRF,
  GetPerfStoreStats() and GetCacheStats() methods
- db.go: add path field to DB struct, GetDBSizeStats() for file
  sizes and row counts
- Tests: verify new fields in health/perf endpoints, add
  TestGetDBSizeStats, wire up PacketStore in test server setup

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:45:00 -07:00
Kpa-clawbot
d7172961f4 fix(go): analytics endpoints parity — fixes #134, #135, #136, #137, #138, #140, #142
Implement all analytics endpoints from in-memory PacketStore instead of
returning stubs/empty data. Each handler now matches the Node.js response
shape field-by-field.

Endpoints fixed:
- /api/analytics/topology (#135): full hop distribution, top repeaters,
  top pairs, hops-vs-SNR, per-observer reachability, cross-observer
  comparison, best path analysis
- /api/analytics/distance (#137): haversine distance computation,
  category stats (R↔R, C↔R, C↔C), distance histogram, top hops/paths,
  distance over time
- /api/analytics/hash-sizes (#136): hash size distribution from raw_hex
  path byte parsing, hourly breakdown, top hops, multi-byte node tracking
- /api/analytics/hash-issues (#138): hash-sizes data now populated so
  frontend collision tab can compute inconsistent sizes and collision risk
- /api/analytics/route-patterns (#134): subpaths and subpath-detail now
  compute from in-memory store with hop resolution
- /api/nodes/bulk-health (#140): switched from N per-node SQL queries to
  in-memory PacketStore lookups with observer stats
- /api/channels (#142): response shape already correct via GetChannels;
  analytics/channels now returns topSenders, channelTimeline, msgLengths
- /api/analytics/channels: full channel analytics with sender tracking,
  timeline, and message length distribution

All handlers fall back to DB/stubs when store is nil (test compat).
All 42+ existing Go tests pass. go vet clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:23:11 -07:00
Kpa-clawbot
6cdbf7e3f6 perf(go): remove debug logging, update history
Remove temporary rf-cache debug logs. Update hicks history with
endpoint optimization learnings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:49:46 -07:00
Kpa-clawbot
b42d7e3f14 debug: add RF cache logging
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:43:40 -07:00
Kpa-clawbot
6158734536 fix(go): fix RF cache - use separate mutex, TTL-only expiry
Previous approach invalidated cache on every ingest (every 1s with live
mesh data). Now uses TTL-only expiry (15s). Separate cache mutex avoids
data race with main store RWMutex.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:40:43 -07:00
Kpa-clawbot
0d9b535451 feat: add version and git commit to /api/stats and /api/health
Node.js: reads version from package.json, commit from .git-commit file
or git rev-parse --short HEAD at runtime, with unknown fallback.

Go: uses -ldflags build-time variables (Version, Commit) with fallback
to .git-commit file and git command at runtime.

Dockerfile: copies .git-commit if present (CI bakes it before build).
Dockerfile.go: passes APP_VERSION and GIT_COMMIT as build args to ldflags.
deploy.yml: writes GITHUB_SHA to .git-commit before docker build steps.
docker-compose.yml: passes build args to Go staging build.

Tests updated to verify version and commit fields in both endpoints.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:39:49 -07:00
Kpa-clawbot
10c672f8d7 perf(go): add TTL cache for RF analytics response
Cache the computed RF analytics result for 15 seconds.
1.2M observation scan takes ~140ms; cached response <1ms.
Cache invalidated when new packets are ingested.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:35:51 -07:00
Kpa-clawbot
73c1f6636e perf(go): optimize RF analytics inner loop
Move per-transmission work (hash indexing, type resolution, packet sizes)
outside the per-observation loop. Cache SNR dereference, pre-resolve type
name once per transmission. Reduces redundant map lookups from 1.2M to 52K.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:32:45 -07:00
Kpa-clawbot
a1e17ef171 feat: add engine identifier to /api/stats and /api/health
Both backends now return an 'engine' field ('node' or 'go') in
/api/stats and /api/health responses so the frontend can display
which backend is running.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:31:59 -07:00
Kpa-clawbot
876faa6e03 perf(go): optimize channels + RF with payload index and pre-allocation
- Add byPayloadType index to PacketStore for O(1) type-5 lookups
- Channels scan reduced from 52K to ~17K packets (3x fewer iterations)
- Use struct-based JSON decoding (avoids map[string]interface{} allocations)
- Pre-allocate snrVals/rssiVals/scatterAll with capacity hints for 1.2M obs
- Remove second-pass time.Parse loop (1.2M calls) in RF analytics
  Track min/max timestamps as strings during first pass instead
- Index also populated during IngestNewFromDB for new packets

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:27:38 -07:00
Kpa-clawbot
42afbb1398 perf(go): switch channels + RF analytics to in-memory store
Replace SQLite-backed handlers for /api/channels, /api/channels/:hash/messages,
/api/analytics/rf, and /api/analytics/channels with in-memory PacketStore queries.

Before (SQLite via packets_v VIEW on 1.2M rows):
  /api/channels              7.2s
  /api/channels/:hash/msgs   8.2s
  /api/analytics/rf           4.2s

After (in-memory scan of ~50K transmissions):
  Target: all under 100ms

Three new PacketStore methods:
- GetChannels(region) — filters payload_type 5 + decoded type CHAN
- GetChannelMessages(hash, limit, offset) — deduplicates by sender+hash
- GetAnalyticsRF(region) — full RF stats with histograms, scatter, per-type SNR

All handlers fall back to DB queries when store is nil (test compat).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:17:24 -07:00
Kpa-clawbot
afe16db960 feat(go-server): in-memory packet store — port of packet-store.js
Streams transmissions + observations from SQLite at startup into
5 indexed in-memory structures. QueryPackets and QueryGroupedPackets
now serve from RAM (<10ms) instead of hitting SQLite (2.3s).

- store.go: PacketStore with byHash, byTxID, byObsID, byObserver, byNode indexes
- main.go: create + load store at startup
- routes.go: dispatch to store for packet/stats endpoints
- websocket.go: poller ingests new transmissions into store

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:52:07 -07:00
Kpa-clawbot
dbac8e9d52 fix: Go server since/until filter uses observation timestamp, not first_seen
The frontend sends ISO timestamps to filter by observation time.
Go was filtering by transmission first_seen which missed packets
with recent observations but old first_seen. Now converts ISO to
unix epoch and queries the observations table directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 07:53:59 -07:00
Kpa-clawbot
5c68605f2c feat(go-server): full API parity with Node.js server
Performance:
- QueryGroupedPackets: 8s → <100ms (transmissions table, not packets_v VIEW)

Field parity:
- /api/stats: totalNodes uses 7-day window, added totalNodesAllTime
- /api/stats: role counts filtered by 7-day (matching Node.js)
- /api/nodes: role counts use all-time (matching Node.js)
- /api/packets/🆔 path field returns parsed path_json hops
- /api/packets: added multi-node filter (?nodes=pk1,pk2)
- /api/observers: packetsLastHour, lat, lon, nodeRole computed
- /api/observers/🆔 packetsLastHour computed
- /api/nodes/bulk-health: per-node stats from SQL

Tests updated with dynamic timestamps for 7-day filter compat.
All tests pass, go vet clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 02:11:33 -07:00
Kpa-clawbot
e18a73e1f2 feat: Go server API parity with Node.js — response shapes, perf, computed fields
- Packets query rewired from packets_v VIEW (9s) to direct table joins (~50ms)
- Packet response: added first_seen, observation_count; removed created_at, score
- Node response: added last_heard, hash_size, hash_size_inconsistent
- Schema-aware v2/v3 detection for observer_idx vs observer_id
- All Go tests passing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 01:50:46 -07:00
Kpa-clawbot
842b49e8c4 perf: fast-path count for unfiltered /api/packets (skip packets_v scan)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 01:29:22 -07:00
Kpa-clawbot
b2e6c8105b fix: handle WebSocket upgrade at root path (client connects to ws://host/)
Node.js upgrades WS at /, Go was only at /ws. Now the static file
handler checks for Upgrade header first and routes to WebSocket.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 01:25:35 -07:00
Kpa-clawbot
742ed86596 feat: add Go web server (cmd/server/) — full API + WebSocket + static files
35+ REST endpoints matching Node.js server, WebSocket broadcast,
static file serving with SPA fallback, config.json support.
Uses modernc.org/sqlite (pure Go, no CGO required).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 01:16:59 -07:00
Kpa-clawbot
e89c2bfe1f test: add comprehensive Go test coverage for ingestor (80%) and server (90%)
- ingestor: add config_test.go (LoadConfig, env overrides, legacy MQTT)
- ingestor: add main_test.go (toFloat64, firstNonEmpty, handleMessage, advertRole)
- ingestor: extend decoder_test.go (short buffer errors, edge cases, all payload types)
- ingestor: extend db_test.go (empty hash, timestamp updates, BuildPacketData, schema)
- server: add config_test.go (LoadConfig, LoadTheme, health thresholds, ResolveDBPath)
- server: add helpers_test.go (writeJSON/Error, queryInt, mergeMap, round, percentile, spaHandler)
- server: extend db_test.go (all query functions, filters, channel messages, node health)
- server: extend routes_test.go (all endpoints, error paths, analytics, observer analytics)
- server: extend websocket_test.go (multi-client, buffer full, poller cycle)

Coverage: ingestor 48% -> 80%, server 52% -> 90%

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 00:07:44 -07:00