| … | … | @@ -0,0 +1,9 @@ |
| 1 | # Dependencies (also matches symlinks to a sibling node_modules) | |
| 2 | node_modules | |
| 3 | ||
| 4 | # Editor and OS cruft | |
| 5 | .DS_Store | |
| 6 | *.log | |
| 7 | *.swp | |
| 8 | .vscode/ | |
| 9 | .idea/ |
| … | … | @@ -0,0 +1,453 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | 'use strict' | |
| 3 | ||
| 4 | // | |
| 5 | // atomspace-p2p-bridge — CogServer over Hyperswarm | |
| 6 | // | |
| 7 | // Identity-first design: uses hyperident profiles for persistent, | |
| 8 | // deterministic P2P identity. Both server and client derive the | |
| 9 | // same DHT topic from a profile name — no manual key exchange. | |
| 10 | // | |
| 11 | // Derivation chain: | |
| 12 | // hyperident mnemonic → SLIP-21(profileName) → seed | |
| 13 | // seed + "atomspace-bridge" salt → Ed25519 keypair | |
| 14 | // discoveryKey(publicKey) → DHT topic | |
| 15 | // | |
| 16 | // Server: runs alongside CogServer, announces on DHT | |
| 17 | // Client: creates local TCP proxy, connects via DHT lookup | |
| 18 | // | |
| 19 | // CogStorageNode ─TCP─► bridge client ─Hyperswarm─► bridge server ─TCP─► CogServer | |
| 20 | // | |
| 21 | ||
| 22 | const Hyperswarm = require('hyperswarm') | |
| 23 | const createTestnet = require('@hyperswarm/testnet') | |
| 24 | const Protomux = require('protomux') | |
| 25 | const c = require('compact-encoding') | |
| 26 | const net = require('net') | |
| 27 | const crypto = require('hypercore-crypto') | |
| 28 | const b4a = require('b4a') | |
| 29 | const fs = require('fs') | |
| 30 | const path = require('path') | |
| 31 | ||
| 32 | // hyperident is ESM — loaded dynamically | |
| 33 | let _identityManagerPromise = null | |
| 34 | function getIdentityManager () { | |
| 35 | if (!_identityManagerPromise) { | |
| 36 | _identityManagerPromise = import('hyperident/lib/identity-manager.js') | |
| 37 | .then(m => m.IdentityManager) | |
| 38 | .catch(() => null) | |
| 39 | } | |
| 40 | return _identityManagerPromise | |
| 41 | } | |
| 42 | ||
| 43 | const PROTOCOL = 'atomspace-proxy' | |
| 44 | const SALT = 'atomspace-bridge' // app-specific namespace | |
| 45 | ||
| 46 | // ---- Identity derivation ---- | |
| 47 | // Derives a deterministic Ed25519 keypair from a hyperident profile. | |
| 48 | // The salt ensures this keypair is unique to the atomspace bridge | |
| 49 | // (other apps using the same profile get different keys). | |
| 50 | async function keyPairFromProfile (profileName) { | |
| 51 | const IdentityManager = await getIdentityManager() | |
| 52 | if (!IdentityManager) { | |
| 53 | console.error('Error: hyperident not available') | |
| 54 | console.error('Run: npm install hyperident') | |
| 55 | process.exit(1) | |
| 56 | } | |
| 57 | const profileTopic = await IdentityManager.deriveTopicFromProfile(profileName) | |
| 58 | const seed = crypto.hash(b4a.concat([profileTopic, b4a.from(SALT, 'utf8')])) | |
| 59 | return crypto.keyPair(seed) | |
| 60 | } | |
| 61 | ||
| 62 | // Fallback: keypair from raw 32-byte seed | |
| 63 | function keyPairFromSeed (seed) { | |
| 64 | return crypto.keyPair(seed) | |
| 65 | } | |
| 66 | ||
| 67 | // ---- Session message encoding ---- | |
| 68 | function packSession (id, data) { | |
| 69 | const buf = b4a.allocUnsafe(4 + data.length) | |
| 70 | buf.writeUInt32BE(id, 0) | |
| 71 | b4a.copy(data, buf, 4) | |
| 72 | return buf | |
| 73 | } | |
| 74 | ||
| 75 | function unpackSession (buf) { | |
| 76 | return { | |
| 77 | id: buf.readUInt32BE(0), | |
| 78 | data: buf.subarray(4) | |
| 79 | } | |
| 80 | } | |
| 81 | ||
| 82 | // ---- Testnet bootstrap ---- | |
| 83 | async function resolveBootstrap (testnetFile) { | |
| 84 | if (!testnetFile) return null | |
| 85 | const testnetPath = path.resolve(testnetFile) | |
| 86 | if (fs.existsSync(testnetPath)) { | |
| 87 | const data = JSON.parse(fs.readFileSync(testnetPath, 'utf8')) | |
| 88 | console.log('Loaded testnet: ' + data.bootstrap.length + ' bootstrap nodes') | |
| 89 | return data.bootstrap | |
| 90 | } | |
| 91 | const testnet = await createTestnet(3) | |
| 92 | const bootstrap = testnet.bootstrap.map(n => ({ host: n.host, port: n.port })) | |
| 93 | fs.writeFileSync(testnetPath, JSON.stringify({ bootstrap }, null, 2) + '\n') | |
| 94 | console.log('Created testnet: ' + bootstrap.length + ' bootstrap nodes → ' + testnetPath) | |
| 95 | return bootstrap | |
| 96 | } | |
| 97 | ||
| 98 | // ---- CLI ---- | |
| 99 | const args = process.argv.slice(2) | |
| 100 | const mode = args[0] | |
| 101 | ||
| 102 | function getFlag (name, fallback) { | |
| 103 | const i = args.indexOf(name) | |
| 104 | return (i !== -1 && i + 1 < args.length) ? args[i + 1] : fallback | |
| 105 | } | |
| 106 | ||
| 107 | if (!mode || !['server', 'client'].includes(mode)) { | |
| 108 | console.log('AtomSpace P2P Bridge — CogServer over Hyperswarm') | |
| 109 | console.log('') | |
| 110 | console.log('Usage:') | |
| 111 | console.log(' node bridge.js server --profile <name> [options]') | |
| 112 | console.log(' node bridge.js client --profile <name> [options]') | |
| 113 | console.log(' node bridge.js client <public-key> [options]') | |
| 114 | console.log('') | |
| 115 | console.log('Identity (pick one):') | |
| 116 | console.log(' --profile <name> Hyperident profile (recommended)') | |
| 117 | console.log(' --seed <hex> Raw 64-char hex seed (fallback)') | |
| 118 | console.log('') | |
| 119 | console.log('Server options:') | |
| 120 | console.log(' --port <n> CogServer port (default: 17001)') | |
| 121 | console.log(' --testnet <file> Use local testnet for testing') | |
| 122 | console.log('') | |
| 123 | console.log('Client options:') | |
| 124 | console.log(' --proxy-port <n> Local proxy port (default: 17002)') | |
| 125 | console.log(' --testnet <file> Use local testnet (must match server)') | |
| 126 | console.log('') | |
| 127 | console.log('Examples:') | |
| 128 | console.log(' # Both sides use same profile — no key exchange needed:') | |
| 129 | console.log(' node bridge.js server --profile tabertron') | |
| 130 | console.log(' node bridge.js client --profile tabertron') | |
| 131 | console.log('') | |
| 132 | console.log(' # Manual key (no hyperident):') | |
| 133 | console.log(' node bridge.js server --seed <64-hex-chars>') | |
| 134 | console.log(' node bridge.js client <public-key>') | |
| 135 | console.log('') | |
| 136 | console.log(' # Then in Guile:') | |
| 137 | console.log(' (define csn (CogStorageNode "cog://localhost:17002/"))') | |
| 138 | console.log(' (cog-open csn)') | |
| 139 | process.exit(0) | |
| 140 | } | |
| 141 | ||
| 142 | if (mode === 'server') runServer() | |
| 143 | else runClient() | |
| 144 | ||
| 145 | // ============================================================ | |
| 146 | // SERVER | |
| 147 | // ============================================================ | |
| 148 | ||
| 149 | async function runServer () { | |
| 150 | const cogPort = parseInt(getFlag('--port', '17001')) | |
| 151 | const profileName = getFlag('--profile', null) | |
| 152 | const seedHex = getFlag('--seed', null) | |
| 153 | const testnetFile = getFlag('--testnet', null) | |
| 154 | ||
| 155 | const bootstrap = await resolveBootstrap(testnetFile) | |
| 156 | ||
| 157 | // Resolve identity — profile is primary, seed is fallback | |
| 158 | let keyPair | |
| 159 | if (profileName) { | |
| 160 | keyPair = await keyPairFromProfile(profileName) | |
| 161 | } else if (seedHex) { | |
| 162 | keyPair = keyPairFromSeed(b4a.from(seedHex, 'hex')) | |
| 163 | } else { | |
| 164 | console.error('Error: identity required') | |
| 165 | console.error('Use --profile <name> (recommended) or --seed <hex>') | |
| 166 | process.exit(1) | |
| 167 | } | |
| 168 | ||
| 169 | const topic = crypto.discoveryKey(keyPair.publicKey) | |
| 170 | ||
| 171 | const swarmOpts = { keyPair } | |
| 172 | if (bootstrap) swarmOpts.bootstrap = bootstrap | |
| 173 | const swarm = new Hyperswarm(swarmOpts) | |
| 174 | ||
| 175 | let peerCount = 0 | |
| 176 | let totalSessions = 0 | |
| 177 | ||
| 178 | console.log('=== AtomSpace P2P Bridge: Server ===') | |
| 179 | if (profileName) console.log('Profile: ' + profileName) | |
| 180 | if (bootstrap) console.log('Testnet: ' + bootstrap.length + ' nodes') | |
| 181 | console.log('Public key: ' + b4a.toString(keyPair.publicKey, 'hex')) | |
| 182 | console.log('Topic: ' + b4a.toString(topic, 'hex')) | |
| 183 | console.log('CogServer: localhost:' + cogPort) | |
| 184 | ||
| 185 | const discovery = swarm.join(topic, { server: true, client: false }) | |
| 186 | await discovery.flushed() | |
| 187 | console.log('DHT announce complete — waiting for peers...\n') | |
| 188 | ||
| 189 | swarm.on('connection', (socket, info) => { | |
| 190 | const peerId = b4a.toString(info.publicKey, 'hex').slice(0, 12) | |
| 191 | peerCount++ | |
| 192 | console.log('[+] Peer ' + peerId + ' (total: ' + peerCount + ')') | |
| 193 | ||
| 194 | const mux = Protomux.from(socket) | |
| 195 | const sessions = new Map() | |
| 196 | ||
| 197 | const channel = mux.createChannel({ | |
| 198 | protocol: PROTOCOL, | |
| 199 | onopen () { | |
| 200 | console.log(' Channel opened with ' + peerId) | |
| 201 | }, | |
| 202 | onclose () { | |
| 203 | console.log(' Channel closed with ' + peerId) | |
| 204 | for (const [, tcp] of sessions) tcp.destroy() | |
| 205 | sessions.clear() | |
| 206 | } | |
| 207 | }) | |
| 208 | ||
| 209 | // Message 0: open session (client → server) | |
| 210 | channel.addMessage({ | |
| 211 | encoding: c.uint32, | |
| 212 | onmessage (sessionId) { | |
| 213 | totalSessions++ | |
| 214 | const tcp = net.connect(cogPort, '127.0.0.1') | |
| 215 | ||
| 216 | tcp.on('connect', () => { | |
| 217 | console.log(' Session ' + sessionId + ' → CogServer:' + cogPort + | |
| 218 | ' (total: ' + totalSessions + ')') | |
| 219 | }) | |
| 220 | ||
| 221 | tcp.on('data', (buf) => { | |
| 222 | try { dataMsg.send(packSession(sessionId, buf)) } catch (e) {} | |
| 223 | }) | |
| 224 | ||
| 225 | tcp.on('error', (err) => { | |
| 226 | console.log(' Session ' + sessionId + ' TCP error: ' + err.message) | |
| 227 | try { closeMsg.send(sessionId) } catch (e) {} | |
| 228 | sessions.delete(sessionId) | |
| 229 | }) | |
| 230 | ||
| 231 | tcp.on('close', () => { | |
| 232 | try { closeMsg.send(sessionId) } catch (e) {} | |
| 233 | sessions.delete(sessionId) | |
| 234 | }) | |
| 235 | ||
| 236 | sessions.set(sessionId, tcp) | |
| 237 | } | |
| 238 | }) | |
| 239 | ||
| 240 | // Message 1: data (bidirectional) | |
| 241 | const dataMsg = channel.addMessage({ | |
| 242 | encoding: c.raw, | |
| 243 | onmessage (buf) { | |
| 244 | const msg = unpackSession(buf) | |
| 245 | const tcp = sessions.get(msg.id) | |
| 246 | if (tcp && !tcp.destroyed) tcp.write(msg.data) | |
| 247 | } | |
| 248 | }) | |
| 249 | ||
| 250 | // Message 2: close session (bidirectional) | |
| 251 | const closeMsg = channel.addMessage({ | |
| 252 | encoding: c.uint32, | |
| 253 | onmessage (sessionId) { | |
| 254 | const tcp = sessions.get(sessionId) | |
| 255 | if (tcp) { | |
| 256 | tcp.destroy() | |
| 257 | sessions.delete(sessionId) | |
| 258 | } | |
| 259 | } | |
| 260 | }) | |
| 261 | ||
| 262 | channel.open() | |
| 263 | ||
| 264 | socket.on('close', () => { | |
| 265 | peerCount-- | |
| 266 | console.log('[-] Peer ' + peerId + ' (total: ' + peerCount + ')') | |
| 267 | for (const [, tcp] of sessions) tcp.destroy() | |
| 268 | sessions.clear() | |
| 269 | }) | |
| 270 | ||
| 271 | socket.on('error', (err) => { | |
| 272 | console.log('[!] Peer ' + peerId + ' error: ' + err.message) | |
| 273 | }) | |
| 274 | }) | |
| 275 | } | |
| 276 | ||
| 277 | // ============================================================ | |
| 278 | // CLIENT | |
| 279 | // ============================================================ | |
| 280 | ||
| 281 | async function runClient () { | |
| 282 | const profileName = getFlag('--profile', null) | |
| 283 | const seedHex = getFlag('--seed', null) | |
| 284 | const proxyPort = parseInt(getFlag('--proxy-port', '17002')) | |
| 285 | const testnetFile = getFlag('--testnet', null) | |
| 286 | ||
| 287 | const bootstrap = await resolveBootstrap(testnetFile) | |
| 288 | ||
| 289 | // Resolve server identity — profile derives the same key the server uses | |
| 290 | let serverKey | |
| 291 | if (profileName) { | |
| 292 | const keyPair = await keyPairFromProfile(profileName) | |
| 293 | serverKey = keyPair.publicKey | |
| 294 | } else if (seedHex) { | |
| 295 | const keyPair = keyPairFromSeed(b4a.from(seedHex, 'hex')) | |
| 296 | serverKey = keyPair.publicKey | |
| 297 | } else { | |
| 298 | // Raw public key as positional argument | |
| 299 | const serverKeyHex = args[1] | |
| 300 | if (!serverKeyHex || serverKeyHex.length !== 64 || serverKeyHex.startsWith('--')) { | |
| 301 | console.error('Error: identity required') | |
| 302 | console.error('Use --profile <name> (recommended) or provide <public-key>') | |
| 303 | process.exit(1) | |
| 304 | } | |
| 305 | serverKey = b4a.from(serverKeyHex, 'hex') | |
| 306 | } | |
| 307 | ||
| 308 | const topic = crypto.discoveryKey(serverKey) | |
| 309 | ||
| 310 | const swarmOpts = {} | |
| 311 | if (bootstrap) swarmOpts.bootstrap = bootstrap | |
| 312 | const swarm = new Hyperswarm(swarmOpts) | |
| 313 | swarm.join(topic, { server: false, client: true }) | |
| 314 | ||
| 315 | console.log('=== AtomSpace P2P Bridge: Client ===') | |
| 316 | if (profileName) console.log('Profile: ' + profileName) | |
| 317 | if (bootstrap) console.log('Testnet: ' + bootstrap.length + ' nodes') | |
| 318 | console.log('Server key: ' + b4a.toString(serverKey, 'hex').slice(0, 24) + '...') | |
| 319 | console.log('Topic: ' + b4a.toString(topic, 'hex').slice(0, 24) + '...') | |
| 320 | console.log('Proxy port: ' + proxyPort) | |
| 321 | console.log('Looking up server on DHT...\n') | |
| 322 | ||
| 323 | let channel = null | |
| 324 | let openMsg = null | |
| 325 | let dataMsg = null | |
| 326 | let closeMsg = null | |
| 327 | const localSessions = new Map() | |
| 328 | let nextSessionId = 1 | |
| 329 | const pending = [] | |
| 330 | ||
| 331 | swarm.on('connection', (socket, info) => { | |
| 332 | const peerId = b4a.toString(info.publicKey, 'hex').slice(0, 12) | |
| 333 | console.log('[+] Connected to server ' + peerId) | |
| 334 | ||
| 335 | const mux = Protomux.from(socket) | |
| 336 | ||
| 337 | channel = mux.createChannel({ | |
| 338 | protocol: PROTOCOL, | |
| 339 | onopen () { | |
| 340 | console.log(' Channel ready — proxy active') | |
| 341 | for (const fn of pending) fn() | |
| 342 | pending.length = 0 | |
| 343 | }, | |
| 344 | onclose () { | |
| 345 | console.log(' Channel closed') | |
| 346 | channel = null | |
| 347 | openMsg = null | |
| 348 | dataMsg = null | |
| 349 | closeMsg = null | |
| 350 | for (const [, tcp] of localSessions) tcp.destroy() | |
| 351 | localSessions.clear() | |
| 352 | } | |
| 353 | }) | |
| 354 | ||
| 355 | openMsg = channel.addMessage({ | |
| 356 | encoding: c.uint32, | |
| 357 | onmessage () {} | |
| 358 | }) | |
| 359 | ||
| 360 | dataMsg = channel.addMessage({ | |
| 361 | encoding: c.raw, | |
| 362 | onmessage (buf) { | |
| 363 | const msg = unpackSession(buf) | |
| 364 | const tcp = localSessions.get(msg.id) | |
| 365 | if (tcp && !tcp.destroyed) tcp.write(msg.data) | |
| 366 | } | |
| 367 | }) | |
| 368 | ||
| 369 | closeMsg = channel.addMessage({ | |
| 370 | encoding: c.uint32, | |
| 371 | onmessage (sessionId) { | |
| 372 | const tcp = localSessions.get(sessionId) | |
| 373 | if (tcp) { | |
| 374 | tcp.destroy() | |
| 375 | localSessions.delete(sessionId) | |
| 376 | } | |
| 377 | } | |
| 378 | }) | |
| 379 | ||
| 380 | channel.open() | |
| 381 | ||
| 382 | socket.on('close', () => { | |
| 383 | console.log('[-] Server disconnected, waiting for reconnect...') | |
| 384 | channel = null | |
| 385 | openMsg = null | |
| 386 | dataMsg = null | |
| 387 | closeMsg = null | |
| 388 | }) | |
| 389 | ||
| 390 | socket.on('error', (err) => { | |
| 391 | console.log('[!] Server error: ' + err.message) | |
| 392 | }) | |
| 393 | }) | |
| 394 | ||
| 395 | // Local TCP proxy — CogStorageNode connects here | |
| 396 | const proxy = net.createServer((tcp) => { | |
| 397 | const sessionId = nextSessionId++ | |
| 398 | localSessions.set(sessionId, tcp) | |
| 399 | ||
| 400 | if (channel && openMsg) { | |
| 401 | openMsg.send(sessionId) | |
| 402 | wireSession(sessionId, tcp) | |
| 403 | console.log(' Session ' + sessionId + ' → server') | |
| 404 | } else { | |
| 405 | console.log(' Session ' + sessionId + ' queued (no server yet)') | |
| 406 | pending.push(() => { | |
| 407 | if (tcp.destroyed) return | |
| 408 | openMsg.send(sessionId) | |
| 409 | wireSession(sessionId, tcp) | |
| 410 | console.log(' Session ' + sessionId + ' → server (dequeued)') | |
| 411 | }) | |
| 412 | } | |
| 413 | }) | |
| 414 | ||
| 415 | function wireSession (sessionId, tcp) { | |
| 416 | tcp.on('data', (buf) => { | |
| 417 | try { | |
| 418 | if (dataMsg) dataMsg.send(packSession(sessionId, buf)) | |
| 419 | } catch (e) {} | |
| 420 | }) | |
| 421 | ||
| 422 | tcp.on('error', () => { | |
| 423 | try { if (closeMsg) closeMsg.send(sessionId) } catch (e) {} | |
| 424 | localSessions.delete(sessionId) | |
| 425 | }) | |
| 426 | ||
| 427 | tcp.on('close', () => { | |
| 428 | try { if (closeMsg) closeMsg.send(sessionId) } catch (e) {} | |
| 429 | localSessions.delete(sessionId) | |
| 430 | }) | |
| 431 | } | |
| 432 | ||
| 433 | proxy.listen(proxyPort, () => { | |
| 434 | console.log('Proxy listening on port ' + proxyPort) | |
| 435 | console.log('') | |
| 436 | console.log('Use in Guile:') | |
| 437 | console.log(' (use-modules (opencog persist-cog))') | |
| 438 | console.log(' (define csn (CogStorageNode "cog://localhost:' + proxyPort + '/"))') | |
| 439 | console.log(' (cog-open csn)') | |
| 440 | console.log('') | |
| 441 | }) | |
| 442 | } | |
| 443 | ||
| 444 | // ---- Graceful shutdown ---- | |
| 445 | ||
| 446 | process.on('SIGINT', () => { | |
| 447 | console.log('\nShutting down...') | |
| 448 | process.exit(0) | |
| 449 | }) | |
| 450 | ||
| 451 | process.on('uncaughtException', (err) => { | |
| 452 | console.error('Uncaught:', err.message) | |
| 453 | }) |
| … | … | @@ -0,0 +1,547 @@ |
| 1 | { | |
| 2 | "name": "atomspace-p2p-bridge", | |
| 3 | "version": "1.0.0", | |
| 4 | "lockfileVersion": 3, | |
| 5 | "requires": true, | |
| 6 | "packages": { | |
| 7 | "": { | |
| 8 | "name": "atomspace-p2p-bridge", | |
| 9 | "version": "1.0.0", | |
| 10 | "dependencies": { | |
| 11 | "@hyperswarm/testnet": "^3.1.4", | |
| 12 | "b4a": "^1.6.4", | |
| 13 | "compact-encoding": "^2.12.0", | |
| 14 | "hypercore-crypto": "^3.3.1", | |
| 15 | "hyperident": "file:../../hyper-fleeky/repos/hyperident", | |
| 16 | "hyperswarm": "^4.4.1", | |
| 17 | "protomux": "^3.4.0" | |
| 18 | }, | |
| 19 | "bin": { | |
| 20 | "atomspace-bridge": "bridge.js" | |
| 21 | } | |
| 22 | }, | |
| 23 | "../../hyper-fleeky/repos/hyperident": { | |
| 24 | "version": "0.1.0", | |
| 25 | "license": "Apache-2.0", | |
| 26 | "dependencies": { | |
| 27 | "b4a": "^1.6.7", | |
| 28 | "bare-crypto": "^1.11.1", | |
| 29 | "bare-fs": "^2.3.2", | |
| 30 | "bare-os": "^2.4.2", | |
| 31 | "bare-path": "^2.1.3", | |
| 32 | "hypercore-id-encoding": "^1.3.0", | |
| 33 | "hyperswarm": "^4.12.1", | |
| 34 | "keet-identity-key": "^3.1.0", | |
| 35 | "sodium-universal": "^5.0.1" | |
| 36 | }, | |
| 37 | "bin": { | |
| 38 | "hyperident": "index.js" | |
| 39 | }, | |
| 40 | "devDependencies": { | |
| 41 | "brittle": "^3.0.0", | |
| 42 | "pear-interface": "^1.0.0" | |
| 43 | } | |
| 44 | }, | |
| 45 | "node_modules/@hyperswarm/secret-stream": { | |
| 46 | "version": "6.9.1", | |
| 47 | "resolved": "https://registry.npmjs.org/@hyperswarm/secret-stream/-/secret-stream-6.9.1.tgz", | |
| 48 | "integrity": "sha512-xb0S5y3YJwBakD77JOGBHlBxdp63mHClZoXBYoLv+9wH8e054ESKlmQptWqjJK5dv5VMUIVYOJB4MaOpB0JdGw==", | |
| 49 | "license": "Apache-2.0", | |
| 50 | "dependencies": { | |
| 51 | "b4a": "^1.1.0", | |
| 52 | "hypercore-crypto": "^3.3.1", | |
| 53 | "noise-curve-ed": "^2.0.1", | |
| 54 | "noise-handshake": "^4.0.0", | |
| 55 | "sodium-secretstream": "^1.1.0", | |
| 56 | "sodium-universal": "^5.0.0", | |
| 57 | "streamx": "^2.14.0", | |
| 58 | "timeout-refresh": "^2.0.0", | |
| 59 | "unslab": "^1.3.0" | |
| 60 | } | |
| 61 | }, | |
| 62 | "node_modules/@hyperswarm/testnet": { | |
| 63 | "version": "3.1.4", | |
| 64 | "resolved": "https://registry.npmjs.org/@hyperswarm/testnet/-/testnet-3.1.4.tgz", | |
| 65 | "integrity": "sha512-Jw8JIZ9kdZ4R9jwJHsqctj5YeRqecw3aeCg2NS12HBXCqMioQIYzVqHs/8BWdOQCf34+BEXkwMQjba9CzaLwkg==", | |
| 66 | "license": "Apache-2.0", | |
| 67 | "dependencies": { | |
| 68 | "hyperdht": "^6.6.0" | |
| 69 | }, | |
| 70 | "bin": { | |
| 71 | "hyperswarm-testnet": "bin.js" | |
| 72 | } | |
| 73 | }, | |
| 74 | "node_modules/adaptive-timeout": { | |
| 75 | "version": "1.0.1", | |
| 76 | "resolved": "https://registry.npmjs.org/adaptive-timeout/-/adaptive-timeout-1.0.1.tgz", | |
| 77 | "integrity": "sha512-+seGiBtHqbnyZ0Quq/ZGxxwP66153WBABawa07/QeyuPFzbduPBjiGQAyCiriiLDzt3dkbMBskSlfqtRwblK8Q==", | |
| 78 | "license": "Apache-2.0", | |
| 79 | "dependencies": { | |
| 80 | "xache": "^1.2.1" | |
| 81 | } | |
| 82 | }, | |
| 83 | "node_modules/b4a": { | |
| 84 | "version": "1.7.3", | |
| 85 | "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", | |
| 86 | "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", | |
| 87 | "license": "Apache-2.0", | |
| 88 | "peerDependencies": { | |
| 89 | "react-native-b4a": "*" | |
| 90 | }, | |
| 91 | "peerDependenciesMeta": { | |
| 92 | "react-native-b4a": { | |
| 93 | "optional": true | |
| 94 | } | |
| 95 | } | |
| 96 | }, | |
| 97 | "node_modules/bare-addon-resolve": { | |
| 98 | "version": "1.9.7", | |
| 99 | "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.9.7.tgz", | |
| 100 | "integrity": "sha512-cIo93Y4Maw8ZNrfq5qlvRBqCHUyeSzMyFBqJRNi91SOspeWiFq3ixiq0ExYWmtOBauIkCLod2Mng73xr7fmkXA==", | |
| 101 | "license": "Apache-2.0", | |
| 102 | "dependencies": { | |
| 103 | "bare-module-resolve": "^1.10.0", | |
| 104 | "bare-semver": "^1.0.0" | |
| 105 | }, | |
| 106 | "peerDependencies": { | |
| 107 | "bare-url": "*" | |
| 108 | }, | |
| 109 | "peerDependenciesMeta": { | |
| 110 | "bare-url": { | |
| 111 | "optional": true | |
| 112 | } | |
| 113 | } | |
| 114 | }, | |
| 115 | "node_modules/bare-events": { | |
| 116 | "version": "2.8.2", | |
| 117 | "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", | |
| 118 | "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", | |
| 119 | "license": "Apache-2.0", | |
| 120 | "peerDependencies": { | |
| 121 | "bare-abort-controller": "*" | |
| 122 | }, | |
| 123 | "peerDependenciesMeta": { | |
| 124 | "bare-abort-controller": { | |
| 125 | "optional": true | |
| 126 | } | |
| 127 | } | |
| 128 | }, | |
| 129 | "node_modules/bare-module-resolve": { | |
| 130 | "version": "1.12.1", | |
| 131 | "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.1.tgz", | |
| 132 | "integrity": "sha512-hbmAPyFpEq8FoZMd5sFO3u6MC5feluWoGE8YKlA8fCrl6mNtx68Wjg4DTiDJcqRJaovTvOYKfYngoBUnbaT7eg==", | |
| 133 | "license": "Apache-2.0", | |
| 134 | "dependencies": { | |
| 135 | "bare-semver": "^1.0.0" | |
| 136 | }, | |
| 137 | "peerDependencies": { | |
| 138 | "bare-url": "*" | |
| 139 | }, | |
| 140 | "peerDependenciesMeta": { | |
| 141 | "bare-url": { | |
| 142 | "optional": true | |
| 143 | } | |
| 144 | } | |
| 145 | }, | |
| 146 | "node_modules/bare-semver": { | |
| 147 | "version": "1.0.2", | |
| 148 | "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.0.2.tgz", | |
| 149 | "integrity": "sha512-ESVaN2nzWhcI5tf3Zzcq9aqCZ676VWzqw07eEZ0qxAcEOAFYBa0pWq8sK34OQeHLY3JsfKXZS9mDyzyxGjeLzA==", | |
| 150 | "license": "Apache-2.0" | |
| 151 | }, | |
| 152 | "node_modules/bits-to-bytes": { | |
| 153 | "version": "1.3.0", | |
| 154 | "resolved": "https://registry.npmjs.org/bits-to-bytes/-/bits-to-bytes-1.3.0.tgz", | |
| 155 | "integrity": "sha512-OJoHTpFXS9bXHBCekGTByf3MqM8CGblBDIduKQeeVVeiU9dDWywSSirXIBYGgg3d1zbVuvnMa1vD4r6PA0kOKg==", | |
| 156 | "license": "ISC", | |
| 157 | "dependencies": { | |
| 158 | "b4a": "^1.5.0" | |
| 159 | } | |
| 160 | }, | |
| 161 | "node_modules/blind-relay": { | |
| 162 | "version": "1.4.0", | |
| 163 | "resolved": "https://registry.npmjs.org/blind-relay/-/blind-relay-1.4.0.tgz", | |
| 164 | "integrity": "sha512-6xt7fDfCs6eGmNNym6I9N42jmjcMQn2qwwOVnkP9ZnrkXFk6c4/tdO1xqRmDEzKzV8gigd+DVdCUG/RUYnen7Q==", | |
| 165 | "license": "Apache-2.0", | |
| 166 | "dependencies": { | |
| 167 | "b4a": "^1.6.4", | |
| 168 | "bare-events": "^2.2.0", | |
| 169 | "bits-to-bytes": "^1.3.0", | |
| 170 | "compact-encoding": "^2.12.0", | |
| 171 | "compact-encoding-bitfield": "^1.0.0", | |
| 172 | "protomux": "^3.5.1", | |
| 173 | "sodium-universal": "^5.0.0", | |
| 174 | "streamx": "^2.15.1" | |
| 175 | } | |
| 176 | }, | |
| 177 | "node_modules/bogon": { | |
| 178 | "version": "1.2.0", | |
| 179 | "resolved": "https://registry.npmjs.org/bogon/-/bogon-1.2.0.tgz", | |
| 180 | "integrity": "sha512-FqOBr/1VMzCOsoJd+fzNUMarUYki2+TKt07A2+xaulsNx4r53iJ7MV5k0jbqg7W2U0CsLqxCZOrFibdG6h6HSg==", | |
| 181 | "license": "MIT", | |
| 182 | "dependencies": { | |
| 183 | "compact-encoding": "^2.11.0", | |
| 184 | "compact-encoding-net": "^1.2.0" | |
| 185 | } | |
| 186 | }, | |
| 187 | "node_modules/compact-encoding": { | |
| 188 | "version": "2.18.0", | |
| 189 | "resolved": "https://registry.npmjs.org/compact-encoding/-/compact-encoding-2.18.0.tgz", | |
| 190 | "integrity": "sha512-goACAOlhMI2xo5jGOMUDfOLnGdRE1jGfyZ+zie8N5114nHrbPIqf6GLUtzbLof6DSyrERlYRm3EcBplte5LcQw==", | |
| 191 | "license": "Apache-2.0", | |
| 192 | "dependencies": { | |
| 193 | "b4a": "^1.3.0" | |
| 194 | } | |
| 195 | }, | |
| 196 | "node_modules/compact-encoding-bitfield": { | |
| 197 | "version": "1.0.0", | |
| 198 | "resolved": "https://registry.npmjs.org/compact-encoding-bitfield/-/compact-encoding-bitfield-1.0.0.tgz", | |
| 199 | "integrity": "sha512-3nMVKUg+PF72UHfainmCL8uKvyWfxsjqOtUY+HiMPGLPCTjnwzoKfFAMo1Ad7nwTPdjBqtGK5b3BOFTFW4EBTg==", | |
| 200 | "license": "ISC", | |
| 201 | "dependencies": { | |
| 202 | "compact-encoding": "^2.4.1" | |
| 203 | } | |
| 204 | }, | |
| 205 | "node_modules/compact-encoding-net": { | |
| 206 | "version": "1.2.0", | |
| 207 | "resolved": "https://registry.npmjs.org/compact-encoding-net/-/compact-encoding-net-1.2.0.tgz", | |
| 208 | "integrity": "sha512-LVXpNpF7PGQeHRVVLGgYWzuVoYAaDZvKUsUxRioGfkotzvOh4AzoQF1HBH3zMNaSnx7gJXuUr3hkjnijaH/Eng==", | |
| 209 | "license": "ISC", | |
| 210 | "dependencies": { | |
| 211 | "compact-encoding": "^2.4.1" | |
| 212 | } | |
| 213 | }, | |
| 214 | "node_modules/dht-rpc": { | |
| 215 | "version": "6.24.3", | |
| 216 | "resolved": "https://registry.npmjs.org/dht-rpc/-/dht-rpc-6.24.3.tgz", | |
| 217 | "integrity": "sha512-XZXIBG+FBFhfiz+3uimuH3vBw8FmqLkqR/W0VCo/lbEq3qDlFAxCOZxUHBqcRT254EysirP8+rw+Ynv0e8zhtg==", | |
| 218 | "license": "MIT", | |
| 219 | "dependencies": { | |
| 220 | "adaptive-timeout": "^1.0.1", | |
| 221 | "b4a": "^1.6.1", | |
| 222 | "bare-events": "^2.2.0", | |
| 223 | "compact-encoding": "^2.11.0", | |
| 224 | "compact-encoding-net": "^1.2.0", | |
| 225 | "fast-fifo": "^1.1.0", | |
| 226 | "kademlia-routing-table": "^1.0.1", | |
| 227 | "nat-sampler": "^1.0.1", | |
| 228 | "sodium-universal": "^5.0.0", | |
| 229 | "streamx": "^2.13.2", | |
| 230 | "time-ordered-set": "^2.0.0", | |
| 231 | "udx-native": "^1.5.3" | |
| 232 | } | |
| 233 | }, | |
| 234 | "node_modules/events-universal": { | |
| 235 | "version": "1.0.1", | |
| 236 | "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", | |
| 237 | "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", | |
| 238 | "license": "Apache-2.0", | |
| 239 | "dependencies": { | |
| 240 | "bare-events": "^2.7.0" | |
| 241 | } | |
| 242 | }, | |
| 243 | "node_modules/fast-fifo": { | |
| 244 | "version": "1.3.2", | |
| 245 | "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", | |
| 246 | "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", | |
| 247 | "license": "MIT" | |
| 248 | }, | |
| 249 | "node_modules/hypercore-crypto": { | |
| 250 | "version": "3.6.1", | |
| 251 | "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.6.1.tgz", | |
| 252 | "integrity": "sha512-ltIz2uDwy9pO/ZGTvqcjzyBkvt6O4cVm4r/nNxh0GFs/RbQtqP/i4wCvLEdmU7ptgtnw7fI67WYD1aHPuv4OVA==", | |
| 253 | "license": "MIT", | |
| 254 | "dependencies": { | |
| 255 | "b4a": "^1.6.6", | |
| 256 | "compact-encoding": "^2.15.0", | |
| 257 | "sodium-universal": "^5.0.0" | |
| 258 | } | |
| 259 | }, | |
| 260 | "node_modules/hypercore-id-encoding": { | |
| 261 | "version": "1.3.0", | |
| 262 | "resolved": "https://registry.npmjs.org/hypercore-id-encoding/-/hypercore-id-encoding-1.3.0.tgz", | |
| 263 | "integrity": "sha512-W6sHdGo5h7LXEsoWfKf/KfuROZmZRQDlGqJF2EPHW+noCK66Vvr0+zE6cL0vqQi18s0kQPeN7Sq3QyR0Ytc2VQ==", | |
| 264 | "license": "Apache-2.0", | |
| 265 | "dependencies": { | |
| 266 | "b4a": "^1.5.3", | |
| 267 | "z32": "^1.0.0" | |
| 268 | } | |
| 269 | }, | |
| 270 | "node_modules/hyperdht": { | |
| 271 | "version": "6.29.0", | |
| 272 | "resolved": "https://registry.npmjs.org/hyperdht/-/hyperdht-6.29.0.tgz", | |
| 273 | "integrity": "sha512-okIpelpIhRTBDCCe2CNWfLHbVSQ5DPbl1Npb1zet8SqrkCsYF27RHJ04IaYqiigHI9y0KZgjOHdZbejalbFVoQ==", | |
| 274 | "license": "MIT", | |
| 275 | "dependencies": { | |
| 276 | "@hyperswarm/secret-stream": "^6.6.2", | |
| 277 | "b4a": "^1.3.1", | |
| 278 | "bare-events": "^2.2.0", | |
| 279 | "blind-relay": "^1.3.0", | |
| 280 | "bogon": "^1.0.0", | |
| 281 | "compact-encoding": "^2.4.1", | |
| 282 | "compact-encoding-net": "^1.0.1", | |
| 283 | "dht-rpc": "^6.15.1", | |
| 284 | "hypercore-crypto": "^3.3.0", | |
| 285 | "hypercore-id-encoding": "^1.2.0", | |
| 286 | "noise-curve-ed": "^2.0.0", | |
| 287 | "noise-handshake": "^4.0.0", | |
| 288 | "record-cache": "^1.1.1", | |
| 289 | "safety-catch": "^1.0.1", | |
| 290 | "signal-promise": "^1.0.3", | |
| 291 | "sodium-universal": "^5.0.1", | |
| 292 | "streamx": "^2.16.1", | |
| 293 | "unslab": "^1.3.0", | |
| 294 | "xache": "^1.1.0" | |
| 295 | }, | |
| 296 | "bin": { | |
| 297 | "hyperdht": "bin.js" | |
| 298 | } | |
| 299 | }, | |
| 300 | "node_modules/hyperident": { | |
| 301 | "resolved": "../../hyper-fleeky/repos/hyperident", | |
| 302 | "link": true | |
| 303 | }, | |
| 304 | "node_modules/hyperswarm": { | |
| 305 | "version": "4.16.0", | |
| 306 | "resolved": "https://registry.npmjs.org/hyperswarm/-/hyperswarm-4.16.0.tgz", | |
| 307 | "integrity": "sha512-f86RsI1qP/nbBTv4wYxOhu1JnWvGZgiC1w69aRFn0xGSavLMWjvbbVspVApOnRjiPKpYVVGACXdrvEOrL3rHdw==", | |
| 308 | "license": "MIT", | |
| 309 | "dependencies": { | |
| 310 | "b4a": "^1.3.1", | |
| 311 | "bare-events": "^2.2.0", | |
| 312 | "hyperdht": "^6.21.0", | |
| 313 | "safety-catch": "^1.0.2", | |
| 314 | "shuffled-priority-queue": "^2.1.0", | |
| 315 | "streamx": "^2.22.1", | |
| 316 | "unslab": "^1.3.0" | |
| 317 | } | |
| 318 | }, | |
| 319 | "node_modules/kademlia-routing-table": { | |
| 320 | "version": "1.0.6", | |
| 321 | "resolved": "https://registry.npmjs.org/kademlia-routing-table/-/kademlia-routing-table-1.0.6.tgz", | |
| 322 | "integrity": "sha512-Ve6jwIlUCYvUzBnXnzVRHDZCFgXURW9gmF3r7n05kZs/2rNbLHXwGdcq0qIaSwdmJCvtosgR4JensnVU65hzNQ==", | |
| 323 | "license": "MIT", | |
| 324 | "dependencies": { | |
| 325 | "bare-events": "^2.2.0" | |
| 326 | } | |
| 327 | }, | |
| 328 | "node_modules/nanoassert": { | |
| 329 | "version": "2.0.0", | |
| 330 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", | |
| 331 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==", | |
| 332 | "license": "ISC" | |
| 333 | }, | |
| 334 | "node_modules/nat-sampler": { | |
| 335 | "version": "1.0.1", | |
| 336 | "resolved": "https://registry.npmjs.org/nat-sampler/-/nat-sampler-1.0.1.tgz", | |
| 337 | "integrity": "sha512-yQvyNN7xbqR8crTKk3U8gRgpcV1Az+vfCEijiHu9oHHsnIl8n3x+yXNHl42M6L3czGynAVoOT9TqBfS87gDdcw==", | |
| 338 | "license": "MIT" | |
| 339 | }, | |
| 340 | "node_modules/noise-curve-ed": { | |
| 341 | "version": "2.1.0", | |
| 342 | "resolved": "https://registry.npmjs.org/noise-curve-ed/-/noise-curve-ed-2.1.0.tgz", | |
| 343 | "integrity": "sha512-zAzJx+VwZM3w6EA1hTmDhJfvAnCeBQn/1FAeZ0LtGxCcCtlAK/uJXQVF/eDVUOaAZ286lHlx77WJ+qj9SmsRRg==", | |
| 344 | "license": "ISC", | |
| 345 | "dependencies": { | |
| 346 | "b4a": "^1.1.0", | |
| 347 | "nanoassert": "^2.0.0", | |
| 348 | "sodium-universal": "^5.0.0" | |
| 349 | } | |
| 350 | }, | |
| 351 | "node_modules/noise-handshake": { | |
| 352 | "version": "4.2.0", | |
| 353 | "resolved": "https://registry.npmjs.org/noise-handshake/-/noise-handshake-4.2.0.tgz", | |
| 354 | "integrity": "sha512-9O/VTNX/E2/AToyMTTDU0J/4WhaXMTdqc2DHs9vf+snoZ0cenSBq0dNYTVV1snYYEkmo6QeRrYMxtqtoYnY+LA==", | |
| 355 | "license": "Apache-2.0", | |
| 356 | "dependencies": { | |
| 357 | "b4a": "^1.1.0", | |
| 358 | "nanoassert": "^2.0.0", | |
| 359 | "sodium-universal": "^5.0.0" | |
| 360 | } | |
| 361 | }, | |
| 362 | "node_modules/protomux": { | |
| 363 | "version": "3.10.1", | |
| 364 | "resolved": "https://registry.npmjs.org/protomux/-/protomux-3.10.1.tgz", | |
| 365 | "integrity": "sha512-jgBqx8ZyaBWea/DFG4eOu1scOaeBwcnagiRC1XFVrjeGt7oAb0Pk5udPpBUpJ4DJBRjra50jD6YcZiQQTRqaaA==", | |
| 366 | "license": "MIT", | |
| 367 | "dependencies": { | |
| 368 | "b4a": "^1.3.1", | |
| 369 | "compact-encoding": "^2.5.1", | |
| 370 | "queue-tick": "^1.0.0", | |
| 371 | "safety-catch": "^1.0.1", | |
| 372 | "unslab": "^1.3.0" | |
| 373 | } | |
| 374 | }, | |
| 375 | "node_modules/queue-tick": { | |
| 376 | "version": "1.0.1", | |
| 377 | "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", | |
| 378 | "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", | |
| 379 | "license": "MIT" | |
| 380 | }, | |
| 381 | "node_modules/record-cache": { | |
| 382 | "version": "1.2.0", | |
| 383 | "resolved": "https://registry.npmjs.org/record-cache/-/record-cache-1.2.0.tgz", | |
| 384 | "integrity": "sha512-kyy3HWCez2WrotaL3O4fTn0rsIdfRKOdQQcEJ9KpvmKmbffKVvwsloX063EgRUlpJIXHiDQFhJcTbZequ2uTZw==", | |
| 385 | "license": "MIT", | |
| 386 | "dependencies": { | |
| 387 | "b4a": "^1.3.1" | |
| 388 | } | |
| 389 | }, | |
| 390 | "node_modules/require-addon": { | |
| 391 | "version": "1.2.0", | |
| 392 | "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", | |
| 393 | "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", | |
| 394 | "license": "Apache-2.0", | |
| 395 | "dependencies": { | |
| 396 | "bare-addon-resolve": "^1.3.0" | |
| 397 | }, | |
| 398 | "engines": { | |
| 399 | "bare": ">=1.10.0" | |
| 400 | } | |
| 401 | }, | |
| 402 | "node_modules/safety-catch": { | |
| 403 | "version": "1.0.2", | |
| 404 | "resolved": "https://registry.npmjs.org/safety-catch/-/safety-catch-1.0.2.tgz", | |
| 405 | "integrity": "sha512-C1UYVZ4dtbBxEtvOcpjBaaD27nP8MlvyAQEp2fOTOEe6pfUpk1cDUxij6BR1jZup6rSyUTaBBplK7LanskrULA==", | |
| 406 | "license": "MIT" | |
| 407 | }, | |
| 408 | "node_modules/shuffled-priority-queue": { | |
| 409 | "version": "2.1.0", | |
| 410 | "resolved": "https://registry.npmjs.org/shuffled-priority-queue/-/shuffled-priority-queue-2.1.0.tgz", | |
| 411 | "integrity": "sha512-xhdh7fHyMsr0m/w2kDfRJuBFRS96b9l8ZPNWGaQ+PMvnUnZ/Eh+gJJ9NsHBd7P9k0399WYlCLzsy18EaMfyadA==", | |
| 412 | "license": "MIT", | |
| 413 | "dependencies": { | |
| 414 | "unordered-set": "^2.0.1" | |
| 415 | } | |
| 416 | }, | |
| 417 | "node_modules/signal-promise": { | |
| 418 | "version": "1.0.3", | |
| 419 | "resolved": "https://registry.npmjs.org/signal-promise/-/signal-promise-1.0.3.tgz", | |
| 420 | "integrity": "sha512-WBgv0UnIq2C+Aeh0/n+IRpP6967eIx9WpynTUoiW3isPpfe1zu2LJzyfXdo9Tgef8yR/sGjcMvoUXD7EYdiz+g==", | |
| 421 | "license": "MIT" | |
| 422 | }, | |
| 423 | "node_modules/sodium-native": { | |
| 424 | "version": "5.0.10", | |
| 425 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.0.10.tgz", | |
| 426 | "integrity": "sha512-UIw+0AbpCQRuTJF88JWrZomP4O+PXhlWvdopiAJOsUivTyHTf3korMyStxkZuPngSbBEtEfDdc4ewEd8/T4/lA==", | |
| 427 | "license": "MIT", | |
| 428 | "dependencies": { | |
| 429 | "require-addon": "^1.1.0", | |
| 430 | "which-runtime": "^1.2.1" | |
| 431 | }, | |
| 432 | "engines": { | |
| 433 | "bare": ">=1.16.0" | |
| 434 | } | |
| 435 | }, | |
| 436 | "node_modules/sodium-secretstream": { | |
| 437 | "version": "1.2.0", | |
| 438 | "resolved": "https://registry.npmjs.org/sodium-secretstream/-/sodium-secretstream-1.2.0.tgz", | |
| 439 | "integrity": "sha512-q/DbraNFXm1KfCiiZvapmz5UC3OlpirYFIvBK2MhGaOFSb3gRyk8OXTi17UI9SGfshQNCpsVvlopogbzZNyW6Q==", | |
| 440 | "license": "MIT", | |
| 441 | "dependencies": { | |
| 442 | "b4a": "^1.1.1", | |
| 443 | "sodium-universal": "^5.0.0" | |
| 444 | } | |
| 445 | }, | |
| 446 | "node_modules/sodium-universal": { | |
| 447 | "version": "5.0.1", | |
| 448 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 449 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 450 | "license": "MIT", | |
| 451 | "dependencies": { | |
| 452 | "sodium-native": "^5.0.1" | |
| 453 | }, | |
| 454 | "peerDependencies": { | |
| 455 | "sodium-javascript": "~0.8.0" | |
| 456 | }, | |
| 457 | "peerDependenciesMeta": { | |
| 458 | "sodium-javascript": { | |
| 459 | "optional": true | |
| 460 | } | |
| 461 | } | |
| 462 | }, | |
| 463 | "node_modules/streamx": { | |
| 464 | "version": "2.23.0", | |
| 465 | "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", | |
| 466 | "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", | |
| 467 | "license": "MIT", | |
| 468 | "dependencies": { | |
| 469 | "events-universal": "^1.0.0", | |
| 470 | "fast-fifo": "^1.3.2", | |
| 471 | "text-decoder": "^1.1.0" | |
| 472 | } | |
| 473 | }, | |
| 474 | "node_modules/text-decoder": { | |
| 475 | "version": "1.2.3", | |
| 476 | "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", | |
| 477 | "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", | |
| 478 | "license": "Apache-2.0", | |
| 479 | "dependencies": { | |
| 480 | "b4a": "^1.6.4" | |
| 481 | } | |
| 482 | }, | |
| 483 | "node_modules/time-ordered-set": { | |
| 484 | "version": "2.0.1", | |
| 485 | "resolved": "https://registry.npmjs.org/time-ordered-set/-/time-ordered-set-2.0.1.tgz", | |
| 486 | "integrity": "sha512-VJEKmgSN2UiOLB8BpN8Sh2b9LGMHTP5OPrQRpnKjvOheOyzk0mufbjzjKTIG2gO4A+Y+vDJ+0TcLbpUmMLsg8A==", | |
| 487 | "license": "MIT" | |
| 488 | }, | |
| 489 | "node_modules/timeout-refresh": { | |
| 490 | "version": "2.0.1", | |
| 491 | "resolved": "https://registry.npmjs.org/timeout-refresh/-/timeout-refresh-2.0.1.tgz", | |
| 492 | "integrity": "sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw==", | |
| 493 | "license": "MIT" | |
| 494 | }, | |
| 495 | "node_modules/udx-native": { | |
| 496 | "version": "1.19.2", | |
| 497 | "resolved": "https://registry.npmjs.org/udx-native/-/udx-native-1.19.2.tgz", | |
| 498 | "integrity": "sha512-RNYh+UhfryCsF5hE2ZOuIqcZ+qdipXK3UsarwxWJwsUQZFE3ybwz0mPjwb5ev1PMBcjFahWiepS/q0wwL51c2g==", | |
| 499 | "license": "Apache-2.0", | |
| 500 | "dependencies": { | |
| 501 | "b4a": "^1.5.0", | |
| 502 | "bare-events": "^2.2.0", | |
| 503 | "require-addon": "^1.1.0", | |
| 504 | "streamx": "^2.22.0" | |
| 505 | }, | |
| 506 | "engines": { | |
| 507 | "bare": ">=1.17.4" | |
| 508 | } | |
| 509 | }, | |
| 510 | "node_modules/unordered-set": { | |
| 511 | "version": "2.0.1", | |
| 512 | "resolved": "https://registry.npmjs.org/unordered-set/-/unordered-set-2.0.1.tgz", | |
| 513 | "integrity": "sha512-eUmNTPzdx+q/WvOHW0bgGYLWvWHNT3PTKEQLg0MAQhc0AHASHVHoP/9YytYd4RBVariqno/mEUhVZN98CmD7bg==", | |
| 514 | "license": "MIT" | |
| 515 | }, | |
| 516 | "node_modules/unslab": { | |
| 517 | "version": "1.3.0", | |
| 518 | "resolved": "https://registry.npmjs.org/unslab/-/unslab-1.3.0.tgz", | |
| 519 | "integrity": "sha512-YATkfKAFj47kTzmiQrWXMyRvaVrHsW6MEALa4bm+FhiA2YG4oira+Z3DXN6LrYOYn2Y8eO94Lwl9DOHjs1FpoQ==", | |
| 520 | "license": "Apache-2.0", | |
| 521 | "dependencies": { | |
| 522 | "b4a": "^1.6.6" | |
| 523 | } | |
| 524 | }, | |
| 525 | "node_modules/which-runtime": { | |
| 526 | "version": "1.3.2", | |
| 527 | "resolved": "https://registry.npmjs.org/which-runtime/-/which-runtime-1.3.2.tgz", | |
| 528 | "integrity": "sha512-5kwCfWml7+b2NO7KrLMhYihjRx0teKkd3yGp1Xk5Vaf2JGdSh+rgVhEALAD9c/59dP+YwJHXoEO7e8QPy7gOkw==", | |
| 529 | "license": "Apache-2.0" | |
| 530 | }, | |
| 531 | "node_modules/xache": { | |
| 532 | "version": "1.2.1", | |
| 533 | "resolved": "https://registry.npmjs.org/xache/-/xache-1.2.1.tgz", | |
| 534 | "integrity": "sha512-igRS6jPreJ54ABdzhh4mCDXcz+XMaWO2q1ABRV2yWYuk29jlp8VT7UBdCqNkX7rpYBbXsebVVKkwIuYZjyZNqA==", | |
| 535 | "license": "MIT" | |
| 536 | }, | |
| 537 | "node_modules/z32": { | |
| 538 | "version": "1.1.0", | |
| 539 | "resolved": "https://registry.npmjs.org/z32/-/z32-1.1.0.tgz", | |
| 540 | "integrity": "sha512-1WUHy+VS6d0HPNspDxvLssBbeQjXMjSnpv0vH82vRAUfg847NmX3OXozp/hRP5jPhxBbrVzrgvAt+UsGNzRFQQ==", | |
| 541 | "license": "MIT", | |
| 542 | "dependencies": { | |
| 543 | "b4a": "^1.5.3" | |
| 544 | } | |
| 545 | } | |
| 546 | } | |
| 547 | } |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | { | |
| 2 | "name": "atomspace-p2p-bridge", | |
| 3 | "version": "1.0.0", | |
| 4 | "description": "Hyperswarm TCP bridge for CogServer — P2P AtomSpace networking", | |
| 5 | "main": "bridge.js", | |
| 6 | "type": "commonjs", | |
| 7 | "bin": { | |
| 8 | "atomspace-bridge": "./bridge.js" | |
| 9 | }, | |
| 10 | "dependencies": { | |
| 11 | "@hyperswarm/testnet": "^3.1.4", | |
| 12 | "b4a": "^1.6.4", | |
| 13 | "compact-encoding": "^2.12.0", | |
| 14 | "hypercore-crypto": "^3.3.1", | |
| 15 | "hyperident": "file:../../hyper-fleeky/repos/hyperident", | |
| 16 | "hyperswarm": "^4.4.1", | |
| 17 | "protomux": "^3.4.0" | |
| 18 | } | |
| 19 | } |
| … | … | @@ -0,0 +1,156 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | 'use strict' | |
| 3 | ||
| 4 | // Full bridge integration test: | |
| 5 | // 1. Start a mock CogServer (TCP echo server on 17001) | |
| 6 | // 2. Start bridge server with testnet | |
| 7 | // 3. Start bridge client pointing to server | |
| 8 | // 4. Connect to client proxy port | |
| 9 | // 5. Verify data flows through | |
| 10 | ||
| 11 | const net = require('net') | |
| 12 | const { spawn } = require('child_process') | |
| 13 | const path = require('path') | |
| 14 | const crypto = require('hypercore-crypto') | |
| 15 | const b4a = require('b4a') | |
| 16 | ||
| 17 | const BRIDGE = path.join(__dirname, 'bridge.js') | |
| 18 | const COG_PORT = 17099 // Mock CogServer | |
| 19 | const PROXY_PORT = 17098 // Client proxy | |
| 20 | const TEST_SEED = b4a.toString(crypto.randomBytes(32), 'hex') | |
| 21 | ||
| 22 | async function test () { | |
| 23 | console.log('=== Bridge Integration Test ===\n') | |
| 24 | ||
| 25 | // Step 1: Mock CogServer — echoes with "OK: " prefix | |
| 26 | const mockCog = net.createServer((sock) => { | |
| 27 | console.log('[mock-cog] Client connected') | |
| 28 | sock.on('data', (d) => { | |
| 29 | console.log('[mock-cog] Received:', d.toString().trim()) | |
| 30 | sock.write('OK: ' + d.toString()) | |
| 31 | }) | |
| 32 | sock.on('error', () => {}) | |
| 33 | }) | |
| 34 | ||
| 35 | await new Promise((resolve) => mockCog.listen(COG_PORT, resolve)) | |
| 36 | console.log('[mock-cog] Listening on port', COG_PORT) | |
| 37 | ||
| 38 | // Step 2: Start bridge server with testnet | |
| 39 | const server = spawn('node', [ | |
| 40 | BRIDGE, 'server', | |
| 41 | '--port', String(COG_PORT), | |
| 42 | '--seed', TEST_SEED, | |
| 43 | '--testnet', '.test-testnet.json' | |
| 44 | ], { cwd: __dirname }) | |
| 45 | ||
| 46 | let serverKey = null | |
| 47 | ||
| 48 | server.stdout.on('data', (d) => { | |
| 49 | const line = d.toString() | |
| 50 | process.stdout.write('[server] ' + line) | |
| 51 | const m = line.match(/Public key: ([a-f0-9]{64})/) | |
| 52 | if (m) serverKey = m[1] | |
| 53 | }) | |
| 54 | server.stderr.on('data', (d) => process.stderr.write('[server-err] ' + d.toString())) | |
| 55 | ||
| 56 | // Wait for server to announce on DHT | |
| 57 | await new Promise((resolve) => { | |
| 58 | const check = setInterval(() => { | |
| 59 | if (serverKey) { | |
| 60 | clearInterval(check) | |
| 61 | resolve() | |
| 62 | } | |
| 63 | }, 200) | |
| 64 | }) | |
| 65 | ||
| 66 | // Extra wait for DHT announce | |
| 67 | await new Promise(r => setTimeout(r, 2000)) | |
| 68 | ||
| 69 | console.log('\n[test] Server key:', serverKey) | |
| 70 | ||
| 71 | // Step 3: Start bridge client using testnet | |
| 72 | const client = spawn('node', [ | |
| 73 | BRIDGE, 'client', serverKey, | |
| 74 | '--proxy-port', String(PROXY_PORT), | |
| 75 | '--testnet', '.test-testnet.json' | |
| 76 | ], { cwd: __dirname }) | |
| 77 | ||
| 78 | let clientReady = false | |
| 79 | ||
| 80 | client.stdout.on('data', (d) => { | |
| 81 | const line = d.toString() | |
| 82 | process.stdout.write('[client] ' + line) | |
| 83 | if (line.includes('Channel ready') || line.includes('Connected to server')) { | |
| 84 | clientReady = true | |
| 85 | } | |
| 86 | }) | |
| 87 | client.stderr.on('data', (d) => process.stderr.write('[client-err] ' + d.toString())) | |
| 88 | ||
| 89 | // Wait for client to connect to server | |
| 90 | await new Promise((resolve) => { | |
| 91 | const check = setInterval(() => { | |
| 92 | if (clientReady) { | |
| 93 | clearInterval(check) | |
| 94 | resolve() | |
| 95 | } | |
| 96 | }, 200) | |
| 97 | // Timeout after 15s | |
| 98 | setTimeout(() => { | |
| 99 | clearInterval(check) | |
| 100 | resolve() | |
| 101 | }, 15000) | |
| 102 | }) | |
| 103 | ||
| 104 | if (!clientReady) { | |
| 105 | console.log('\n[test] FAILED: Client never connected to server') | |
| 106 | cleanup() | |
| 107 | return | |
| 108 | } | |
| 109 | ||
| 110 | await new Promise(r => setTimeout(r, 1000)) | |
| 111 | ||
| 112 | // Step 4: Connect to proxy port and send data | |
| 113 | console.log('\n[test] Connecting to proxy port', PROXY_PORT) | |
| 114 | const conn = net.connect(PROXY_PORT, '127.0.0.1') | |
| 115 | ||
| 116 | const result = await new Promise((resolve, reject) => { | |
| 117 | conn.on('connect', () => { | |
| 118 | console.log('[test] Connected to proxy — sending test data') | |
| 119 | conn.write('(cog-version)\n') | |
| 120 | }) | |
| 121 | ||
| 122 | conn.on('data', (d) => { | |
| 123 | console.log('[test] Received:', d.toString().trim()) | |
| 124 | resolve(d.toString()) | |
| 125 | }) | |
| 126 | ||
| 127 | conn.on('error', (err) => { | |
| 128 | console.log('[test] Connection error:', err.message) | |
| 129 | reject(err) | |
| 130 | }) | |
| 131 | ||
| 132 | setTimeout(() => resolve(null), 5000) | |
| 133 | }) | |
| 134 | ||
| 135 | // Step 5: Verify | |
| 136 | if (result && result.includes('OK:')) { | |
| 137 | console.log('\n✓ SUCCESS — data flowed through P2P bridge!') | |
| 138 | } else { | |
| 139 | console.log('\n✗ FAILED — no response from mock CogServer through bridge') | |
| 140 | } | |
| 141 | ||
| 142 | function cleanup () { | |
| 143 | conn && conn.destroy() | |
| 144 | server.kill('SIGINT') | |
| 145 | client.kill('SIGINT') | |
| 146 | mockCog.close() | |
| 147 | setTimeout(() => process.exit(0), 500) | |
| 148 | } | |
| 149 | ||
| 150 | cleanup() | |
| 151 | } | |
| 152 | ||
| 153 | test().catch(err => { | |
| 154 | console.error('Test error:', err) | |
| 155 | process.exit(1) | |
| 156 | }) |
| … | … | @@ -0,0 +1,78 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | 'use strict' | |
| 3 | ||
| 4 | // Minimal same-machine Hyperswarm connectivity test | |
| 5 | // Tests the fix: testnet + flushed() | |
| 6 | ||
| 7 | const Hyperswarm = require('hyperswarm') | |
| 8 | const createTestnet = require('@hyperswarm/testnet') | |
| 9 | const crypto = require('hypercore-crypto') | |
| 10 | const b4a = require('b4a') | |
| 11 | ||
| 12 | async function test () { | |
| 13 | console.log('Creating testnet with 3 DHT bootstrap nodes...') | |
| 14 | const testnet = await createTestnet(3) | |
| 15 | console.log('Testnet ready:', testnet.bootstrap.map(n => n.host + ':' + n.port)) | |
| 16 | ||
| 17 | const topic = crypto.hash(b4a.from('atomspace-bridge-test')) | |
| 18 | ||
| 19 | // Server swarm | |
| 20 | const server = new Hyperswarm({ bootstrap: testnet.bootstrap }) | |
| 21 | server.on('connection', (conn, info) => { | |
| 22 | console.log('[server] Got connection from', b4a.toString(info.publicKey, 'hex').slice(0, 12)) | |
| 23 | conn.on('data', d => console.log('[server] Received:', d.toString())) | |
| 24 | conn.write('hello from server') | |
| 25 | conn.on('error', () => {}) | |
| 26 | }) | |
| 27 | ||
| 28 | // Client swarm | |
| 29 | const client = new Hyperswarm({ bootstrap: testnet.bootstrap }) | |
| 30 | client.on('connection', (conn, info) => { | |
| 31 | console.log('[client] Got connection to', b4a.toString(info.publicKey, 'hex').slice(0, 12)) | |
| 32 | conn.on('data', d => console.log('[client] Received:', d.toString())) | |
| 33 | conn.write('hello from client') | |
| 34 | conn.on('error', () => {}) | |
| 35 | }) | |
| 36 | ||
| 37 | // Server: join + wait for DHT announce | |
| 38 | console.log('\nServer joining topic...') | |
| 39 | const t0 = Date.now() | |
| 40 | const discovery = server.join(topic, { server: true, client: false }) | |
| 41 | await discovery.flushed() | |
| 42 | console.log('Server announced on DHT (' + (Date.now() - t0) + 'ms)') | |
| 43 | ||
| 44 | // Client: join + lookup | |
| 45 | console.log('Client joining topic...') | |
| 46 | const t1 = Date.now() | |
| 47 | client.join(topic, { server: false, client: true }) | |
| 48 | await client.flush() | |
| 49 | console.log('Client connected (' + (Date.now() - t1) + 'ms)') | |
| 50 | ||
| 51 | // Wait a beat for both connection events to fire | |
| 52 | await new Promise(r => setTimeout(r, 500)) | |
| 53 | ||
| 54 | console.log('\nServer connections:', server.connections.size) | |
| 55 | console.log('Client connections:', client.connections.size) | |
| 56 | ||
| 57 | if (server.connections.size > 0 && client.connections.size > 0) { | |
| 58 | console.log('\n✓ SUCCESS — peers connected!') | |
| 59 | } else { | |
| 60 | console.log('\n✗ FAILED — no connections') | |
| 61 | } | |
| 62 | ||
| 63 | // Give time for data exchange | |
| 64 | await new Promise(r => setTimeout(r, 500)) | |
| 65 | ||
| 66 | // Cleanup | |
| 67 | await server.destroy() | |
| 68 | await client.destroy() | |
| 69 | await testnet.destroy() | |
| 70 | ||
| 71 | console.log('\nDone.') | |
| 72 | process.exit(0) | |
| 73 | } | |
| 74 | ||
| 75 | test().catch(err => { | |
| 76 | console.error('Test failed:', err) | |
| 77 | process.exit(1) | |
| 78 | }) |
| … | … | @@ -0,0 +1,77 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | 'use strict' | |
| 3 | ||
| 4 | // Test same-machine connectivity on PRODUCTION DHT | |
| 5 | // with the flushed() fix applied | |
| 6 | ||
| 7 | const Hyperswarm = require('hyperswarm') | |
| 8 | const crypto = require('hypercore-crypto') | |
| 9 | const b4a = require('b4a') | |
| 10 | ||
| 11 | async function test () { | |
| 12 | const topic = crypto.hash(b4a.from('atomspace-bridge-test-' + Date.now())) | |
| 13 | ||
| 14 | console.log('Topic:', b4a.toString(topic, 'hex').slice(0, 24) + '...') | |
| 15 | console.log('Using PRODUCTION DHT (no testnet)\n') | |
| 16 | ||
| 17 | // Server swarm | |
| 18 | const server = new Hyperswarm() | |
| 19 | let serverConnected = false | |
| 20 | server.on('connection', (conn, info) => { | |
| 21 | serverConnected = true | |
| 22 | console.log('[server] Got connection from', b4a.toString(info.publicKey, 'hex').slice(0, 12)) | |
| 23 | conn.on('data', d => console.log('[server] Received:', d.toString())) | |
| 24 | conn.write('hello from server') | |
| 25 | conn.on('error', () => {}) | |
| 26 | }) | |
| 27 | ||
| 28 | // Client swarm | |
| 29 | const client = new Hyperswarm() | |
| 30 | let clientConnected = false | |
| 31 | client.on('connection', (conn, info) => { | |
| 32 | clientConnected = true | |
| 33 | console.log('[client] Got connection to', b4a.toString(info.publicKey, 'hex').slice(0, 12)) | |
| 34 | conn.on('data', d => console.log('[client] Received:', d.toString())) | |
| 35 | conn.write('hello from client') | |
| 36 | conn.on('error', () => {}) | |
| 37 | }) | |
| 38 | ||
| 39 | // Server: join + wait for DHT announce (THE FIX) | |
| 40 | console.log('Server: joining topic + waiting for flushed()...') | |
| 41 | const t0 = Date.now() | |
| 42 | const discovery = server.join(topic, { server: true, client: false }) | |
| 43 | await discovery.flushed() | |
| 44 | const announceMs = Date.now() - t0 | |
| 45 | console.log('Server: DHT announce complete (' + announceMs + 'ms)') | |
| 46 | console.log('Server: DHT bootstrapped') | |
| 47 | ||
| 48 | // Client: join + wait for connection | |
| 49 | console.log('\nClient: joining topic + flushing...') | |
| 50 | const t1 = Date.now() | |
| 51 | client.join(topic, { server: false, client: true }) | |
| 52 | await client.flush() | |
| 53 | const lookupMs = Date.now() - t1 | |
| 54 | console.log('Client: flush complete (' + lookupMs + 'ms)') | |
| 55 | ||
| 56 | // Wait for connection events | |
| 57 | await new Promise(r => setTimeout(r, 2000)) | |
| 58 | ||
| 59 | console.log('\nServer connections:', server.connections.size, '(event fired:', serverConnected + ')') | |
| 60 | console.log('Client connections:', client.connections.size, '(event fired:', clientConnected + ')') | |
| 61 | ||
| 62 | if (serverConnected && clientConnected) { | |
| 63 | console.log('\n✓ SUCCESS — production DHT works with flushed()!') | |
| 64 | console.log(' Announce: ' + announceMs + 'ms, Lookup: ' + lookupMs + 'ms') | |
| 65 | } else { | |
| 66 | console.log('\n✗ FAILED — need testnet for same-machine connections') | |
| 67 | } | |
| 68 | ||
| 69 | await server.destroy() | |
| 70 | await client.destroy() | |
| 71 | process.exit(0) | |
| 72 | } | |
| 73 | ||
| 74 | test().catch(err => { | |
| 75 | console.error('Test failed:', err) | |
| 76 | process.exit(1) | |
| 77 | }) |