Commit Graph

881 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 1e39b2439c Scribe: Record Hicks perf (#143), Bishop E2E expansion (+25 tests #144), issue triage (#134-#142 closed, #146 filed)
- Hicks: Go /api/perf endpoint with runtime.MemStats, GC pauses, cache metrics
- Hicks: Fixed /api/health to include schema compat detection
- Bishop: 25 new Playwright E2E tests (42 total) covering perf, audio, channels, observers, traces
- Issues #134-#142 manually closed (dupes/Polish already fixed by Newt)
- Issue #146 filed: unique node count bug (6502 phantom nodes)
- CI run #565 all tests passing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:47: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 4ca850fdd0 test(e2e): add Playwright tests for all uncovered pages #144
Add 25 new E2E tests covering pages that previously had no Playwright coverage:

Packets page (4 tests):
- Detail pane hidden on fresh load
- groupByHash toggle
- Clicking row shows detail pane
- Detail pane close button

Analytics sub-tabs (7 tests):
- RF, Topology, Channels, Hash Stats, Hash Issues, Route Patterns, Distance

Compare page (2 tests):
- Observer dropdowns populate
- Running comparison produces results

Live page (2 tests):
- Page loads with map and stats
- WebSocket connection indicators

Channels page (2 tests):
- Channel list loads with items
- Clicking channel shows messages

Traces page (2 tests):
- Search input and button present
- Search returns results for valid hash

Observers page (2 tests):
- Table loads with rows
- Health indicators present

Perf page (2 tests):
- Metrics load
- Refresh button works

Audio Lab page (3 tests):
- Controls load (play, voice, BPM, volume)
- Sidebar lists packets by type
- Clicking packet shows detail and hex dump

Total: 42 tests (was 17). All new tests validated against analyzer.00id.net.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:42:35 -07:00
Kpa-clawbot a6f713a09c chore: bump cache busters to trigger CI deploy 2026-03-27 10:40:18 -07:00
Kpa-clawbot d007ed03f7 ci: trigger deploy with all fixes included 2026-03-27 10:38:45 -07:00
Kpa-clawbot 57bb5aaac9 ci: trigger fresh run with all fixes 2026-03-27 10:37:05 -07:00
Kpa-clawbot 3f10ca6065 Session close: merge decisions inbox, finalize logs
- Merged copilot-directive-scribe-always.md into decisions.md
- Added 2026-03-27T17:13 directive: Scribe auto-run after agent batches
- Verified all 6 orchestration logs (Hicks, Newt, Hudson, Bishop, Kobayashi, Ripley)
- Appended scribe consolidation summary to 2026-03-27T16-session.md
- Deleted inbox file after merge

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:27:12 -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 7bd14dce6a fix: run go tool cover from module directory, not repo root
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:21:59 -07:00
Kpa-clawbot 9ca7777851 fix: version-badge link contrast in nav stats bar
Style .version-badge anchor elements to use --nav-text-muted color
instead of browser-default blue. Adds hover state using --nav-text.
Works with both light and dark themes via existing CSS variables.

fixes #139

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:14:50 -07:00
Kpa-clawbot 7807063967 ci: add Go test coverage reporting to CI pipeline
- Go server and ingestor tests now run with -coverprofile
- Coverage percentages parsed and printed in CI output
- Badge JSON files generated (.badges/go-server-coverage.json,
  .badges/go-ingestor-coverage.json) matching existing format
- Badges uploaded as artifacts from go-build job, downloaded
  in test job, and published alongside existing Node.js badges
- Coverage summary table added to GitHub Step Summary

fixes #141

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 10:14:14 -07:00
Kpa-clawbot a026a75fc8 .squad/agents/hudson: Summarize history, keep 2026-03-27 session details
- Archived pre-2026-03-27 detailed entries into summary section
- Retained current session context (DB merge, Docker Compose, staging setup)
- File reduced from 33.5KB to 4.4KB while preserving important learnings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:57:42 -07:00
Kpa-clawbot b6c567903f .squad: Merge session inbox to decisions, update agent histories
- Merged all 27 inbox decision files into decisions.md (comprehensive dedup)
- Cleaned inbox/ directory (only .gitkeep remains)
- Updated agent histories with 2026-03-27 session context:
  - Kobayashi: DB merge plan, #133 triage, team coordination
  - Hicks: 6 fixes + Go rewrite completion, API parity, phantom node cleanup
  - Newt: 4 frontend fixes, live page improvements, observer comparison
  - Bishop: PR reviews, test gap fixes, E2E validation
  - Hudson: DB merge execution, Docker Compose migration, staging setup
  - Ripley: Support engineer onboarded, staleness thresholds documented
- decisions.md now comprehensive reference (20+ technical decisions)
- Removed 51 lines of duplicate inbox entries

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:56:44 -07:00
Kpa-clawbot b92e71fa0e refine version badge: clickable links, version only on prod
- Commit hash is now an <a> linking to GitHub commit (full hash in URL, 7-char display)
- Version tag only shown on prod (port 80/443 or no port), linked to GitHub release
- Staging (non-standard port) shows commit + engine only, no version noise
- Detect prod vs staging via location.port
- Updated tests: 16 cases covering prod/staging/links/edge cases
- Bumped cache busters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:54:53 -07:00
Kpa-clawbot a7a280801a feat: display version and commit hash in stats bar
Add formatVersionBadge() that renders version, short commit hash, and
engine as a single badge in the nav stats area. Format: v2.6.0 · abc1234 [go].
Skips commit when 'unknown' or missing. Truncates commit to 7 chars.
Replaces the standalone engine badge call in updateNavStats().

8 unit tests cover all edge cases (missing fields, v-prefix dedup,
unknown commit, truncation).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:52:13 -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 ab879b78fe fix: remove continue-on-error from Go staging deploy — broken deploys should fail CI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:37:14 -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 013a67481f ci: add Go staging auto-deploy to CI pipeline
Build and deploy the Go staging container (port 82) after Node staging
is healthy. Uses continue-on-error so Go staging failures don't block
the Node.js deploy. Health-checks the Go container for up to 60s and
verifies /api/stats returns the engine field.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:34:16 -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 e47b5f85ed feat: display backend engine badge in stats bar
Show [go] or [node] badge in the nav stats bar when /api/stats
returns an engine field. Gracefully hidden when field is absent.

- Add formatEngineBadge() to app.js (top-level, testable)
- Add .engine-badge CSS class using CSS variables
- Add 5 unit tests in test-frontend-helpers.js
- Bump cache busters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:31:32 -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 bad023ccad fix: hide packet detail pane on fresh page load
Add detail-collapsed class to split-layout initial HTML so the empty
right panel is hidden before any packet is selected. The class is
already removed when a packet row is clicked and re-added when the
close button is pressed.

Add 3 tests verifying the detail pane starts collapsed and that
open/close toggling is wired correctly.

Bump cache busters.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 09:10:51 -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 1137dd1c08 test: close test gaps for #123 decrypted status and #131 WS handler runtime
Gap 1 (#123): Add 3 decoder tests for GRP_TXT decrypted status path.
Mock ChannelCrypto via require.cache to simulate successful decryption.
Tests cover: sender+message formatting, no-sender fallback, multi-key
iteration with first-match-wins semantics.

Gap 2 (#131): Rewrite 5 src.includes() string-match tests as runtime
vm.createContext tests. New makeNodesWsSandbox() helper with controllable
setTimeout, mock DOM, tracked API/cache calls, and real debouncedOnWS.
Tests verify: ADVERT triggers refresh, non-ADVERT ignored, debounce
collapses multiple ADVERTs, cache reset forces re-fetch, scroll/selection
preserved during WS-triggered refresh.

Decoder: 58 -> 61 tests. Frontend helpers: 87 (5 replaced, not added).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:47:52 -07:00
Kpa-clawbot 3cd6cb98fa ci: add Go build/test job, re-enable frontend coverage, clean up temp files
- Add go-build job to deploy.yml that builds and tests cmd/server and cmd/ingestor
- Go job gates the Node.js test job and deploy job
- Re-enable frontend coverage detection (was hardcoded to false)
- Remove stale temp files from repo root (recover-delta.sh, merge.sh, replacements.txt, reps.txt)
- Add temp scripts and Go build artifacts to .gitignore

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:47:10 -07:00
Kpa-clawbot e3cb0421b1 fix(docker): resolve Go staging port conflict and MQTT connectivity
- Change staging-go HTTP port from 81 to 82 (via STAGING_GO_HTTP_PORT)
  to avoid conflict with CI's Node.js staging on port 81
- Change staging-go MQTT port from 1884 to 1885 (via STAGING_GO_MQTT_PORT)
  to avoid conflict with Node.js staging MQTT on port 1884
- Add MQTT_BROKER=mqtt://localhost:1883 env var so Go ingestor connects
  to its own internal mosquitto instead of unreachable prod external IP

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:42:51 -07:00
github-actions 724b9581fd ci: update test badges [skip ci] 2026-03-27 15:12:06 +00:00
Kpa-clawbot 99c23f8b59 feat: add observer packet comparison page (fixes #129)
Add #/compare page that lets users select two observers and compare
which packets each sees. Fetches last 24h of packets per observer,
computes set diff client-side using O(n) Set lookups. Shows summary
cards (both/only-A/only-B), stacked bar, type breakdown, and tabbed
detail tables. URL is shareable via ?a=ID1&b=ID2 query params.

- New file: public/compare.js (comparePacketSets + page module)
- Added compare button to observers page header
- 11 new tests for comparePacketSets (87 total frontend tests)
- Cache busters bumped

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:11:30 -07:00
github-actions bb9eccebce ci: update test badges [skip ci] 2026-03-27 15:09:15 +00:00
Kpa-clawbot 55db2bef27 fix: auto-update Nodes tab when ADVERT packets arrive via WebSocket
Fixes #131

The Nodes tab required a full page reload to see newly advertised nodes
because loadNodes() cached the node list in _allNodes and never
re-fetched it on WebSocket updates.

Changes:
- WS handler now filters for ADVERT packets only (payload_type 4 or
  payloadTypeName ADVERT), instead of triggering on every packet type
- Uses 5-second debounce to avoid excessive API calls during bursts
- Resets _allNodes cache and invalidates API cache before re-fetching
- loadNodes(refreshOnly) parameter: when true, updates table rows and
  counts without rebuilding the entire panel (preserves scroll position,
  selected node, tabs, filters, and event listeners)
- Extracted isAdvertMessage() as testable helper with window._nodesIsAdvertMessage hook
- 13 new tests (76 total frontend helpers)
- Cache busters bumped

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:08:43 -07:00
github-actions d793a561f3 ci: update test badges [skip ci] 2026-03-27 15:02:41 +00:00
Kpa-clawbot 65a7f055de fix: dim stale nodes on live map instead of removing them
Fixes #130 — Nodes loaded from the database (API) are now dimmed with
reduced opacity when stale, matching the static map behavior, instead of
being completely removed by pruneStaleNodes(). WS-only (dynamically
added) nodes are still pruned to prevent memory leaks.

Changes:
- loadNodes() marks API-loaded nodes with _fromAPI flag
- pruneStaleNodes() dims _fromAPI nodes (fillOpacity 0.25) vs removing
- Active nodes restore full opacity when refreshed
- 3 new tests for dim/restore/WS-only behavior (63 total passing)
- Cache busters bumped

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 08:02:09 -07:00
github-actions a75b533495 ci: update test badges [skip ci] 2026-03-27 14:59:47 +00:00
Kpa-clawbot aea0fc51bd fix: skip ambiguous hop prefixes in path-seen tracking (fixes #126)
When multiple nodes share the same hash prefix (e.g. 1CC4 and 1C82 both
starting with 1C under 1-byte hash_size), updatePathSeenTimestamps() was
non-deterministically picking the first DB match, keeping dead nodes alive
on the map. Now resolveUniquePrefixMatch() only resolves prefixes that
match exactly one node. Ambiguous prefixes are cached in a negative-cache
set to avoid repeated DB queries.

- Extract resolveUniquePrefixMatch() used by both autoLearnHopNodes and
  updatePathSeenTimestamps (DRY)
- Add ambiguousHopPrefixes negative cache (Set)
- LIMIT 2 in the uniqueness query to detect collisions efficiently
- 3 new regression tests: ambiguous prefix, unique prefix, 1-byte
  collision scenario (204 -> 207 tests)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 07:58:28 -07:00
Kpa-clawbot 616af26981 fix: Go ingestor normalize mqtt:// to tcp:// and mqtts:// to ssl:// for paho
Paho MQTT client uses tcp:// and ssl:// schemes, not mqtt:// and mqtts://.
Also properly configure TLS for mqtts connections with InsecureSkipVerify
when rejectUnauthorized is false.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 07:56:57 -07:00
github-actions f852bce56c ci: update test badges [skip ci] 2026-03-27 14:56:25 +00:00
Kpa-clawbot 14b7e56403 Show channel hash and decryption status for undecrypted GRP_TXT packets
When a CHANNEL_MSG (GRP_TXT) can't be decrypted, the decoder now includes:
- channelHashHex: zero-padded uppercase hex string of the channel hash byte
- decryptionStatus: 'decrypted', 'no_key', or 'decryption_failed'

Frontend changes:
- Packet list preview shows '🔒 Ch 0xXX (no key)' or '(decryption failed)'
- Detail pane hex breakdown shows channel hash with status label
- Detail pane message area shows channel hash info for undecrypted packets

6 new decoder tests (58 total): channelHashHex formatting, decryptionStatus
for no keys, empty keys, bad keys, and short encrypted data.

Fixes #123

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 07:54:27 -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
github-actions 38d4840b10 ci: update test badges [skip ci] 2026-03-27 09:06:03 +00:00
Kpa-clawbot eec6caaa48 fix: stop autoLearnHopNodes from creating phantom nodes, fixes #133
autoLearnHopNodes was creating stub 'repeater' entries in the nodes table
for every unresolved hop prefix. With hash_size=1, this generated thousands
of phantom nodes (6,638 fake repeaters on a ~300-node mesh).

Root cause fix:
- autoLearnHopNodes no longer calls db.upsertNode() for unresolved hops
- Hop prefixes are still cached to avoid repeated DB lookups
- Unresolved hops display as raw hex via hop-resolver (no behavior change)

Cleanup:
- Added db.removePhantomNodes() — deletes nodes with public_key <= 16 chars
  (real MeshCore pubkeys are 64 hex chars / 32 bytes)
- Called at server startup to purge existing phantoms

Tests: 14 new assertions in test-db.js (109 total, all passing)

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