Commit Graph
932 Commits
Author SHA1 Message Date
Kpa-clawbotandCopilot 2435f2eaaf fix: observation timestamps, leaked fields, perf path normalization
- #178: Use strftime ISO 8601 format instead of datetime() for observation
  timestamps in all SQL queries (v3 + v2 views). Add normalizeTimestamp()
  helper for non-v3 paths that may store space-separated timestamps.

- #179: Strip internal fields (decoded_json, direction, payload_type,
  raw_hex, route_type, score, created_at) from ObservationResp. Only
  expose id, transmission_id, observer_id, observer_name, snr, rssi,
  path_json, timestamp — matching Node.js parity.

- #180: Remove _parsedDecoded and _parsedPath from node detail
  recentAdverts response. These internal/computed fields were leaking
  to the API. Updated golden shapes.json accordingly.

- #181: Use mux route template (GetPathTemplate) for perf stats path
  normalization, converting {param} to :param for Node.js parity.
  Fallback to hex regex for unmatched routes. Compile regexes once at
  package level instead of per-request.

fixes #178, fixes #179, fixes #180, fixes #181

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 18:09:36 -07:00
Kpa-clawbotandCopilot 2c9c6503fb scribe: merge inbox decisions
Deduplicated protobuf contract + fixture directive into single entry.
Protobuf API contract is now single source of truth for all frontend/backend interfaces, with fixture capture running against prod (stable).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 18:02:13 -07:00
Kpa-clawbotandCopilot 91a8c0405f feat(go): implement channel decryption for GRP_TXT packets, fixes #176
Go ingestor never had channel decryption — GRP_TXT packets were stored
with raw encrypted data while Node.js decoded them successfully.

Changes:
- decoder.go: Add decryptChannelMessage() implementing MeshCore channel
  crypto (HMAC-SHA256 MAC verification + AES-128-ECB decryption), matching
  the algorithm in @michaelhart/meshcore-decoder. Update decodeGrpTxt(),
  decodePayload(), and DecodePacket() to accept and pass channel keys.
  Add Payload fields: ChannelHashHex, DecryptionStatus, Channel, Text,
  Sender, SenderTimestamp.
- config.go: Add ChannelKeysPath and ChannelKeys fields to Config struct.
- main.go: Add loadChannelKeys() that loads channel-rainbow.json (same
  file used by Node.js server) from beside the config file, with env var
  and config overrides. Pass loaded keys through the decoder pipeline.
- decoder_test.go: Add 14 channel decryption tests covering valid
  decryption, MAC failure, wrong key, no-sender messages, bracket
  sender exclusion, key iteration, channelHashHex formatting, and
  decryption status states. Cross-validated against Node.js output.
- Update all DecodePacket/decodePayload/decodeGrpTxt/handleMessage call
  sites in test files to pass the new channelKeys parameter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 17:55:52 -07:00
Kpa-clawbotandCopilot a51b77ea11 add tools/live-comparison.sh for Go vs Node API parity testing
Automated script that compares all 13 major API endpoints between
Go staging (meshcore-staging-go) and Node prod (meshcore-prod)
containers. Uses python3 for JSON field diffing and reports
MATCH/PARTIAL/MISMATCH per endpoint.

Usage: scp to server then run, or pipe via ssh.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 17:51:19 -07:00
Kpa-clawbotandCopilot d16d3dffd6 fix: remove MQTT_BROKER env override — let config.json mqttSources connect to external brokers
The env var was overriding the config and forcing Go staging to only
connect to its own empty local mosquitto, missing all external data.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 17:27:08 -07:00
Kpa-clawbotandCopilot df63efa78d fix: poll new observations for existing transmissions (fixes #174)
The poller only queried WHERE t.id > sinceID, which missed new
observations added to transmissions already in the store. The trace
page was correct because it always queries the DB directly.

Add IngestNewObservations() that polls observations by o.id watermark,
adds them to existing StoreTx entries, re-picks best observation, and
invalidates analytics caches. The Poller now tracks both lastTxID and
lastObsID watermarks.

Includes tests for v3, v2, dedup, best-path re-pick, and
GetMaxObservationID.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 17:26:26 -07:00
Kpa-clawbotandCopilot ef72484ad2 fix: widen trace page layout to fill screen, fixes #175
- Change .traces-page max-width from 1000px to 95vw via CSS variable
  (--trace-max-width) and center with margin: 0 auto
- Increase SVG path graph column spacing from 140px to 200px so nodes
  and labels don't overlap
- Bump cache busters

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 17:23:31 -07:00
Kpa-clawbotandCopilot 59c225593f fix(go): resolve 6 backend issues — #165 #168 #169 #170 #171 #172
#165 — build_time in API: already implemented (BuildTime ldflags in
Dockerfile.go, main.go, StatsResponse, HealthResponse)

