{"id":27447357,"url":"https://github.com/feross/bittorrent-dht","last_synced_at":"2025-04-15T06:01:36.307Z","repository":{"id":11745235,"uuid":"14274230","full_name":"webtorrent/bittorrent-dht","owner":"webtorrent","description":"🕸 Simple, robust, BitTorrent DHT implementation","archived":false,"fork":false,"pushed_at":"2025-02-16T22:52:43.000Z","size":658,"stargazers_count":1243,"open_issues_count":20,"forks_count":205,"subscribers_count":46,"default_branch":"master","last_synced_at":"2025-04-11T01:02:15.569Z","etag":null,"topics":["bittorrent","bittorrent-dht","dht","dht-protocol","javascript","nodejs","p2p","peer","torrent","webtorrent"],"latest_commit_sha":null,"homepage":"https://webtorrent.io","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/webtorrent.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.md","dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["webtorrent","feross"]}},"created_at":"2013-11-10T10:02:45.000Z","updated_at":"2025-03-29T13:13:22.000Z","dependencies_parsed_at":"2023-02-17T01:31:31.937Z","dependency_job_id":"ed9633d3-8dda-40a0-aeb5-fd5becb0a0ff","html_url":"https://github.com/webtorrent/bittorrent-dht","commit_stats":{"total_commits":629,"total_committers":46,"mean_commits":"13.673913043478262","dds":0.4133545310015898,"last_synced_commit":"74638d731b66ebfe6b7508a0be8afc8e8075179c"},"previous_names":["feross/bittorrent-dht"],"tags_count":136,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webtorrent%2Fbittorrent-dht","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webtorrent%2Fbittorrent-dht/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webtorrent%2Fbittorrent-dht/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webtorrent%2Fbittorrent-dht/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/webtorrent","download_url":"https://codeload.github.com/webtorrent/bittorrent-dht/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249016625,"owners_count":21198832,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bittorrent","bittorrent-dht","dht","dht-protocol","javascript","nodejs","p2p","peer","torrent","webtorrent"],"created_at":"2025-04-15T06:01:23.780Z","updated_at":"2025-04-15T06:01:36.259Z","avatar_url":"https://github.com/webtorrent.png","language":"JavaScript","funding_links":["https://github.com/sponsors/webtorrent","https://github.com/sponsors/feross"],"categories":["Modules"],"sub_categories":[],"readme":"# bittorrent-dht [![ci][ci-image]][ci-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url]\n\n[ci-image]: https://github.com/webtorrent/bittorrent-dht/actions/workflows/ci.yml/badge.svg?branch=master\n[ci-url]: https://github.com/webtorrent/bittorrent-dht/actions/workflows/ci.yml\n[npm-image]: https://img.shields.io/npm/v/bittorrent-dht.svg\n[npm-url]: https://npmjs.org/package/bittorrent-dht\n[downloads-image]: https://img.shields.io/npm/dm/bittorrent-dht.svg\n[downloads-url]: https://npmjs.org/package/bittorrent-dht\n[standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg\n[standard-url]: https://standardjs.com\n\n### Simple, robust, BitTorrent DHT implementation\n\nNode.js implementation of the [BitTorrent DHT protocol](http://www.bittorrent.org/beps/bep_0005.html). BitTorrent DHT is the main peer discovery layer for BitTorrent, which allows for trackerless torrents. DHTs are awesome!\n\nThis module is used by [WebTorrent](http://webtorrent.io).\n\n### features\n\n- complete implementation of the DHT protocol in JavaScript\n- follows [the spec](http://www.bittorrent.org/beps/bep_0005.html)\n- supports [BEP44](http://www.bittorrent.org/beps/bep_0044.html) for storing arbitrary data in the DHT\n- robust and well-tested\n  - Comprehensive, fully-offline test suite\n  - Used by [WebTorrent](http://webtorrent.io), [peerflix](https://www.npmjs.com/package/peerflix), and [Playback](https://mafintosh.github.io/playback/)\n- efficient recursive lookup algorithm minimizes UDP traffic\n- supports multiple, concurrent lookups using the same routing table\n\nAlso see [bittorrent-tracker](https://www.npmjs.com/package/bittorrent-tracker).\n\n### install\n\n```\nnpm install bittorrent-dht\n```\n\n### example\n\n```\nnpm install magnet-uri\n```\n\n```js\nimport DHT from 'bittorrent-dht'\nimport magnet from 'magnet-uri'\n\nconst uri = 'magnet:?xt=urn:btih:e3811b9539cacff680e418124272177c47477157'\nconst parsed = magnet(uri)\n\nconsole.log(parsed.infoHash) // 'e3811b9539cacff680e418124272177c47477157'\n\nconst dht = new DHT()\n\ndht.listen(20000, function () {\n  console.log('now listening')\n})\n\ndht.on('peer', function (peer, infoHash, from) {\n  console.log('found potential peer ' + peer.host + ':' + peer.port + ' through ' + from.address + ':' + from.port)\n})\n\n// find peers for the given torrent info hash\ndht.lookup(parsed.infoHash)\n\n```\n\n### api\n\n#### `dht = new DHT([opts])`\n\nCreate a new `dht` instance.\n\nIf `opts` is specified, then the default options (shown below) will be overridden.\n\n``` js\n{\n  nodeId: '',      // 160-bit DHT node ID (Buffer or hex string, default: randomly generated)\n  bootstrap: [],   // bootstrap servers (default: router.bittorrent.com:6881, router.utorrent.com:6881, dht.transmissionbt.com:6881)\n  host: false,     // host of local peer, if specified then announces get added to local table (String, disabled by default)\n  concurrency: 16, // k-rpc option to specify maximum concurrent UDP requests allowed (Number, 16 by default)\n  hash: Function,  // custom hash function to use (Function, SHA1 by default),\n  krpc: krpc(),     // optional k-rpc instance\n  timeBucketOutdated: 900000, // check buckets every 15min\n  maxAge: Infinity  // optional setting for announced peers to time out\n}\n```\n\nTo use `dht_store`, set `opts.verify` to an ed25519 supercop/ref10\nimplementation. `opts.verify(signature, value, publicKey)` should return a\nboolean whether the `signature` and value `buffers` were generated by the\n`publicKey`.\n\nFor example, for `dht_store` you can do:\n\n``` js\nimport ed from 'ed25519-supercop'\nconst dht = new DHT({ verify: ed.verify })\n```\n\n#### `dht.lookup(infoHash, [callback])`\n\nFind peers for the given info hash.\n\nThis does a recursive lookup in the DHT. Potential peers that are discovered are emitted\nas `peer` events. See the `peer` event below for more info.\n\n`infoHash` can be a string or Buffer. `callback` is called when the recursive lookup has\nterminated, and is called with two paramaters. The first is an `Error` or null. The second\nis the number of nodes found that had peers. You usually don't need to use this info and\ncan simply listen for `peer` events.\n\nReturns an `abort()` function that would allow us to abort the query.\n\n#### `dht.listen([port], [address], [onlistening])`\n\nMake the DHT listen on the given `port`. If `port` is undefined, an available port is\nautomatically picked.\n\nIf `address` is undefined, the DHT will try to listen on all addresses.\n\nIf `onlistening` is defined, it is attached to the `listening` event.\n\n\n#### `dht.address()`\n\nReturns an object containing the address information for the listening socket of the DHT.\nThis object contains `address`, `family` and `port` properties.\n\n\n#### `dht.announce(infoHash, [port], [callback])`\n\nAnnounce that the peer, controlling the querying node, is downloading a torrent on a port.\n\nIf you omit `port` the implied port option will be set and other peers will use the public\ndht port as your announced port.\n\nIf `dht.announce` is called soon (\u003c 5 minutes) after `dht.lookup`, then the routing table\ngenerated during the lookup can be re-used, because the \"tokens\" sent by each node will\nstill be valid.\n\nIf `dht.announce` is called and there is no cached routing table, then a `dht.lookup` will\nfirst be performed to discover relevant nodes and get valid \"tokens\" from each of them.\nThis will take longer.\n\nA \"token\" is an opaque value that must be presented for a node to announce that its\ncontrolling peer is downloading a torrent. It must present the token received from the\nsame queried node in a recent query for peers. This is to prevent malicious hosts from\nsigning up other hosts for torrents. **All token management is handled internally by this\nmodule.**\n\n`callback` will be called when the announce operation has completed, and is called with\na single parameter that is an `Error` or null.\n\n\n#### `arr = dht.toJSON()`\n\nReturns the current state of the DHT, including DHT nodes and BEP44 values.\n\n```json\n{\n  \"nodes\": [],\n  \"values\": {}\n}\n```\n\nThe DHT nodes, in particular, are useful for persisting the DHT to disk between restarts\nof a BitTorrent client (as recommended by the spec). Each node in the array is an object\nwith `host` (string) and `port` (number) properties.\n\nTo restore the DHT nodes when instantiating a new `DHT` object, simply loop over the nodes in the array and add them with the `addNode` method.\n\n```js\nconst dht1 = new DHT()\n\n// some time passes ...\n\n// destroy the dht\nconst nodes = dht1.toJSON().nodes\ndht1.destroy()\n\n// some time passes ...\n\n// initialize a new dht with the same routing table as the first\nconst dht2 = new DHT()\n\nnodes.forEach(function (node) {\n  dht2.addNode(node)\n})\n```\n\n\n#### `dht.addNode(node)`\n\nManually add a node to the DHT routing table. If there is space in the routing table (or\nan unresponsive node can be evicted to make space), the node will be added. If not, the\nnode will not be added. This is useful to call when a peer wire sends a `PORT` message to\nshare their DHT port.\n\nA node should look like this:\n\n``` js\n{\n  host: nodeHost,\n  port: nodePort\n}\n```\n\n#### `dht.destroy([callback])`\n\nDestroy the DHT. Closes the socket and cleans up large data structure resources.\n\n#### `dht.put(opts, callback)`\n\nWrite an arbitrary payload to the DHT.\n([BEP 44](http://bittorrent.org/beps/bep_0044.html)).\n\nFor all requests, you must specify:\n\n* `opts.v` - a buffer payload to write, less than 1000 bytes\n\nIf you only specify `opts.v`, the content is considered immutable and the hash\nwill just be the hash of the content.\n\nHere is a simple example of creating some immutable content on the dht:\n\n``` js\nimport DHT from 'bittorrent-dht'\nconst dht = new DHT()\nconst value = Buffer.alloc(200).fill('abc')\n\ndht.put({ v: value }, function (err, hash) {\n  console.error('error=', err)\n  console.log('hash=', hash)\n})\n```\n\nFor mutable content, the hash will be the hash of the public key, `opts.k`.\nThese options are available:\n\n* `opts.k` - ed25519 public key buffer (32 bytes) (REQUIRED)\n* `opts.sign(buf)` - function to generate an ed25519 signature buffer (64 bytes) corresponding to the `opts.k` public key (REQUIRED)\n* `opts.seq` - optional sequence (integer), must monotonically increase (REQUIRED)\n* `opts.cas` - optional previous sequence for compare-and-swap\n* `opts.salt` - optional salt buffer to include (\u003c= 64 bytes) when calculating\n  the hash of the content. You can use a salt to have multiple mutable addresses\n  for the same public key `opts.k`.\n\nNote that bittorrent bep44 uses ed25519. You can use the [ed25519-supercop](https://npmjs.com/package/ed25519-supercop)\npackage to generate the appropriate signatures,\n[bittorrent-dht-store-keypair](https://npmjs.com/package/bittorrent-dht-store-keypair),\n[bittorrent-dht-sodium](https://npmjs.com/package/bittorrent-dht-sodium) or\nfor a more convenient version.\n\nTo make a mutable update, you will need to create an elliptic key and pack\nvalues precisely according to the specification, like so:\n\n``` js\nimport ed from 'bittorrent-dht-sodium'\nconst keypair = ed.keygen()\n\nconst value = Buffer.alloc(200).fill('whatever') // the payload you want to send\nconst opts = {\n  k: keypair.pk,\n  seq: 0,\n  v: value,\n  sign: function (buf) {\n    return ed.sign(buf, keypair.sk)\n  }\n}\n\nimport DHT from 'bittorrent-dht'\nconst dht = new DHT()\n\ndht.put(opts, function (err, hash) {\n  console.error('error=', err)\n  console.log('hash=', hash)\n})\n```\n\nIn either mutable or immutable forms, `callback(error, hash, n)` fires with an\n`error` if no nodes were able to store the `value`. `n` is set the amount of peers\nthat accepted the `put` and `hash`, the location where the mutable or immutable\ncontent can be retrieved (with `dht.get(hash)`).\n\nNote that you should call `.put()` every hour for content that you want to keep\nalive, since nodes may discard data nodes older than 2 hours.\n\nIf you receive a key/value pair and you want to re-add to the dht it to keep it\nalive you can just `put` it again.\n\n``` js\nimport ed from 'bittorrent-dht-sodium'\nconst dht = new DHT({ verify: ed.verify }) // you MUST specify the \"verify\" param if you want to get mutable content, otherwise null will be returned\n\ndht.get(key, function (err, res) {\n  dht.put(res, function () {\n    // re-added the key/value pair\n  })\n})\n```\n\n#### `dht.get(hash, opts, callback)`\n\nRead a data record (created with `.put()`) from the DHT.\n([BEP 44](http://bittorrent.org/beps/bep_0044.html))\n\nGiven `hash`, a hex string or buffer, lookup data content from the DHT, sending the\nresult in `callback(err, res)`.\n\nThese options are available:\n\n* `opts.verify` - override the default ed25519 verification function supplied during DHT instantiation.\n* `opts.salt` - optional salt buffer (if any) that was used to calculate the hash. Must be specified if included in the hash.\n* `opts.cache` - use locally cached response value when available instead of performing a network lookup (defaults to true).\n\n`res` objects are similar to the options objects written to the DHT with\n`.put()`:\n\n* `res.v` - the value put in\n* `res.id` - the node that returned the content\n* `res.k` - the public key (only present for mutable data)\n* `res.sig` - the signature (only present for mutable data)\n* `res.seq` - the sequence (optional, only present for mutable data)\n\n### events\n\n#### `dht.on('ready', function () { ... })`\n\nEmitted when the DHT is fully bootstrapped (i.e. the routing table is sufficiently\npopulated via the bootstrap nodes). Note that it is okay to do lookups before the 'ready'\nevent fires.\n\nNote: If you initialize the DHT with the `{ bootstrap: false }` option, then the 'ready'\nevent will fire on the next tick even if there are not any nodes in the routing table.\nIt is assumed that you will manually populate the routing table with `dht.addNode` if you\npass this option.\n\n\n#### `dht.on('listening', function () { ... })`\n\nEmitted when the DHT is listening.\n\n\n#### `dht.on('peer', function (peer, infoHash, from) { ... })`\n\nEmitted when a potential peer is found. `peer` is of the form `{host, port}`.\n`infoHash` is the torrent info hash of the swarm that the peer belongs to. Emitted\nin response to a `lookup(infoHash)` call.\n\n\n#### `dht.on('error', function (err) { ... })`\n\nEmitted when the DHT has a fatal error.\n\n\n#### internal events\n\n#### `dht.on('node', function (node) { ... })`\n\nEmitted when the DHT finds a new node.\n\n\n#### `dht.on('announce', function (peer, infoHash) { ... })`\n\nEmitted when a peer announces itself in order to be stored in the DHT.\n\n\n#### `dht.on('warning', function (err) { ... })`\n\nEmitted when the DHT gets an unexpected message from another DHT node. This is purely\ninformational.\n\n\n### further reading\n\n- [BitTorrent DHT protocol](http://www.bittorrent.org/beps/bep_0005.html)\n- [Kademlia: A Peer-to-peer Information System Based on the XOR Metric](http://www.ic.unicamp.br/~bit/ensino/mo809_1s13/papers/P2P/Kademlia-%20A%20Peer-to-Peer%20Information%20System%20Based%20on%20the%20XOR%20Metric%20.pdf)\n\n\n### license\n\nMIT. Copyright (c) [Feross Aboukhadijeh](https://feross.org), Mathias Buus, and [WebTorrent, LLC](https://webtorrent.io).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffeross%2Fbittorrent-dht","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffeross%2Fbittorrent-dht","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffeross%2Fbittorrent-dht/lists"}