the-invisible-billion-cli 1.0.0
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/LICENSE +21 -0
- package/README.md +371 -0
- package/bin/ib.js +90 -0
- package/package.json +28 -0
- package/src/cli/commands/file.js +20 -0
- package/src/cli/commands/scan.js +50 -0
- package/src/cli/commands/send.js +113 -0
- package/src/cli/commands/start.js +126 -0
- package/src/cli/commands/status.js +43 -0
- package/src/cli/commands/stop.js +15 -0
- package/src/cli/commands/sync.js +31 -0
- package/src/cli/ipc.js +127 -0
- package/src/crypto/encrypt.js +100 -0
- package/src/crypto/keys.js +95 -0
- package/src/daemon/epidemic.js +103 -0
- package/src/daemon/index.js +683 -0
- package/src/daemon/network.js +84 -0
- package/src/daemon/tcp.js +200 -0
- package/src/daemon/udp.js +282 -0
- package/src/db/cleanup.js +39 -0
- package/src/db/index.js +74 -0
- package/src/db/messages.js +213 -0
- package/src/identity/index.js +92 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const { encrypt } = require('../crypto/encrypt');
|
|
5
|
+
const { getAllPending, insertMessage, getPeerPublicKey } = require('../db/messages');
|
|
6
|
+
const { sendMessage } = require('./tcp');
|
|
7
|
+
const { sendDirectMessage } = require('./udp');
|
|
8
|
+
|
|
9
|
+
function log(msg) {
|
|
10
|
+
console.warn(msg);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Forward all pending messages to a list of peer IPs.
|
|
15
|
+
* Tries TCP first, falls back to UDP if TCP fails (firewall blocking).
|
|
16
|
+
* @param {string[]} peerIPs - List of peer IP addresses
|
|
17
|
+
*/
|
|
18
|
+
function forwardMessages(peerIPs) {
|
|
19
|
+
if (!peerIPs || peerIPs.length === 0) return Promise.resolve();
|
|
20
|
+
|
|
21
|
+
// getAllPending already filters by hop_count < 20 and TTL
|
|
22
|
+
const pending = getAllPending();
|
|
23
|
+
if (pending.length === 0) return Promise.resolve();
|
|
24
|
+
|
|
25
|
+
log(`[epidemic] Forwarding ${pending.length} message(s) to ${peerIPs.length} peer(s)`);
|
|
26
|
+
|
|
27
|
+
const promises = [];
|
|
28
|
+
for (const ip of peerIPs) {
|
|
29
|
+
for (const msg of pending) {
|
|
30
|
+
const relayPayload = {
|
|
31
|
+
id: msg.id,
|
|
32
|
+
from_identity: msg.from_identity,
|
|
33
|
+
destination: msg.destination,
|
|
34
|
+
payload: msg.payload, // Opaque encrypted blob
|
|
35
|
+
hop_count: msg.hop_count, // Receiver will increment this
|
|
36
|
+
ttl: msg.ttl,
|
|
37
|
+
created_at: msg.created_at,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Try TCP first, fallback to UDP if TCP fails
|
|
41
|
+
const relayPromise = sendMessage(ip, 'relay', relayPayload)
|
|
42
|
+
.catch(tcpErr => {
|
|
43
|
+
// TCP failed — try UDP fallback (works through firewalls)
|
|
44
|
+
return sendDirectMessage(ip, 'relay', relayPayload)
|
|
45
|
+
.catch(udpErr => {
|
|
46
|
+
log(`[epidemic] Failed to relay ${msg.id} to ${ip} (TCP+UDP both failed)`);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
promises.push(relayPromise);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return Promise.all(promises).then(() => {
|
|
55
|
+
log(`[epidemic] Bundle exchange complete`);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Process a received bundle of messages from a peer.
|
|
61
|
+
* (Currently not used as TCP/UDP handles frames immediately)
|
|
62
|
+
*/
|
|
63
|
+
function receiveBundle(bundle) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
// Send an ACK for a delivered message back through the epidemic network.
|
|
69
|
+
function sendAck(messageId, originalSender, myIdentity) {
|
|
70
|
+
const ackId = crypto.randomUUID();
|
|
71
|
+
const ackPayload = JSON.stringify({ __type: 'ack', ackMessageId: messageId });
|
|
72
|
+
|
|
73
|
+
let encryptedPayload;
|
|
74
|
+
const peerKey = getPeerPublicKey(originalSender);
|
|
75
|
+
if (peerKey) {
|
|
76
|
+
encryptedPayload = JSON.stringify(encrypt(ackPayload, peerKey));
|
|
77
|
+
} else {
|
|
78
|
+
encryptedPayload = JSON.stringify({
|
|
79
|
+
__pending_encryption: true,
|
|
80
|
+
__phase1_plaintext: ackPayload,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
log(`[epidemic] Generating ACK ${ackId} for message ${messageId} -> ${originalSender}`);
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
insertMessage({
|
|
88
|
+
id: ackId,
|
|
89
|
+
from_identity: myIdentity,
|
|
90
|
+
destination: originalSender,
|
|
91
|
+
payload: encryptedPayload,
|
|
92
|
+
ttl: Date.now() + 7 * 24 * 60 * 60 * 1000,
|
|
93
|
+
status: 'undelivered', // Enqueue for epidemic spread
|
|
94
|
+
hop_count: 0,
|
|
95
|
+
});
|
|
96
|
+
} catch (err) {
|
|
97
|
+
log(`[epidemic] Failed to store ACK msg: ${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return Promise.resolve(); //just return a resolved promise since the actual sending will happen during the next epidemic forwarding cycle
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = { forwardMessages, receiveBundle, sendAck };
|