#168 — subpaths API slow: cache (subpathCache with TTL) and invalidation
already in place; verified working

#169 — distance API slow: cache (distCache with TTL) and invalidation
already in place; verified working

#170 — audio-lab/buckets: in-memory store path already implemented,
matching Node.js pktStore.packets iteration with type grouping and
size-distributed sampling

#171 — channels stale latest message: add companion bridge handling to
Go ingestor for meshcore/message/channel/<n> and meshcore/message/direct/<id>
MQTT topics. Stores decoded channel messages with type CHAN in decoded_json,
enabling the channels endpoint to find them. Also handles direct messages.

#172 — packets page not live-updating: add missing direction field to WS
broadcast packet map for full parity with txToMap/Node.js fullPacket shape.
WS broadcast shape verified correct (type, data.packet structure, timestamp,
payload_type, observer_id all present).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 16:06:24 -07:00
Kpa-clawbotandCopilot 64bf3744e2 fix: channels stale latest message from observation-timestamp ordering, fixes #171
db.GetChannels() queried packets_v (observation-level rows) ordered by
observation timestamp and always overwrote lastMessage. When an older
message had a later re-observation, it would overwrite the correct
latest message with stale data.

Fix: query transmissions table directly (one row per unique message)
ordered by first_seen. This ensures lastMessage always reflects the
most recently sent message, not the most recently observed one.

Also fix db.GetChannelMessages() to use first_seen ordering with
schema-aware queries (v2/v3), and add missing distCache/subpathCache
invalidation on packet ingestion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 16:01:54 -07:00
Kpa-clawbotandCopilot 30bcfff45b fix(go): audio-lab/buckets use in-memory store, match Node.js behavior
Use s.store (in-memory PacketStore) instead of direct packets_v SQL query,
matching how the Node.js handler iterates pktStore.packets. This fixes the
endpoint returning empty buckets when packets_v view is missing or the DB
query fails silently.

Key changes:
- Group by decoded_json.type first, fall back to payloadTypeNames
- Evenly-spaced sampling (up to 8 per type) sorted by raw_hex length
- Use actual ObservationCount instead of hardcoded 1
- Reuse payloadTypeNames from store.go instead of duplicating
- Retain DB fallback path when store is nil

fixes #170

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:47:32 -07:00
Kpa-clawbotandCopilot f5ea4d5a87 docs: Merge decision inbox — proto fixture capture and protobuf architecture contract
- Added decision: Proto fixture capture in CI runs against prod (stable reference)
- Added decision: Protobuf API contract architecture (single source of truth for frontend/backend interfaces)
- Merged 2 directives from spawn manifest processing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:46:07 -07:00
Kpa-clawbotandCopilot e300228874 perf: add TTL cache for subpaths API + build timestamp in stats/health
- Add 15s TTL cache to GetAnalyticsSubpaths with composite key (region|minLen|maxLen|limit),
  matching the existing cache pattern used by RF, topology, hash, channel, and distance analytics.
  Cache hits return instantly vs 900ms+ computation. fixes #168

- Add BuildTime to /api/stats and /api/health responses, injected via ldflags at build time.
  Dockerfile.go now accepts BUILD_TIME build arg. fixes #165

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:44:06 -07:00
Kpa-clawbotandCopilot 4a6ac482e6 ci: fix proto syntax check command — fixes #173
The proto validation infrastructure was added in commit e70ba44 but used
an invalid --syntax_check flag. Changed to use --descriptor_set_out=/dev/null
which validates syntax without generating files.

