├── .gitignore ├── README.md ├── bin.js ├── dht.js ├── discovery.js ├── package.json └── swarm.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This tool has been deprecated 2 | 3 | Use the bundled clis in each Hyperswarm module instead. 4 | -------------------------------------------------------------------------------- /bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const cmd = process.argv[1] 4 | const op = process.argv[2] 5 | if (!['discovery', 'swarm', 'dht'].includes(op)) { 6 | console.error(`Usage: ${cmd} [command] --help 7 | 8 | Commands: 9 | 10 | discovery ... Interact with the discovery network (DHT and MDNS) 11 | swarm ....... Use the discovery to make connections 12 | dht ......... Start a dht node 13 | 14 | Example: 15 | 16 | hyperswarm discovery --help 17 | `) 18 | process.exit(1) 19 | } 20 | const path = require('path') 21 | const file = path.join(__dirname, `${op}.js`) 22 | 23 | process.argv = [process.argv[0], `${cmd} ${op}`].concat(process.argv.slice(3)) 24 | require(file) 25 | -------------------------------------------------------------------------------- /dht.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const createDht = require('@hyperswarm/dht') 4 | const minimist = require('minimist') 5 | const os = require('os') 6 | const path = require('path') 7 | const fs = require('fs') 8 | const dhtrpc = require('dht-rpc') 9 | 10 | const argv = minimist(process.argv, { 11 | boolean: [ 12 | 'adaptive', 13 | 'verbose', 14 | 'help', 15 | 'json' 16 | ], 17 | string: [ 18 | 'address', 19 | 'id-file', 20 | 'id' 21 | ], 22 | default: { 23 | adaptive: true, 24 | 'id-file': path.join(os.tmpdir(), 'dht-rpc-id'), 25 | verbose: false, 26 | port: 49737, 27 | address: '0.0.0.0' 28 | }, 29 | alias: { 30 | bootstrap: 'b', 31 | verbose: 'V', 32 | address: 'a', 33 | port: 'p', 34 | 'id-file': 'f', 35 | id: 'i', 36 | help: 'h', 37 | json: 'j' 38 | } 39 | }) 40 | 41 | if (argv.help) { 42 | console.error(`Usage: ${process.argv[1]} [options] 43 | 44 | --id, -i [key] ID for this dht node 45 | --id-file, -f [path] Path to store a random id for this node (ignored if id is given) 46 | --port, -p [port] Specify port to listen to (optional, as dht-nodes don't require listening) 47 | --address, -a [addr] Specify address to listen to (optional, only used if port is given) 48 | --json Output all messages as json. 49 | --verbose, -V Print all lookups,announces,unannounces 50 | --bootstrap, -b Specify bootstrap peers (optional) 51 | --no-adaptive Disable adaptive ephemerality 52 | `) 53 | process.exit(1) 54 | } 55 | 56 | const idFile = argv['id-file'] 57 | const id = resolveId(argv.id, idFile) 58 | const adaptive = argv.adaptive 59 | const port = argv.port 60 | const address = argv.address 61 | const bootstrap = argv.boostrap ? [].concat(argv.bootstrap || []) : undefined 62 | const version = require('@hyperswarm/dht/package.json').version 63 | const verbose = argv.verbose 64 | 65 | const dht = createDht({ adaptive: adaptive, ephemeral: adaptive, id, bootstrap }) 66 | 67 | const msg = `DHT version ${version} 68 | 69 | id=${id.toString('hex')} 70 | id-file=${idFile} 71 | port=${port} 72 | address=${address} 73 | adaptive=${adaptive} 74 | bootstrap=${bootstrap || '(default)'} 75 | verbose=${verbose} 76 | ` 77 | 78 | log(msg, { version, id: id.toString('hex'), idFile, port, address, adaptive, bootstrap, verbose }) 79 | 80 | if (adaptive) { 81 | if (!argv.json) log('Running in adaptive mode. Will go persistent once running for ~30 min and holepunchable') 82 | } 83 | 84 | dht.on('listening', function () { 85 | const { address, port, family } = this.socket.address() 86 | log(`Listening on ${address}:${port} (udp,${family})`, { state: 'listening', address, port, family }) 87 | }) 88 | 89 | dht.on('ready', function () { 90 | log('DHT node fully bootstrapped', { state: 'bootstrapped' }) 91 | }) 92 | 93 | dht.on('initial-nodes', function () { 94 | const holepunchable = dht.holepunchable() 95 | const remoteAddress = dht.remoteAddress() 96 | log(holepunchable ? `Network appears holepunchable (remote address is ${remoteAddress.host}:${remoteAddress.port})` : 'Warning: Network does not appear holepunchable', { holepunchable, remoteAddress }) 97 | }) 98 | 99 | dht.on('persistent', function () { 100 | log('DHT appears holepunchable and stable. Now persistent', { state: 'persistent' }) 101 | }) 102 | 103 | dht.on('warning', function (warning) { 104 | log(`Warning: ${warning.message}`, { warning: { message: warning.message, stack: warning.stack } }) 105 | }) 106 | 107 | dht.on('error', function (error) { 108 | log(`Error: ${error.stack}`, { error: { message: error.message, stack: error.stack } }) 109 | process.exit(1) 110 | }) 111 | 112 | dht.listen(port, address) 113 | 114 | if (argv.verbose) { 115 | for (const event of ['announce', 'unannounce', 'lookup']) { 116 | dht.on(event, function (target, peer) { 117 | log(`Received ${event}: ${peer.host}:${peer.port} @ ${target.toString('hex')}`, { event, target: target.toString('hex'), peer }) 118 | }) 119 | } 120 | } 121 | 122 | function log (s, obj) { 123 | if (argv.json) { 124 | console.log(JSON.stringify(obj)) 125 | } else { 126 | console.log(s) 127 | } 128 | } 129 | 130 | function resolveId (id, idFile) { 131 | if (id !== undefined) { 132 | // Skip writing the id to disc as passing in the id is supposed to increase the startup speed. 133 | return Buffer.from(id, 'hex') 134 | } 135 | 136 | if (!fs.existsSync(idFile)) fs.writeFileSync(idFile, dhtrpc.id().slice(0, 32)) 137 | return fs.readFileSync(idFile).slice(0, 32) 138 | } 139 | -------------------------------------------------------------------------------- /discovery.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const discovery = require('@hyperswarm/discovery') 4 | const sodium = require('sodium-native') 5 | const minimist = require('minimist') 6 | const estimate = require('dht-size-up') 7 | 8 | const argv = minimist(process.argv, { 9 | boolean: [ 10 | 'ephemeral', 11 | 'ping', 12 | 'hash' 13 | ], 14 | default: { 15 | ephemeral: true 16 | }, 17 | alias: { 18 | 'find-node': 'f', 19 | ephemeral: 'e', 20 | announce: 'a', 21 | unannounce: 'u', 22 | lookup: 'l', 23 | port: 'p', 24 | hash: 'h', 25 | bootstrap: 'b' 26 | } 27 | }) 28 | 29 | if (!argv.ping && !argv.announce && !argv.unannounce && !argv.lookup && !argv['find-node'] && !argv['estimate-dht-size']) { 30 | console.error(`Usage: ${process.argv[1]} [options] 31 | 32 | --announce, -a [key] 33 | --unannounce, -u [key] 34 | --lookup, -l [key] 35 | --port, -p [port] Specify port to announce 36 | --local-port Specify local port 37 | --hash, -h Autohash the key 38 | --no-ephemeral Host other peoples keys/values 39 | --bootstrap, -b Specify bootstrap peers 40 | --estimate-dht-size Estimate number of nodes in DHT 41 | `) 42 | process.exit(1) 43 | } 44 | 45 | const d = discovery({ 46 | ephemeral: argv.ephemeral, 47 | bootstrap: argv.boostrap ? [].concat(argv.bootstrap || []) : undefined 48 | }) 49 | 50 | const localPort = argv['local-port'] || argv.port || 0 51 | 52 | if (argv['find-node']) { 53 | const k = argv['find-node'] !== true ? Buffer.from(argv['find-node'], 'hex') : Buffer.alloc(32) 54 | console.log('Looking for ' + k.toString('hex')) 55 | const t = Date.now() 56 | d.dht.query('_find_node', k) 57 | .on('data', function (data) { 58 | if (data.node.id) console.log('Found: ' + data.node.id.toString('hex') + ' ' + data.node.host + ':' + data.node.port + ' (' + (Date.now() - t) + 'ms)') 59 | }) 60 | .on('end', function () { 61 | if (!argv.announce && !argv.lookup) process.exit() 62 | }) 63 | } 64 | 65 | if (argv.ping) { 66 | d.ping(function (_, nodes) { 67 | console.error('[pong]') 68 | console.log(nodes) 69 | if (!argv.announce && !argv.lookup) process.exit() 70 | }) 71 | } 72 | 73 | if (argv.announce) { 74 | console.error('[announcing key]') 75 | const topic = d.announce(key(), { port: argv.port || 0, localPort }) 76 | .on('peer', function (peer) { 77 | if (argv.lookup) console.log(peer) 78 | }) 79 | 80 | process.once('SIGINT', function () { 81 | console.error('[unannouncing key ...]') 82 | topic.once('close', () => process.exit()) 83 | topic.destroy() 84 | }) 85 | } else if (argv.lookup) { 86 | d.lookup(key()) 87 | .on('peer', function (peer) { 88 | console.log(peer) 89 | }) 90 | } 91 | 92 | if (argv['estimate-dht-size']) { 93 | estimate(d.dht, function (err, size, n, q) { 94 | if (err) console.error(err) 95 | else console.log(`Sampled ${n} nodes over ${q} queries. Estimated DHT size is ${size}.`) 96 | }) 97 | } 98 | 99 | function key () { 100 | const k = argv.key || argv.announce || argv.unannounce || argv.lookup 101 | if (argv.hash) return hash(Buffer.from(k)) 102 | return Buffer.from(k, 'hex') 103 | } 104 | 105 | function hash (data) { 106 | const out = Buffer.allocUnsafe(32) 107 | sodium.crypto_generichash(out, data) 108 | return out 109 | } 110 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hyperswarm/cli", 3 | "version": "1.4.3", 4 | "description": "CLI tool to interact with Hyperswarm", 5 | "bin": { 6 | "hyperswarm": "./bin.js", 7 | "hyperswarm-discovery": "./discovery.js", 8 | "hyperswarm-swarm": "./swarm.js", 9 | "hyperswarm-dht": "./dht.js" 10 | }, 11 | "dependencies": { 12 | "@hyperswarm/dht": "^3.6.2", 13 | "@hyperswarm/discovery": "^1.11.4", 14 | "dht-size-up": "^1.0.0", 15 | "hyperswarm": "^2.13.0", 16 | "minimist": "^1.2.5", 17 | "pump": "^3.0.0", 18 | "sodium-native": "^3.1.1" 19 | }, 20 | "devDependencies": { 21 | "standard": "^14.3.3" 22 | }, 23 | "scripts": { 24 | "test": "standard" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/hyperswarm/cli.git" 29 | }, 30 | "keywords": [ 31 | "hyperswarm", 32 | "cli" 33 | ], 34 | "author": "Mathias Buus (@mafintosh)", 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/hyperswarm/cli/issues" 38 | }, 39 | "homepage": "https://github.com/hyperswarm/cli#readme" 40 | } 41 | -------------------------------------------------------------------------------- /swarm.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const hyperswarm = require('hyperswarm') 4 | const sodium = require('sodium-native') 5 | const minimist = require('minimist') 6 | const pump = require('pump') 7 | 8 | const argv = minimist(process.argv, { 9 | boolean: [ 10 | 'hash' 11 | ], 12 | alias: { 13 | hash: 'h', 14 | announce: 'a', 15 | lookup: 'l', 16 | key: 'k' 17 | } 18 | }) 19 | 20 | if (!argv.lookup && !argv.announce) { 21 | console.error(`Usage: ${process.argv[1]} [options] 22 | 23 | --announce, -a 24 | --lookup, -l 25 | --hash, -h 26 | --key, -k 27 | `) 28 | process.exit(1) 29 | } 30 | 31 | const swarm = hyperswarm() 32 | 33 | let id = 0 34 | 35 | swarm.on('connection', function (connection, info) { 36 | const i = id++ 37 | console.error('[connection start id=' + i + ' type=' + info.type + ' client=' + info.client + ']') 38 | pump(process.stdin, connection, process.stdout, function (err) { 39 | console.error('[connection end id=' + i + ' err=' + (err || null) + ']') 40 | }) 41 | }) 42 | 43 | swarm.join(key(), { 44 | lookup: argv.lookup, 45 | announce: argv.announce 46 | }) 47 | 48 | process.once('SIGINT', function () { 49 | console.error('[swarm destroying ...]') 50 | swarm.destroy() 51 | }) 52 | 53 | function key () { 54 | const k = argv.key || argv.announce || argv.lookup 55 | if (argv.hash) return hash(Buffer.from(k)) 56 | return Buffer.from(k, 'hex') 57 | } 58 | 59 | function hash (data) { 60 | const out = Buffer.allocUnsafe(32) 61 | sodium.crypto_generichash(out, data) 62 | return out 63 | } 64 | --------------------------------------------------------------------------------