@ruvector/edge-net 0.2.0 → 0.3.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/p2p.js ADDED
@@ -0,0 +1,577 @@
1
+ /**
2
+ * @ruvector/edge-net P2P Integration
3
+ *
4
+ * Unified P2P networking layer that integrates:
5
+ * - WebRTC for direct peer connections
6
+ * - DHT for decentralized peer discovery
7
+ * - Signaling for connection establishment
8
+ * - Sync for ledger synchronization
9
+ *
10
+ * @module @ruvector/edge-net/p2p
11
+ */
12
+
13
+ import { EventEmitter } from 'events';
14
+ import { randomBytes } from 'crypto';
15
+
16
+ // Import P2P components
17
+ import { WebRTCPeerManager, WEBRTC_CONFIG } from './webrtc.js';
18
+ import { DHTNode, createDHTNode } from './dht.js';
19
+ import { SignalingClient } from './signaling.js';
20
+ import { SyncManager } from './sync.js';
21
+ import { QDAG } from './qdag.js';
22
+ import { Ledger } from './ledger.js';
23
+
24
+ // ============================================
25
+ // P2P NETWORK CONFIGURATION
26
+ // ============================================
27
+
28
+ export const P2P_CONFIG = {
29
+ // Auto-start components
30
+ autoStart: {
31
+ signaling: true,
32
+ webrtc: true,
33
+ dht: true,
34
+ sync: true,
35
+ },
36
+ // Connection settings
37
+ maxPeers: 50,
38
+ minPeers: 3,
39
+ // Reconnection
40
+ reconnectInterval: 5000,
41
+ maxReconnectAttempts: 10,
42
+ // DHT settings
43
+ dhtAnnounceInterval: 60000,
44
+ // Sync settings
45
+ syncInterval: 30000,
46
+ };
47
+
48
+ // ============================================
49
+ // P2P NETWORK NODE
50
+ // ============================================
51
+
52
+ /**
53
+ * Unified P2P Network Node
54
+ *
55
+ * Connects all P2P components into a single interface:
56
+ * - Automatic peer discovery via DHT
57
+ * - WebRTC data channels for direct messaging
58
+ * - QDAG for distributed consensus
59
+ * - Ledger sync across devices
60
+ */
61
+ export class P2PNetwork extends EventEmitter {
62
+ constructor(identity, options = {}) {
63
+ super();
64
+ this.identity = identity;
65
+ this.options = { ...P2P_CONFIG, ...options };
66
+
67
+ // Node ID
68
+ this.nodeId = identity?.piKey || identity?.nodeId || `node-${randomBytes(8).toString('hex')}`;
69
+
70
+ // Components (initialized on start)
71
+ this.signaling = null;
72
+ this.webrtc = null;
73
+ this.dht = null;
74
+ this.syncManager = null;
75
+ this.qdag = null;
76
+ this.ledger = null;
77
+
78
+ // State
79
+ this.state = 'stopped';
80
+ this.peers = new Map();
81
+ this.stats = {
82
+ startedAt: null,
83
+ peersConnected: 0,
84
+ messagesReceived: 0,
85
+ messagesSent: 0,
86
+ bytesTransferred: 0,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Start the P2P network
92
+ */
93
+ async start() {
94
+ if (this.state === 'running') return this;
95
+
96
+ console.log('\n🌐 Starting P2P Network...');
97
+ console.log(` Node ID: ${this.nodeId.slice(0, 16)}...`);
98
+
99
+ this.state = 'starting';
100
+
101
+ try {
102
+ // Initialize QDAG
103
+ this.qdag = new QDAG({ nodeId: this.nodeId });
104
+ console.log(' āœ… QDAG initialized');
105
+
106
+ // Initialize Ledger
107
+ this.ledger = new Ledger({ nodeId: this.nodeId });
108
+ console.log(' āœ… Ledger initialized');
109
+
110
+ // Try signaling server first
111
+ if (this.options.autoStart.signaling) {
112
+ await this.startSignaling();
113
+ }
114
+
115
+ // Initialize WebRTC
116
+ if (this.options.autoStart.webrtc) {
117
+ await this.startWebRTC();
118
+ }
119
+
120
+ // Initialize DHT for peer discovery
121
+ if (this.options.autoStart.dht) {
122
+ await this.startDHT();
123
+ }
124
+
125
+ // Initialize sync
126
+ if (this.options.autoStart.sync && this.identity) {
127
+ await this.startSync();
128
+ }
129
+
130
+ // Wire up event handlers
131
+ this.setupEventHandlers();
132
+
133
+ // Announce presence
134
+ await this.announce();
135
+
136
+ this.state = 'running';
137
+ this.stats.startedAt = Date.now();
138
+
139
+ console.log('\nāœ… P2P Network running');
140
+ console.log(` Mode: ${this.getMode()}`);
141
+ console.log(` Peers: ${this.peers.size}`);
142
+
143
+ this.emit('started', { nodeId: this.nodeId, mode: this.getMode() });
144
+
145
+ return this;
146
+
147
+ } catch (error) {
148
+ this.state = 'error';
149
+ console.error('āŒ P2P Network start failed:', error.message);
150
+ throw error;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Stop the P2P network
156
+ */
157
+ async stop() {
158
+ console.log('\nšŸ›‘ Stopping P2P Network...');
159
+
160
+ this.state = 'stopping';
161
+
162
+ if (this.syncManager) this.syncManager.stop();
163
+ if (this.dht) this.dht.stop();
164
+ if (this.webrtc) this.webrtc.close();
165
+ if (this.signaling) this.signaling.disconnect();
166
+
167
+ this.peers.clear();
168
+ this.state = 'stopped';
169
+
170
+ this.emit('stopped');
171
+ }
172
+
173
+ /**
174
+ * Start signaling client
175
+ */
176
+ async startSignaling() {
177
+ try {
178
+ this.signaling = new SignalingClient({
179
+ serverUrl: this.options.signalingUrl || WEBRTC_CONFIG.signalingServers[0],
180
+ peerId: this.nodeId,
181
+ });
182
+
183
+ const connected = await this.signaling.connect();
184
+ if (connected) {
185
+ console.log(' āœ… Signaling connected');
186
+ } else {
187
+ console.log(' āš ļø Signaling unavailable (will use DHT)');
188
+ }
189
+ } catch (err) {
190
+ console.log(' āš ļø Signaling failed:', err.message);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Start WebRTC peer manager
196
+ */
197
+ async startWebRTC() {
198
+ try {
199
+ this.webrtc = new WebRTCPeerManager({
200
+ piKey: this.nodeId,
201
+ siteId: this.identity?.siteId || 'edge-net',
202
+ }, this.options.webrtc || {});
203
+
204
+ await this.webrtc.initialize();
205
+ console.log(` āœ… WebRTC initialized (${this.webrtc.mode} mode)`);
206
+ } catch (err) {
207
+ console.log(' āš ļø WebRTC failed:', err.message);
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Start DHT for peer discovery
213
+ */
214
+ async startDHT() {
215
+ try {
216
+ if (this.webrtc) {
217
+ this.dht = await createDHTNode(this.webrtc, {
218
+ id: this.nodeId,
219
+ });
220
+ } else {
221
+ this.dht = new DHTNode({ id: this.nodeId });
222
+ await this.dht.start();
223
+ }
224
+ console.log(' āœ… DHT initialized');
225
+ } catch (err) {
226
+ console.log(' āš ļø DHT failed:', err.message);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Start sync manager
232
+ */
233
+ async startSync() {
234
+ try {
235
+ this.syncManager = new SyncManager(this.identity, this.ledger, {
236
+ enableP2P: !!this.webrtc,
237
+ enableFirestore: false, // Use P2P only for now
238
+ });
239
+
240
+ await this.syncManager.start();
241
+ console.log(' āœ… Sync initialized');
242
+ } catch (err) {
243
+ console.log(' āš ļø Sync failed:', err.message);
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Setup event handlers between components
249
+ */
250
+ setupEventHandlers() {
251
+ // WebRTC events
252
+ if (this.webrtc) {
253
+ this.webrtc.on('peer-connected', (peerId) => {
254
+ this.handlePeerConnected(peerId);
255
+ });
256
+
257
+ this.webrtc.on('peer-disconnected', (peerId) => {
258
+ this.handlePeerDisconnected(peerId);
259
+ });
260
+
261
+ this.webrtc.on('message', ({ from, message }) => {
262
+ this.handleMessage(from, message);
263
+ });
264
+ }
265
+
266
+ // Signaling events
267
+ if (this.signaling) {
268
+ this.signaling.on('peer-joined', ({ peerId }) => {
269
+ this.connectToPeer(peerId);
270
+ });
271
+
272
+ this.signaling.on('offer', async ({ from, offer }) => {
273
+ if (this.webrtc) {
274
+ await this.webrtc.handleOffer({ from, offer });
275
+ }
276
+ });
277
+
278
+ this.signaling.on('answer', async ({ from, answer }) => {
279
+ if (this.webrtc) {
280
+ await this.webrtc.handleAnswer({ from, answer });
281
+ }
282
+ });
283
+
284
+ this.signaling.on('ice-candidate', async ({ from, candidate }) => {
285
+ if (this.webrtc) {
286
+ await this.webrtc.handleIceCandidate({ from, candidate });
287
+ }
288
+ });
289
+ }
290
+
291
+ // DHT events
292
+ if (this.dht) {
293
+ this.dht.on('peer-added', (peer) => {
294
+ this.connectToPeer(peer.id);
295
+ });
296
+ }
297
+
298
+ // Sync events
299
+ if (this.syncManager) {
300
+ this.syncManager.on('synced', (data) => {
301
+ this.emit('synced', data);
302
+ });
303
+ }
304
+
305
+ // QDAG events
306
+ if (this.qdag) {
307
+ this.qdag.on('transaction', (tx) => {
308
+ this.broadcastTransaction(tx);
309
+ });
310
+
311
+ this.qdag.on('confirmed', (tx) => {
312
+ this.emit('transaction-confirmed', tx);
313
+ });
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Announce presence to the network
319
+ */
320
+ async announce() {
321
+ // Announce via signaling
322
+ if (this.signaling?.isConnected) {
323
+ this.signaling.send({
324
+ type: 'announce',
325
+ piKey: this.nodeId,
326
+ siteId: this.identity?.siteId,
327
+ capabilities: ['compute', 'storage', 'verify'],
328
+ });
329
+ }
330
+
331
+ // Announce via DHT
332
+ if (this.dht) {
333
+ await this.dht.announce('edge-net');
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Connect to a peer
339
+ */
340
+ async connectToPeer(peerId) {
341
+ if (peerId === this.nodeId) return;
342
+ if (this.peers.has(peerId)) return;
343
+
344
+ try {
345
+ if (this.webrtc) {
346
+ await this.webrtc.connectToPeer(peerId);
347
+ }
348
+ } catch (err) {
349
+ console.warn(`[P2P] Failed to connect to ${peerId.slice(0, 8)}:`, err.message);
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Handle peer connected
355
+ */
356
+ handlePeerConnected(peerId) {
357
+ this.peers.set(peerId, {
358
+ id: peerId,
359
+ connectedAt: Date.now(),
360
+ lastSeen: Date.now(),
361
+ });
362
+
363
+ this.stats.peersConnected++;
364
+
365
+ // Register with sync
366
+ if (this.syncManager && this.webrtc) {
367
+ const peer = this.webrtc.peers.get(peerId);
368
+ if (peer?.dataChannel) {
369
+ this.syncManager.registerPeer(peerId, peer.dataChannel);
370
+ }
371
+ }
372
+
373
+ this.emit('peer-connected', { peerId });
374
+ console.log(` šŸ”— Connected to peer: ${peerId.slice(0, 8)}... (${this.peers.size} total)`);
375
+ }
376
+
377
+ /**
378
+ * Handle peer disconnected
379
+ */
380
+ handlePeerDisconnected(peerId) {
381
+ this.peers.delete(peerId);
382
+ this.emit('peer-disconnected', { peerId });
383
+ }
384
+
385
+ /**
386
+ * Handle incoming message
387
+ */
388
+ handleMessage(from, message) {
389
+ this.stats.messagesReceived++;
390
+
391
+ // Route by message type
392
+ switch (message.type) {
393
+ case 'qdag_transaction':
394
+ this.handleQDAGTransaction(from, message);
395
+ break;
396
+
397
+ case 'qdag_sync_request':
398
+ this.handleQDAGSyncRequest(from, message);
399
+ break;
400
+
401
+ case 'qdag_sync_response':
402
+ this.handleQDAGSyncResponse(from, message);
403
+ break;
404
+
405
+ default:
406
+ this.emit('message', { from, message });
407
+ }
408
+ }
409
+
410
+ /**
411
+ * Handle QDAG transaction
412
+ */
413
+ handleQDAGTransaction(from, message) {
414
+ if (message.transaction && this.qdag) {
415
+ try {
416
+ this.qdag.addTransaction(message.transaction);
417
+ } catch (err) {
418
+ // Duplicate or invalid transaction
419
+ }
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Handle QDAG sync request
425
+ */
426
+ handleQDAGSyncRequest(from, message) {
427
+ if (this.qdag && this.webrtc) {
428
+ const transactions = this.qdag.export(message.since || 0);
429
+ this.webrtc.sendToPeer(from, {
430
+ type: 'qdag_sync_response',
431
+ transactions: transactions.transactions,
432
+ });
433
+ }
434
+ }
435
+
436
+ /**
437
+ * Handle QDAG sync response
438
+ */
439
+ handleQDAGSyncResponse(from, message) {
440
+ if (message.transactions && this.qdag) {
441
+ this.qdag.import({ transactions: message.transactions });
442
+ }
443
+ }
444
+
445
+ /**
446
+ * Broadcast a transaction to all peers
447
+ */
448
+ broadcastTransaction(tx) {
449
+ if (this.webrtc) {
450
+ this.webrtc.broadcast({
451
+ type: 'qdag_transaction',
452
+ transaction: tx.toJSON(),
453
+ });
454
+ this.stats.messagesSent++;
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Send message to a specific peer
460
+ */
461
+ sendToPeer(peerId, message) {
462
+ if (this.webrtc) {
463
+ const sent = this.webrtc.sendToPeer(peerId, message);
464
+ if (sent) this.stats.messagesSent++;
465
+ return sent;
466
+ }
467
+ return false;
468
+ }
469
+
470
+ /**
471
+ * Broadcast message to all peers
472
+ */
473
+ broadcast(message) {
474
+ if (this.webrtc) {
475
+ const sent = this.webrtc.broadcast(message);
476
+ this.stats.messagesSent += sent;
477
+ return sent;
478
+ }
479
+ return 0;
480
+ }
481
+
482
+ /**
483
+ * Submit a task to the network
484
+ */
485
+ async submitTask(task) {
486
+ if (!this.qdag) throw new Error('QDAG not initialized');
487
+
488
+ const tx = this.qdag.createTransaction('task', {
489
+ taskId: task.id || randomBytes(8).toString('hex'),
490
+ type: task.type,
491
+ data: task.data,
492
+ reward: task.reward || 0,
493
+ priority: task.priority || 'medium',
494
+ }, {
495
+ issuer: this.nodeId,
496
+ });
497
+
498
+ return tx;
499
+ }
500
+
501
+ /**
502
+ * Credit the ledger
503
+ */
504
+ credit(amount, reason) {
505
+ if (!this.ledger) throw new Error('Ledger not initialized');
506
+ this.ledger.credit(amount, reason);
507
+
508
+ // Trigger sync
509
+ if (this.syncManager) {
510
+ this.syncManager.sync();
511
+ }
512
+ }
513
+
514
+ /**
515
+ * Get current balance
516
+ */
517
+ getBalance() {
518
+ return this.ledger?.getBalance() || 0;
519
+ }
520
+
521
+ /**
522
+ * Get connection mode
523
+ */
524
+ getMode() {
525
+ if (this.webrtc?.mode === 'webrtc' && this.signaling?.isConnected) {
526
+ return 'full-p2p';
527
+ }
528
+ if (this.webrtc?.mode === 'webrtc') {
529
+ return 'webrtc-dht';
530
+ }
531
+ if (this.dht) {
532
+ return 'dht-only';
533
+ }
534
+ return 'local';
535
+ }
536
+
537
+ /**
538
+ * Get network statistics
539
+ */
540
+ getStats() {
541
+ return {
542
+ ...this.stats,
543
+ nodeId: this.nodeId,
544
+ state: this.state,
545
+ mode: this.getMode(),
546
+ peers: this.peers.size,
547
+ signalingConnected: this.signaling?.isConnected || false,
548
+ webrtcMode: this.webrtc?.mode || 'none',
549
+ dhtPeers: this.dht?.getPeers().length || 0,
550
+ qdagTransactions: this.qdag?.transactions.size || 0,
551
+ ledgerBalance: this.getBalance(),
552
+ uptime: this.stats.startedAt ? Date.now() - this.stats.startedAt : 0,
553
+ };
554
+ }
555
+
556
+ /**
557
+ * Get peer list
558
+ */
559
+ getPeers() {
560
+ return Array.from(this.peers.values());
561
+ }
562
+ }
563
+
564
+ /**
565
+ * Create and start a P2P network node
566
+ */
567
+ export async function createP2PNetwork(identity, options = {}) {
568
+ const network = new P2PNetwork(identity, options);
569
+ await network.start();
570
+ return network;
571
+ }
572
+
573
+ // ============================================
574
+ // EXPORTS
575
+ // ============================================
576
+
577
+ export default P2PNetwork;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruvector/edge-net",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Distributed compute intelligence network with AI agents and workers - contribute browser compute, spawn distributed AI agents, earn credits. Features Time Crystal coordination, Neural DAG attention, P2P swarm intelligence, ONNX inference, WebRTC signaling, CRDT ledger, and multi-agent workflows.",
6
6
  "main": "ruvector_edge_net.js",
@@ -9,7 +9,8 @@
9
9
  "bin": {
10
10
  "edge-net": "./cli.js",
11
11
  "ruvector-edge": "./cli.js",
12
- "edge-net-join": "./join.js"
12
+ "edge-net-join": "./join.js",
13
+ "edge-net-genesis": "./genesis.js"
13
14
  },
14
15
  "keywords": [
15
16
  "wasm",
@@ -43,7 +44,12 @@
43
44
  "signaling",
44
45
  "scheduler",
45
46
  "monitoring",
46
- "qdag"
47
+ "qdag",
48
+ "dht",
49
+ "kademlia",
50
+ "genesis-node",
51
+ "turn-server",
52
+ "nat-traversal"
47
53
  ],
48
54
  "author": "RuVector Team <team@ruvector.dev>",
49
55
  "license": "MIT",
@@ -79,6 +85,9 @@
79
85
  "ledger.js",
80
86
  "scheduler.js",
81
87
  "monitor.js",
88
+ "dht.js",
89
+ "genesis.js",
90
+ "p2p.js",
82
91
  "README.md",
83
92
  "LICENSE"
84
93
  ],
@@ -128,6 +137,15 @@
128
137
  },
129
138
  "./monitor": {
130
139
  "import": "./monitor.js"
140
+ },
141
+ "./dht": {
142
+ "import": "./dht.js"
143
+ },
144
+ "./genesis": {
145
+ "import": "./genesis.js"
146
+ },
147
+ "./p2p": {
148
+ "import": "./p2p.js"
131
149
  }
132
150
  },
133
151
  "sideEffects": [
@@ -147,6 +165,9 @@
147
165
  "peers": "node join.js --peers",
148
166
  "history": "node join.js --history",
149
167
  "signaling": "node -e \"import('./signaling.js').then(m => new m.SignalingServer().start())\"",
168
+ "genesis": "node genesis.js",
169
+ "genesis:start": "node genesis.js --port 8787",
170
+ "p2p": "node -e \"import('./p2p.js').then(m => m.createP2PNetwork({ nodeId: 'test' }))\"",
150
171
  "monitor": "node -e \"import('./monitor.js').then(m => { const mon = new m.Monitor(); mon.start(); setInterval(() => console.log(JSON.stringify(mon.generateReport(), null, 2)), 5000); })\""
151
172
  },
152
173
  "dependencies": {