the-invisible-billion-cli 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "the-invisible-billion-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Fully offline, zero-internet, peer-to-peer epidemic routing CLI messenger — The Invisible Billion",
5
5
  "main": "src/daemon/index.js",
6
6
  "bin": {
@@ -32,7 +32,7 @@ async function scanCommand() {
32
32
 
33
33
  function isOnline(epochMs) {
34
34
  if (!epochMs) return false;
35
- return (Date.now() - epochMs) < 90_000; // 90 s (6 heartbeats at 15s)
35
+ return (Date.now() - epochMs) < 45_000; // 45 s 3 missed heartbeats (heartbeat = 15s)
36
36
  }
37
37
 
38
38
  function formatAgo(epochMs) {
@@ -354,16 +354,19 @@ const stopTCP = startTCP({
354
354
 
355
355
  // ── Network change watcher ────────────────────────────────────────────────────
356
356
  const stopNetworkWatcher = startNetworkWatcher((newIP, previousIP) => {
357
- log(`[network] Changed: ${previousIP} → ${newIP}. Re-announcing and sweeping pending...`);
357
+ log(`[network] Changed: ${previousIP} → ${newIP}. Clearing stale peer IPs and re-announcing...`);
358
+
359
+ // Critical: clear all peer IPs from the OLD network.
360
+ // Peers from the previous network are unreachable now.
361
+ // They will re-announce via UDP within 15s if they are also on the new network.
362
+ clearAllPeerIPs();
363
+ log('[network] Stale peer IPs cleared — waiting for fresh UDP announcements on new network.');
364
+
365
+ // Re-announce ourselves on the new network immediately
358
366
  broadcastNow();
359
- startupDeliverySweep(); // re-try all pending to known peers on new network
360
367
 
361
- // Phase 4: Trigger bundle exchange of all pending messages with recently active peers
362
- const peerIPs = getActivePeers().map(p => p.ip).filter(ip => ip);
363
- if (peerIPs.length > 0) {
364
- log(`[epidemic] Network change triggered bundle exchange with ${peerIPs.length} peer(s)`);
365
- forwardMessages(peerIPs);
366
- }
368
+ // Retry delivery sweep any peers that re-appear will be handled by onPeer callback
369
+ startupDeliverySweep();
367
370
  });
368
371
 
369
372
  // ── Phase 3: Startup / network-change delivery sweep ─────────────────────────
@@ -436,16 +439,37 @@ setInterval(() => {
436
439
  }, RETRY_SWEEP_INTERVAL_MS).unref();
437
440
 
438
441
  // ── Periodic stale peer cleanup ───────────────────────────────────────────────
439
- // Every 60 s, remove peers not seen in 3 minutes (12+ missed 15s heartbeats).
440
- const STALE_PEER_INTERVAL_MS = 60_000;
441
- const STALE_PEER_THRESHOLD_MS = 3 * 60 * 1000;
442
+ // Every 20 s, remove peers not seen in 45 s (3 missed 15s heartbeats).
443
+ // This ensures scan reflects reality quickly when a peer disconnects without
444
+ // sending a goodbye packet (e.g. closes laptop, loses WiFi).
445
+ const STALE_PEER_INTERVAL_MS = 20_000;
446
+ const STALE_PEER_THRESHOLD_MS = 45_000; // 3 missed heartbeats
442
447
  setInterval(() => {
443
448
  const removed = removeInactivePeers(STALE_PEER_THRESHOLD_MS);
444
449
  if (removed > 0) {
445
- log(`[cleanup] Removed ${removed} stale peer(s) (not seen in 3 min)`);
450
+ log(`[cleanup] Removed ${removed} stale peer(s) (not seen in 45s)`);
446
451
  }
447
452
  }, STALE_PEER_INTERVAL_MS).unref();
448
453
 
454
+ // ── Periodic epidemic forwarding sweep ────────────────────────────────────────
455
+ // Every 45 s, push ALL pending messages to ALL currently active peers.
456
+ // This catches the case where a carrier was already on the network when a
457
+ // new message arrived and their onPeer event already fired — they wouldn't
458
+ // get the new message unless we periodically push to them.
459
+ const EPIDEMIC_SWEEP_INTERVAL_MS = 45_000;
460
+ setInterval(() => {
461
+ const pending = getAllPending();
462
+ if (pending.length === 0) return;
463
+
464
+ const peerIPs = getActivePeers().map(p => p.ip).filter(ip => ip);
465
+ if (peerIPs.length === 0) return;
466
+
467
+ log(`[epidemic] Periodic sweep: pushing ${pending.length} pending msg(s) to ${peerIPs.length} active peer(s)`);
468
+ forwardMessages(peerIPs).catch(err => {
469
+ log(`[epidemic] Periodic sweep failed: ${err.message}`);
470
+ });
471
+ }, EPIDEMIC_SWEEP_INTERVAL_MS).unref();
472
+
449
473
  // ── IPC Server ────────────────────────────────────────────────────────────────
450
474
  if (process.platform !== 'win32' && fs.existsSync(IPC_SOCKET)) {
451
475
  fs.unlinkSync(IPC_SOCKET);
@@ -500,8 +524,8 @@ function handleCommand(cmd, socket) {
500
524
  break;
501
525
 
502
526
  case 'scan': {
503
- // Return only peers seen within the last 2 minutes (recent heartbeat)
504
- const SCAN_FRESHNESS_MS = 2 * 60 * 1000;
527
+ // Return only peers seen within the last 90 s (consistent with cleanup threshold)
528
+ const SCAN_FRESHNESS_MS = 90_000;
505
529
  const peers = getAllPeers(SCAN_FRESHNESS_MS);
506
530
  sendResponse(socket, { ok: true, peers });
507
531
  break;
package/src/db/index.js CHANGED
@@ -52,12 +52,12 @@ function openDb() {
52
52
  try {
53
53
  _db.exec(`ALTER TABLE peers ADD COLUMN ip TEXT`);
54
54
  } catch (_) {
55
- // column already exists, leave it
56
- //for safety purpose
55
+ // column already exists ignore safely
56
+ }
57
57
 
58
58
  return _db;
59
59
  }
60
- }
60
+
61
61
 
62
62
  /**
63
63
  * Close the database connection.