Commit Graph

20 Commits

Author SHA1 Message Date
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
Kpa-clawbot
ac7bd13ae8 fix: filter totalNodes to active nodes (7-day window), fixes #133
The /api/stats endpoint returned totalNodes from SELECT COUNT(*) FROM nodes,
which counts every node ever seen. On long-running instances this climbs to
6800+ for a ~200-400 node mesh.

Changes:
- totalNodes now counts only nodes with last_seen within the last 7 days
- Added totalNodesAllTime field for the full historical count
- Role counts (repeaters, rooms, etc.) also filtered to 7-day window
- Added countActiveNodes and countActiveNodesByRole prepared statements
- Added 6 tests verifying active vs all-time node counting

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 01:56:38 -07:00
you
e340949253 feat: optimize observations table — 478MB → 141MB
Schema v3 migration:
- Replace observer_id TEXT (64-char hex) with observer_idx INTEGER FK
- Drop redundant hash, observer_name, created_at columns
- Store timestamp as epoch integer instead of ISO string
- In-memory dedup Set replaces expensive unique index lookups
- Auto-migration on startup with timestamped backup (never overwrites)
- Detects already-migrated DBs via pragma user_version + column inspection

Fixes:
- disambiguateHops: restore 'known' field dropped during refactor (fba5649)
- Skip MQTT connections when NODE_ENV=test
- e2e test: encodeURIComponent for # channel hashes in URLs
- VACUUM + TRUNCATE checkpoint after migration (not just VACUUM)
- Daily TRUNCATE checkpoint at 2:00 AM UTC to reclaim WAL space

Observability:
- SQLite stats in /api/perf (DB size, WAL size, freelist, row counts, busy pages)
- Rendered in perf dashboard with color-coded thresholds

Tests: 839 pass (89 db + 30 migration + 70 helpers + 200 routes + 34 packet-store + 52 decoder + 255 decoder-spec + 62 filter + 47 e2e)
2026-03-25 22:33:39 +00:00
you
629606bbdd feat: optimize observations table schema (v3 migration)
- Replace observer_id TEXT (64-char hex) + observer_name TEXT with observer_idx INTEGER (FK to observers rowid)
- Remove redundant hash TEXT and created_at TEXT columns from observations
- Store timestamp as INTEGER epoch seconds instead of ISO text string
- Auto-migrate old schema on startup: backup DB, migrate data, rebuild indexes, VACUUM
- Migration is safe: backup first, abort on failure, schema_version marker prevents re-runs
- Backward-compatible packets_v view: JOINs observers table, converts epoch→ISO for consumers
- In-memory observer_id→rowid Map for fast lookups during ingestion
- In-memory dedup Set with 5-min TTL to prevent duplicate INSERT attempts
- packet-store.js: detect v3 schema and use appropriate JOIN query
- Tests: 29 migration tests (old→new, idempotency, backup failure, ingestion, dedup)
- Tests: 19 new v3 schema tests in test-db.js (columns, types, view compat, ingestion)

Expected savings on 947K-row prod DB:
- observer_id: 61 bytes → 4 bytes per row (57 bytes saved)
- observer_name: ~15 bytes → 0 (resolved via JOIN)
- hash: 16 bytes → 0 (redundant with transmission_id)
- timestamp: 25 bytes → 4 bytes (21 bytes saved)
- created_at: 25 bytes → 0 (redundant)
- Dedup index: much smaller (integers vs text)
- Estimated ~118 bytes saved per row = ~112MB total + massive index savings
2026-03-25 19:13:22 +00:00
you
63f2f7c995 refactor: unify live page packet rendering into renderPacketTree()
Major refactor of live.js data flow:

- Replaced animatePacket() and animateRealisticPropagation() with
  single renderPacketTree(packets, isReplay) function
- All paths use the same function: WS arrival, VCR replay, DB load,
  feed card replay button
- VCR fetches use expand=observations to get full observation data
- expandToBufferEntries() extracts per-observer paths from observations
- startReplay() pre-aggregates VCR buffer by hash before playback
- Feed dedup accumulates observation packets for full tree replay
- Longest path shown in feed (scans all observations, not just first)
- Replay button uses full observation set for starburst animation

Server changes:
- WS broadcast includes path_json per observation
- packet-store insert() uses longest path for display (was earliest)

DB changes:
- Removed seed() function and synthetic test data

Not pushed to prod — local testing only.
2026-03-25 06:02:48 +00:00
you
81751fd3af Drop legacy packets + paths tables on startup, remove dead code
Migration runs automatically on next startup — drops paths first (FK to
packets), then packets. Removes insertPacket(), insertPath(), all
prepared statements and references to both tables. Server-side type/
observer filtering also removed (client does it in-memory).

Saves ~2M rows (paths) + full packets table worth of disk.
2026-03-22 00:45:27 +00:00
you
cb8e20ae7e fix: deduplicate observations with NULL path_json
The UNIQUE index on (hash, observer_id, path_json) didn't prevent
duplicates when path_json was NULL because SQLite treats NULL != NULL
for uniqueness. Fixed by:

1. Using COALESCE(path_json, '') in the UNIQUE index expression
2. Adding migration to clean up existing duplicate rows
3. Adding NULL-safe dedup checks in PacketStore load and insert paths
2026-03-21 22:06:43 +00:00
you
ec4ebf2ede fix: re-export insertPacket from db.js (packet-store.js needs it)
Migration subagent removed it from exports but packet-store.js
calls dbModule.insertPacket() on every MQTT ingestion.
2026-03-21 20:46:15 +00:00
you
3a1b0b544b refactor: migrate all SQL queries from packets table to packets_v view (transmissions+observations)
- Create packets_v SQL view joining transmissions+observations to match old packets schema
- Replace all SELECT FROM packets with packets_v in db.js, packet-store.js, server.js
- Update countPackets/countRecentPackets to query observations directly
- Update seed() to use insertTransmission instead of insertPacket
- Remove insertPacket from exports (no longer called)
- Keep packets table schema intact (not dropped yet, pending testing)
2026-03-21 20:38:58 +00:00
you
016c4091db perf: disable SQLite auto-checkpoint, use manual PASSIVE checkpoint
SQLite WAL auto-checkpoint (every 1000 pages/4MB) was causing 200ms+
event loop spikes on a 381MB database. This is synchronous I/O that
blocks the Node.js event loop unpredictably.

Fix: disable auto-checkpoint, run PASSIVE (non-blocking) checkpoint
every 5 minutes. PASSIVE won't stall readers or writers — it only
checkpoints pages that aren't currently in use.
2026-03-21 20:19:22 +00:00
you
27f4af3f3b fix: validate ADVERT data to prevent corrupted node entries
Fixes Kpa-clawbot/meshcore-analyzer#112
2026-03-21 05:34:57 +00:00
you
6e0cf0fc3e fix: dedup observations - UNIQUE(hash,observer_id,path_json) + INSERT OR IGNORE
~26% of observations were duplicates from multi-broker MQTT ingestion.
Added UNIQUE index to prevent future dupes, INSERT OR IGNORE to skip
silently, and in-memory dedup check in packet-store.
2026-03-21 02:31:51 +00:00
you
85c356448e fix: switch all user-facing URLs to hash-based for stability across restarts
After dedup migration, packet IDs from the legacy 'packets' table differ
from transmission IDs in the 'transmissions' table. URLs using numeric IDs
became invalid after restart when _loadNormalized() assigned different IDs.

Changes:
- All packet URLs now use 16-char hex hashes instead of numeric IDs
  (#/packets/HASH instead of #/packet/ID)
- selectPacket() accepts hash parameter, uses hash-based URLs
- Copy Link generates hash-based URLs
- Search results link to hash-based URLs
- /api/packets/:id endpoint accepts both numeric IDs and 16-char hashes
- insert() now calls insertTransmission() to get stable transmission IDs
- Added db.getTransmission() for direct transmission table lookup
- Removed redundant byTransmission map (identical to byHash)
- All byTransmission references replaced with byHash
2026-03-21 00:18:11 +00:00
you
aa35164252 M4: API response changes for dedup-normalize
- GET /api/packets: returns transmissions with observation_count, strip
  observations[] by default (use ?expand=observations to include)
- GET /api/packets/🆔 includes observation_count and observations[]
- GET /api/nodes/:pubkey/health: stats.totalTransmissions + totalObservations
  (totalPackets kept for backward compat)
- GET /api/nodes/bulk-health: same transmission/observation split
- WebSocket broadcast: includes observation_count
- db.js getStats(): adds totalTransmissions count
- All backward-compatible: old field names preserved alongside new ones
2026-03-20 20:49:34 +00:00
you
baa60cac0f M2: Dual-write ingest to transmissions/observations tables
- Add transmissions and observations schema to db.js init
- Add insertTransmission() function: upsert transmission by hash,
  always insert observation row
- All 6 pktStore.insert() call sites in server.js now also call
  db.insertTransmission() with try/catch (non-fatal on error)
- packet-store.js: add byTransmission Map index (hash → transmission
  with observations array) for future M3 query migration
- Existing insertPacket() and all read paths unchanged
2026-03-20 20:29:03 +00:00
you
8659cda7b7 fix: remove PII from seed data - no real names/coords 2026-03-20 07:30:48 +00:00
you
2713d501b4 feat: MQTT topic arrays, IATA filtering, observer status parsing
- mqttSources[].topics is now an array of topic patterns
- mqttSources[].iataFilter optionally restricts to specific regions
- meshcore/<region>/<id>/status topic parsed for observer metadata:
  name, model, firmware, client_version, radio, battery, uptime, noise_floor
- New observer columns with auto-migration for existing DBs
- Status updates don't inflate packet_count (separate updateObserverStatus)
2026-03-20 07:29:01 +00:00
you
21b1cbc332 Add per-node analytics page with charts, stats, and heatmap
- New route: #/nodes/:pubkey/analytics with Chart.js v4 visualizations
- Activity timeline (bar), SNR trend (line), packet type breakdown (doughnut)
- Observer coverage (horizontal bar), hop distribution (bar)
- Uptime heatmap (7x24 CSS grid, GitHub-style)
- Peer interactions table with links to node details
- Stat cards: availability, signal grade, packets/day, relay %, silence
- Time range selector: 24h / 7d / 30d / All
- Server: GET /api/nodes/:pubkey/analytics with full aggregation in SQLite
- Analytics button added to both sidebar and full-screen node views
2026-03-19 22:31:09 +00:00
you
d41477d1d8 feat: richer node detail — status badge, avg SNR/hops, observer breakdown table, totalPackets 2026-03-19 22:17:00 +00:00
you
46349172f6 Initial commit: MeshCore Analyzer
Bay Area MeshCore mesh network analyzer with:
- Live packet visualization with map, contrail animations, shockwave pulses
- VCR controls: pause/play/rewind/scrub timeline with speed control
- Packet browser with grouped view, detail panel, byte breakdown
- Channel message decryption (hashtag-derived PSKs)
- Node directory with health cards, favorites, search
- Analytics dashboard with network insights
- Observer management and BLE/companion bridge support
- Trace route visualization
- Dark theme, responsive design, accessibility
- SQLite storage, WebSocket live feed, REST API
2026-03-18 19:34:05 +00:00