Proto validation flow (now complete):
1. go-test job: verify .proto files compile (syntax check) 
2. deploy-node job: validate protos match prod API responses 

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:43:18 -07:00
Kpa-clawbotandCopilot d3347f9d99 fix(analytics): channels table perf + sortable columns (#166, #167)
Performance (#166):
- renderChannelTimeline: replace O(n²) data.find() with O(1) lookup map
- renderChannelTimeline: precompute maxCount once instead of per-point
- renderChannels: pre-build sub-section HTML before single innerHTML write

Sortable columns (#167):
- All 6 channel table columns are now sortable (click header)
- Default sort: last activity descending (latest message first)
- Sort preference persists to localStorage (meshcore-channel-sort)
- Toggles asc/desc on re-click; smart default direction per column type
- Uses existing .sortable/.sort-active CSS patterns on .analytics-table

Tests: 23 new tests for sortChannels, loadChannelSort, saveChannelSort,
channelTheadHtml, channelTbodyHtml (134 total frontend tests, 0 failures)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:42:06 -07:00
Kpa-clawbotandCopilot e70ba440c0 security: scrub PII — remove real name and IP from committed files
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:41:38 -07:00
Kpa-clawbotandCopilot f54a10f04d fix(go): add timestamp field to WS broadcast packet — fixes #172
The Go server's WebSocket broadcast included first_seen but not
timestamp in the nested packet object. The frontend packets.js
filters on m.data.packet and reads p.timestamp for row insertion
and sorting. Without this field, live-updating silently failed
(rows inserted with undefined latest, breaking display).

Mirrors the pattern already used in txToMap() (store.go:1168)
which correctly emits both first_seen and timestamp.

Also updates websocket_test.go to assert timestamp presence
in broadcast data to prevent regression.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:40:57 -07:00
Kpa-clawbotandCopilot 6ec23acfc8 Fix CI: Add Node.js setup to build-node job
The build-node job was failing with 'node: not found' because it
runs scripts/validate.sh (which uses 'node -c' for syntax checking)
but didn't have the actions/setup-node@v4 step.

Added Node.js 22 setup before the validate step to match the pattern
used in other jobs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:39:12 -07:00
Kpa-clawbotandCopilot 75b6dc5d9f perf: add TTL cache for /api/analytics/distance endpoint
The distance analytics endpoint was recomputing haversine distances on
every request (~1.22s). Add a 15s TTL cache following the same pattern
used by RF, topology, hash-sizes, and channels analytics endpoints.
Include distCache in cache stats size calculation.

fixes #169

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:38:43 -07:00
Kpa-clawbotandCopilot dd8a0b72be Scribe: Merge proto contract decisions from spawn batch
- Merged copilot-directive-protobuf-contract.md → decisions.md (architecture decision)
- Merged copilot-directive-fixtures-from-prod.md → decisions.md (user directive)
- Wrote orchestration log entry for spawn batch completion
- decisions.md now 380 lines (7 Go Rewrite decisions, 2 new proto entries)
- Proto validation spike complete: 33 fixtures, 0 errors, compiler-enforced contract

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:20:23 -07:00
Kpa-clawbotandCopilot f55a3454aa feat(go): replace map[string]interface{} with typed Go structs in route handlers
Phase 1: Create cmd/server/types.go with ~80 typed response structs
matching all proto definitions. Every API response shape is now a
compile-time checked struct.

Phase 2: Rewire all route handlers in routes.go to construct typed
structs instead of map[string]interface{} for response building:
- /api/stats -> StatsResponse
- /api/health -> HealthResponse
- /api/perf -> PerfResponse
- /api/config/* -> typed config responses
- /api/nodes/* -> NodeListResponse, NodeDetailResponse, etc.
- /api/packets/* -> PacketListResponse, PacketDetailResponse
- /api/analytics/* -> RFAnalyticsResponse, TopologyResponse, etc.
- /api/observers/* -> ObserverListResponse, ObserverResp
- /api/channels/* -> ChannelListResponse, ChannelMessagesResponse
- /api/traces/* -> TraceResponse
- /api/resolve-hops -> ResolveHopsResponse
- /api/iata-coords -> IataCoordsResponse (typed IataCoord)
- /api/audio-lab/buckets -> AudioLabBucketsResponse
- WebSocket broadcast -> WSMessage struct
- SlowQuery tracking -> SlowQuery struct (was map)

Phase 3 (partial): Add typed store/db methods:
- PacketStore.GetCacheStatsTyped() -> CacheStats
- PacketStore.GetPerfStoreStatsTyped() -> PerfPacketStoreStats
- DB.GetDBSizeStatsTyped() -> SqliteStats

Remaining map usage is in store/db data flow (PacketResult.Packets
still uses maps) — these will be addressed in a follow-up.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:17:21 -07:00
Kpa-clawbotandCopilot b2dc02ee11 fix: capture proto fixtures from prod (stable reference), not staging
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:03:40 -07:00
Kpa-clawbotandCopilot d938e27abb ci: Capture all 33 proto fixtures including dynamic ID endpoints
Previously only captured 19 simple endpoints. Now captures all 33:
- 19 simple endpoints (stats, health, nodes, etc.)
- 14 dynamic ID endpoints (node-detail, packet-detail, etc.)

Dynamic ID resolution:
- Extracts real pubkey from /api/nodes for node detail endpoints
- Extracts real hash from /api/packets for packet-detail
- Extracts real observer ID from /api/observers for observer endpoints
- Gracefully skips fixtures if DB is empty (no data yet)

WebSocket capture:
- Uses node -e with ws module to capture one live WS message
- Falls back gracefully if no live packets available

The validator already handles missing fixtures without failing, so this
will work even when staging container has no data yet.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 15:01:56 -07:00
Kpa-clawbotandCopilot &lt;223556219+Copilot@users.noreply.github.com&gt; dc57168d96 ci: add proto validation step to catch API contract drift
Added a CI step that:
- Refreshes Node fixtures from the staging container after deployment
- Runs tools/validate-protos.py to validate proto definitions match actual API responses
- Fails the pipeline if proto drift is detected

This ensures nobody can merge a Node change that breaks the Go proto contract
without updating the .proto definitions.

The step runs after the Node staging healthcheck, capturing fresh responses
from 19 API endpoints (stats, health, nodes, analytics/*, config/*, etc.).
Endpoints requiring parameters (node-detail, packet-detail) use existing
fixtures and aren't auto-refreshed.

Co-authored-by: Copilot &lt;223556219+Copilot@users.noreply.github.com&gt;
2026-03-27 14:57:21 -07:00
Kpa-clawbotandCopilot 3c53680e7c fix: resolve 24 proto definition mismatches against Node fixtures
fixes #164

Mismatches fixed:
- analytics-channels: ChannelAnalyticsSummary.hash string -> int32
- analytics-rf: PayloadTypeEntry.type -> optional int32 (can be null)
- bulk-health: flatten BulkHealthEntry (remove .node nesting)
- node-analytics: TimeBucket field label -> bucket (keep both as optional)
- observer-analytics: recentPackets Transmission -> Observation
- packet-detail: ByteRange add string color field
- websocket-message: DecodedResult add transportCodes, raw, routeTypeName;
  flatten payload to DecodedFlatPayload; packet -> Observation
- validate-protos: bare-array wrapping note downgraded to WARNING

Validator now reports 0 errors across all 33 fixtures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 14:52:02 -07:00
Kpa-clawbotandCopilot 8414015b2c fix: resolve 15 API contract violations in Go server
- Fix #11: Remove goRuntime and heapMB from /api/health response
- Fix #12: Remove status, uptimeHuman, websocket, goRuntime from /api/perf
- Fix #10: Add POST /api/perf/reset endpoint
- Fix #7: Return real IATA airport coordinates from /api/iata-coords
- Fix #8: Add POST /api/packets endpoint with decode+insert
- Fix #9: Add POST /api/decode endpoint
- Fix #1: Implement real SQL for hopDistribution, uptimeHeatmap,
  computedStats in /api/nodes/:pubkey/analytics
- Fix #2: Implement SQL fallback for /api/analytics/topology
- Fix #3: Implement real SQL queries for /api/nodes/:pubkey/paths
- Fix #4: Add per-observer breakdown in /api/nodes/bulk-health
- Fix #5: Implement SQL fallback for /api/analytics/distance
- Fix #6: Implement timeline, nodesTimeline, snrDistribution in
  /api/observers/:id/analytics

New file: cmd/server/decoder.go -- decoder from ingestor adapted for
server package (uses time.Unix instead of util.go helper)

fixes #163

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 14:45:29 -07:00
Kpa-clawbotandCopilot 87fd6f3417 Add proto validation script: 24 errors across 7 fixtures
Validates all 33 captured Node fixtures against the 10 .proto files.
Parses proto message definitions, maps each fixture to its response
message, and checks field presence, types, and structural shape.

Mismatches found (for Hicks to fix):

1. analytics-channels.json: ChannelAnalyticsSummary.hash is int in
   fixture but proto says string
2. analytics-rf.json: PayloadTypeEntry.type is null in fixture but
   proto says non-optional int32
3. bulk-health.json: API returns bare array with flat node fields;
   proto nests them in BulkHealthEntry.node (structural mismatch)
4. node-analytics.json: activityTimeline uses 'bucket' key but
   TimeBucket proto expects 'label'
5. observer-analytics.json: recentPackets are Observation-shaped
   (have transmission_id) but proto says repeated Transmission
6. packet-detail.json: ByteRange has 'color' field not in proto
7. websocket-message.json: DecodedResult missing transportCodes,
   raw fields; DecodedHeader missing routeTypeName; DecodedPayload
   is flat (not oneof-wrapped); WSPacketData.packet is Observation-
   shaped, not Transmission-shaped

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 14:24:11 -07:00
Kpa-clawbotandCopilot fc494962d1 fix(go): add broadcast diagnostic logging, rebuild fixes stale deployment
The Go staging packets page wasn't live-updating because the deployed
binary was stale (built before the #162 fix). Rebuilding from current
source fixed the issue — broadcasts now fire correctly.

Added two permanent diagnostic log lines:
- [poller] IngestNewFromDB: logs when new transmissions are found
- [broadcast] sending N packets to M clients: logs each broadcast batch

These log lines make it easy to verify the broadcast pipeline is working
and would have caught this stale-deployment issue immediately.

Verified on VM: WS clients receive packets with nested 'packet' field,
all Go tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 14:15:19 -07:00
Kpa-clawbotandCopilot 0775302c95 Add protobuf definitions for all API responses and WebSocket messages
Define .proto files as single source of truth for the API contract.
10 files covering all 40 endpoints from docs/api-spec.md:

  proto/common.proto     - Pagination, Histogram, RoleCounts, SignalStats
  proto/decoded.proto    - DecodedResult, DecodedPayload oneof (10 payload types)
  proto/packet.proto     - Transmission, Observation, GroupedPacket, traces, audio-lab
  proto/node.proto       - Node, BulkHealth, NodeAnalytics, ResolveHops
  proto/observer.proto   - Observer, ObserverAnalytics
  proto/channel.proto    - Channel, ChannelMessage
  proto/analytics.proto  - RF, Topology, Distance, HashSizes, Subpaths
  proto/websocket.proto  - WSMessage, WSPacketData
  proto/stats.proto      - Stats, Health, Perf
  proto/config.proto     - Theme, Regions, ClientConfig, MapConfig, IataCoords

DRY composition:
- Node defined once (node.proto), reused in 8 response types
- Transmission defined once (packet.proto), reused in 5 response types
- Observation defined once (packet.proto), reused in detail + analytics
- DecodedPayload uses oneof for ADVERT/TXT_MSG/GRP_TXT/ACK/REQ/etc.
- Shared types: Histogram, SignalStats, TimeBucket, RoleCounts
- NodeObserverStats/NodeStats shared across bulk-health, node-health, analytics

All files validated with protoc. proto3 syntax, package meshcore.v1,
go_package github.com/meshcore-analyzer/proto/v1, json_name annotations
where proto3 default camelCase differs from API spec.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 14:13:59 -07:00
Kpa-clawbotandCopilot 17a606c0e0 Add Node.js API response fixtures from prod
Capture real responses from all 32 REST endpoints + 1 WebSocket
message from the production MeshCore Analyzer instance. Fixtures
include nodes, packets, observers, channels, analytics, config,
and health endpoints with real IDs substituted.

Stored in proto/testdata/node-fixtures/ for Go port contract testing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 14:10:04 -07:00
Kpa-clawbotandCopilot 35433c9c1a Scribe: Merge protobuf contract decision inbox entry
- Merged copilot-directive-protobuf-contract.md → decisions.md
- Added protobuf API contract as Go Rewrite architecture decision
- Timestamp: 2026-03-27T20:56:00Z
- decisions.md now 360 lines (5 Go Rewrite decisions total)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 14:08:53 -07:00
Kpa-clawbotandCopilot 3ddd9662e6 docs: add formal API contract spec for all REST endpoints and WebSocket messages
Document the exact response shape, query parameters, and type information
for every endpoint in server.js. This is the authoritative contract that
both Node.js and Go backends must conform to.

Covers:
- All 30+ REST endpoints with full JSON response schemas
- WebSocket message envelope and data shapes
- Shared object shapes (Packet, Observation, DecodedHeader, DecodedPath)
- Query parameter documentation with types and defaults
- Null rules, pagination conventions, error response format
- Frontend consumer matrix (which page reads which WS fields)
- Payload type and route type reference tables

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 13:55:42 -07:00
Kpa-clawbotandCopilot 385d2ae578 ci: split pipeline into two independent tracks (Node + Go)
- build-node depends only on node-test
- build-go depends only on go-test
- deploy-node depends only on build-node
- deploy-go depends only on build-go
- publish job waits for both deploy-node and deploy-go to complete
- Badges and deployment summary moved to final publish step

Result: Go staging no longer waits for Node tests to complete.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 13:36:45 -07:00
Kpa-clawbotandCopilot 85047eab08 ci: deploy-go no longer waits for node-test or deploy-node
Go staging now deploys immediately after build completes, in parallel
with Node staging. Both test suites still gate the build job.

Before:
  go-test + node-test → build → deploy-node → deploy-go

After:
  go-test + node-test → build → deploy-node (parallel)
                                 deploy-go  (parallel)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 13:34:22 -07:00
Kpa-clawbotandCopilot 95afaf2f0d fix(go): add nested packet field to WS broadcast, fixes #162
The frontend packets.js filters WS messages with m.data?.packet and
extracts m.data.packet for live rendering. Node's server.js includes
a packet sub-object (packet: fullPacket) in the broadcast data, but
Go's IngestNewFromDB built the data flat without a nested packet field.

This caused the Go staging packets page to never live-update via WS
even though messages were being sent — they were silently filtered out
by packets.js.

Fix: build the packet fields map separately, then create the broadcast
map with both top-level fields (for live.js) and nested packet (for
packets.js). Also fixes the fallback DB-direct poller path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 13:31:37 -07:00
Kpa-clawbotandCopilot 2d17f91639 ci: fix 3 deploy.yml warnings (Node24, Go cache, badge artifacts)
- Add FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 env var for Node.js 20 deprecation
- Add cache-dependency-path for go.sum files in cmd/server and cmd/ingestor
- Add if-no-files-found: ignore to go-badges upload-artifact step

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 13:28:35 -07:00
Kpa-clawbotandCopilot 5539bc9fde ci: restructure deploy.yml into 5 clear jobs with readable step names
Split the monolithic 3-job pipeline (go-build, test, deploy) into 5
focused jobs that each do ONE thing:

  go-test      - Go Build & Test (coverage badges, runs on ubuntu-latest)
  node-test    - Node.js Tests (backend + Playwright E2E, coverage)
  build        - Build Docker Images (Node + Go, badge publishing)
  deploy-node  - Deploy Node Staging (port 81, healthcheck, smoke test)
  deploy-go    - Deploy Go Staging (port 82, healthcheck, smoke test)

Dependency chain: go-test + node-test (parallel) -> build -> deploy-node -> deploy-go

Every step now has a human-readable name describing exactly what it does.
Job names include emoji for visual scanning on GitHub Actions.
All existing functionality preserved - just reorganized for clarity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 13:24:29 -07:00
github-actions 57fdf773f3 ci: update test badges [skip ci] 2026-03-27 20:22:33 +00:00
Kpa-clawbotandCopilot 6dfc9128f9 test: add 7 Playwright E2E tests for recent features (#144)
- Compare results: verify shared/unique cards and tab table (#129)
- Channel hash: find undecrypted GRP_TXT, verify Channel Hash label (#123)
- Nodes WS auto-update: verify onWS/offWS infra and liveDot (#131)
- Version/commit badge: check navStats for version-badge (skips gracefully)
- Perf Node.js vs Go: verify Event Loop on Node, Go Runtime on Go staging
- GO_BASE_URL env var for Go-specific tests (port 82)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 13:14:01 -07:00
Kpa-clawbotandCopilot 6ec00a5aab test(go): fix failing test + add 18 tests, coverage 91.5% → 92.3%
Fix TestStoreGetAnalyticsChannelsNumericHash: corrected observation
transmission_id references (3,4,5 → 4,5,6) and changed assertion to
only check numeric-hash channels (seed data CHAN lacks channelHash).

New tests covering gaps:
- resolvePayloadTypeName unknown type fallback
- Cache hit paths for Topology, HashSizes, Channels
- GetChannelMessages edge cases (empty, offset, default limit)
- filterPackets: empty region, since/until, hash-only fast path,
  observer+type combo, node filter
- GetNodeHashSizeInfo: short hex, bad hex, bad JSON, missing pubKey,
  public_key field, flip-flop inconsistency detection
- handleResolveHops: empty param, empty hop skip, nonexistent prefix
- handleObservers error path (closed DB)
- handleAnalyticsChannels DB fallback (no store)
- GetChannelMessages dedup/repeats
- transmissionsForObserver from-slice filter path
- GetPerfStoreStats advertByObserver count
- handleAudioLabBuckets query error path

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 13:13:31 -07:00
github-actions 00453c843e ci: update test badges [skip ci] 2026-03-27 20:07:54 +00:00
Kpa-clawbotandCopilot d16ac82f6d fix: subscribe MQTT with explicit QoS 0 to prevent puback flag errors (fixes #161)
Upstream brokers publishing QoS 1 cause our mqtt client to send PUBACKs
with invalid flag bits, triggering Mosquitto disconnect storms. Explicitly
requesting QoS 0 avoids PUBACKs entirely.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 13:06:47 -07:00
Kpa-clawbotandCopilot 25ab880fd4 docs: Scribe session finalization log — 28 issues closed, Go rewrite staged, DB merge complete
- Merged decision inbox (empty, no new decisions to file)
- Orchestration log entry summarizing session deliverables
- Agent work: Hicks (6 fixes + Go MQTT ingestor + Go web server), Newt (4 frontend fixes), Bishop (gap coverage + E2E expansion), Kobayashi (root cause + DB plan), Hudson (merge execution + Docker setup), Coordinator (issue triage), Ripley (support onboarding)
- Session impact: 28 issues closed, 7,308→400 nodes, 2.7GB→860MB RSS, 185MB staged DB merged to prod, Go ingestor (25 tests) + server (42 tests) ready, E2E coverage 16→42 tests
- Infrastructure: Staging environment operational (Docker Compose, old problematic DB), CI pipeline self-healing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 12:58:40 -07:00
Kpa-clawbotandCopilot 8f712774ae fix: WS broadcast missing decoded fields + cache analytics endpoints
#156: The Go WebSocket broadcast from IngestNewFromDB was missing the
'decoded' field (with header.payloadTypeName) that live.js needs to
display packet types. Added decoded object with payloadTypeName
resolution, plus observer_id, observer_name, snr, rssi, path_json,
and observation_count fields to match the Node.js broadcast shape.

#157-160: Analytics endpoints (hash-sizes, topology, channels) were
recomputing everything on every request. Added:
- TTL caching (15s) for topology, hash-sizes, and channels endpoints
  (matching the existing RF cache pattern)
- Cached node list + prefix map shared across analytics (30s TTL)
- Lazy-cached parsed path JSON on StoreTx (parse once, read many)
- Cache invalidation on new data ingestion
- Global payloadTypeNames map (avoids per-call allocation)

Fixes #156, fixes #157, fixes #158, fixes #159, fixes #160

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 12:17:26 -07:00
github-actions 59e616668b ci: update test badges [skip ci] 2026-03-27 19:16:27 +00:00
Kpa-clawbotandCopilot 1457795e3e fix: analytics channels + uniqueNodes mismatch
fixes #154: Go analytics channels showed single 'ch?' because
channelHash is a JSON number (from decoder.js) but the Go struct
declared it as string. json.Unmarshal failed on every packet.
Changed to interface{} with proper type conversion. Also fixed
chKey to use hash (not name) for grouping, matching Node.js.

fixes #155: uniqueNodes in topology analytics used hop resolution
count (phantom hops inflated it). Both Node.js and Go now use
db.getStats().totalNodes (7-day active window), matching /api/stats.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 12:07:50 -07:00
Kpa-clawbotandCopilot 2f5404edc3 fix: close last parity gaps in /api/perf and /api/nodes/:pubkey
- db.go: Add freelistMB (PRAGMA freelist_count * page_size) and walPages
  (PRAGMA wal_checkpoint(PASSIVE)) to GetDBSizeStats
- store.go: Add advertByObserver count to GetPerfStoreStats indexes
  (count distinct pubkeys with ADVERT observations)
- db.go: Add getObservationsForTransmissions helper; enrich
  GetRecentTransmissionsForNode results with observations array,
  _parsedPath, and _parsedDecoded
- db_test.go: Add second ADVERT with different hash_size to seed data
  so hash_sizes_seen is populated; enrich decoded_json with full
  ADVERT fields; update count assertions for new seed row

fixes #151, fixes #152

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 11:57:35 -07:00
Kpa-clawbotandCopilot 744702ccf6 feat(perf): show Go Runtime stats instead of Event Loop on Go backend
When engine=go, the perf page now renders Go-specific runtime stats
(goroutines, GC collections, GC pause times, heap breakdown, CPUs)
instead of the misleading Node.js Event Loop metrics. Falls back to
the existing Node UI when engine is not 'go' or goRuntime data is
missing. Includes color-coded GC pause thresholds.

fixes #153

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 11:49:10 -07:00
Kpa-clawbotandCopilot 979b649028 fix(go): add missing packetStore fields to /api/perf (inMemory, maxPackets, etc.)
Frontend reads ps.inMemory.toLocaleString() which crashed because
the Go response was missing inMemory, sqliteOnly, maxPackets, maxMB,
evicted, inserts, queries fields. Added all + atomic counters.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 11:39:16 -07:00
Kpa-clawbotandCopilot 47531e5487 Add golden fixture parity test suite — Go must match Node shapes
- Capture Node.js API response shapes from prod server as golden fixtures
- Store normalized shape schema in cmd/server/testdata/golden/shapes.json
  covering 16 endpoints: stats, nodes, packets (raw + grouped), observers,
  channels, channel_messages, analytics (rf, topology, hash-sizes, distance,
  subpaths), bulk-health, health, perf, and node detail
- Add parity_test.go with recursive shape validator:
  - TestParityShapes: validates Go response keys/types match Node golden
  - TestParityNodeDetail: validates node detail response shape
  - TestParityArraysNotNull: catches nil slices marshaled as null
  - TestParityHealthEngine: verifies Go identifies itself as engine=go
  - TestValidateShapeFunction: unit tests for the validator itself
- Add tools/check-parity.sh for live Node vs Go comparison on VM
- Shape spec handles dynamic-key objects (perObserverReach, perf.endpoints)
- Nullable fields properly marked (observer lat/lon, snr/rssi, hop names)

Current mismatches found (genuine Go bugs):
- /api/perf: packetStore missing 8 fields, sqlite missing 2 fields
- /api/nodes/{pubkey}: missing hash_sizes_seen, observations, _parsedPath,
  _parsedDecoded in node detail response

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 11:37:56 -07:00
Kpa-clawbotandCopilot f97b4c78ad fix: add missing status, uptimeHuman, websocket fields to Go /api/perf
The frontend perf dashboard crashes with toLocaleString error because
the Go /api/perf endpoint was missing fields that Node returns:
- status: "ok"
- uptimeHuman: formatted "Xh Ym" string
- websocket: { clients: N } via hub.ClientCount()

The Go /api/health endpoint already had all three fields.

fixes #150

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