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>
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>