LogoPear Docs
ReferencesBuilding blocks

HyperDHT

P2P DHT for keyed peer lookup, hole punching, and encrypted direct connections.

stable

HyperDHT is the lower-level keyed connection layer underneath Hyperswarm. Use it when you want to dial a known public key directly or work with raw DHT discovery and record APIs.

Install

npm i hyperdht

Quickstart

import DHT from 'hyperdht'
import { once } from 'node:events'

const serverNode = new DHT()
const clientNode = new DHT()
const keyPair = DHT.keyPair()

const server = serverNode.createServer(socket => {
  socket.end('hello from hyperdht')
})

await server.listen(keyPair)
const socket = clientNode.connect(keyPair.publicKey)

await once(socket, 'open')
socket.write('ping')

for await (const chunk of socket) {
  console.log(chunk.toString())
}

await server.close()
await Promise.all([serverNode.destroy(), clientNode.destroy()])

API Reference

Node setup

new DHT([options])

src

Create a new DHT node.

ParameterTypeDefault
optionsHyperDHTOptions{}

keyPair = DHT.keyPair([seed])

Use this method to generate the required keypair for DHT operations.

  • Returns: an object with {publicKey, secretKey}.

node = DHT.bootstrapper(port, host, [options])

If you want to run your own Hyperswarm network use this method to easily create a bootstrap node.

Servers

node.createServer([options], [onconnection])

src

Create a new server for accepting incoming encrypted P2P connections.

ParameterTypeDescription
optionsCreateServerOptions
onconnectionfunction(NoiseSecretStream): voidShorthand listener for the 'connection' event.
  • Returns: Server — A server object — call server.listen(keyPair) to start accepting connections.
{
  firewall (remotePublicKey, remoteHandshakePayload) {
    // validate if you want a connection from remotePublicKey
    // if you do return false, else return true
    // remoteHandshakePayload contains their ip and some more info
    return true
  }
}

await server.listen(keyPair)

Make the server listen on a keyPair. To connect to this server use keyPair.publicKey as the connect address.

  • Throws:
    • ALREADY_LISTENING if the server is already listening.
    • NODE_DESTROYED if the DHT node has been destroyed.

server.refresh()

Refresh the server, causing it to reannounce its address. This is automatically called on network changes.

Call this to reannounce the server address. HyperDHT also calls it automatically when the network changes.

server.address()

You can also get this info from node.remoteAddress() minus the public key.

  • Returns: an object containing the address of the server:
{
  ;(host, // external IP of the server,
    port, // external port of the server if predictable,
    publicKey) // public key of the server
}

server.on('connection', socket)

Emitted when a new encrypted connection has passed the firewall check.

The socket exposes socket.remotePublicKey, and socket.handshakeHash is a shared identifier for the encrypted session.

server.on('listening')

Emitted when the server is fully listening on a keyPair.

await server.close()

Stop listening.

server.on('close')

Emitted when the server is fully closed.

Clients and sockets

node.connect(remotePublicKey, [options])

src

Connect to a remote server. Similar to createServer this performs UDP holepunching for P2P connectivity.

ParameterTypeDescription
remotePublicKeyBuffer|stringPublic key of the server to connect to (Buffer, hex string, or z-base32 string).
optionsConnectOptions
  • Returns: NoiseSecretStream — An encrypted duplex stream — use socket.on('open', ...) to know when it is ready.

socket.on('open')

Emitted when the encrypted connection has been fully established with the server.

socket.remotePublicKey

The public key of the remote peer.

socket.publicKey

The public key of the local socket.

Peer discovery

node.lookup(topic, [options])

src

Look for peers in the DHT on the given topic. Topic should be a 32 byte buffer (normally a hash of something).

ParameterTypeDefaultDescription
topicBuffer
optionsobject{}Options forwarded to dht-rpc.
  • Returns: QueryStream — An async-iterable query stream whose values are { from, to, peers } objects.
Option
from
to
peers

node.announce(topic, keyPair, [relayAddresses], [options])

src

Announce that you are listening on a key-pair to the DHT under a specific topic.

ParameterTypeDefaultDescription
topicBuffer32-byte topic buffer to announce under.
keyPair{publicKey: Buffer, secretKey: Buffer}The Ed25519 key pair to announce with.
relayAddressesArray<{host: string, port: number}>Up to 3 DHT relay node addresses to include in the announcement.
optionsobject{}Options forwarded to dht-rpc.
  • Returns: QueryStream — An async-iterable query stream (same shape as lookup()).
const topic = DHT.hash(Buffer.from('my-topic'))
const stream = node.announce(topic, keyPair)
await stream.finished()

Servers created with node.createServer() already announce themselves on the key pair they are listening on. Use node.announce() when you also want to publish a topic mapping.

await node.unannounce(topic, keyPair, [options])

src

Unannounce a key-pair.

ParameterTypeDefaultDescription
topicBuffer32-byte topic buffer previously used in announce().
keyPair{publicKey: Buffer, secretKey: Buffer}The Ed25519 key pair to unannounce.
optionsobject{}Options forwarded to dht-rpc.
  • Returns: Promise<void> — Resolves when the unannounce query has completed.
const topic = DHT.hash(Buffer.from('my-topic'))
await node.unannounce(topic, keyPair)

Mutable and immutable records

await node.immutablePut(value, [options])

src

Store an immutable value in the DHT. When successful, the hash of the value is returned.

ParameterTypeDefaultDescription
valueBufferThe value to store (a Buffer).
optionsobject{}Options forwarded to dht-rpc.
const { hash } = await node.immutablePut(Buffer.from('hello world'))
const result = await node.immutableGet(hash)
console.log(result.value.toString()) // 'hello world'

await node.immutableGet(hash, [options])

src

Fetch an immutable value from the DHT. When successful, it returns the value corresponding to the hash.

ParameterTypeDefaultDescription
hashBuffer32-byte hash of the value to fetch (returned by immutablePut()).
optionsobject{}Options forwarded to dht-rpc.
const result = await node.immutableGet(hash)
if (result) console.log(result.value.toString())

await node.mutablePut(keyPair, value, [options])

src

Store a mutable value in the DHT.

ParameterTypeDefaultDescription
keyPair{publicKey: Buffer, secretKey: Buffer}Ed25519 key pair used to sign the value.
valueBufferThe value to store (a Buffer).
optionsMutablePutOptions{}Put options.
const keyPair = DHT.keyPair()
const { seq } = await node.mutablePut(keyPair, Buffer.from('v1'), { seq: 0 })
console.log('stored at seq', seq)

await node.mutableGet(publicKey, [options])

src

Fetch a mutable value from the DHT.

ParameterTypeDefaultDescription
publicKeyBufferEd25519 public key of the key pair used to sign the value with mutablePut().
optionsMutableGetOptions{}Fetch options.
const result = await node.mutableGet(keyPair.publicKey)
if (result) console.log(result.value.toString(), 'seq:', result.seq)

Lifecycle

await node.destroy([options])

src

Fully destroy this DHT node.

ParameterTypeDefaultDescription
optionsobject{}Pass { force: true } to skip waiting for servers to unannounce before closing.
  • Returns: Promise<void> — Resolves when the node is fully shut down.
await node.destroy()
// or, to skip graceful unannounce:
await node.destroy({ force: true })

HyperDHT inherits additional lower-level RPC APIs from dht-rpc. Reach for those when you need custom queries beyond the keyed connection and record helpers above.

Types

HyperDHTOptions

Options for creating a HyperDHT node.

PropertyTypeDefaultDescription
bootstrapArray<string>Bootstrap server addresses ('host:port'). Defaults to the Holepunch public bootstrap nodes.
keyPair{publicKey: Buffer, secretKey: Buffer}Default key pair used for server.listen() and connect().
connectionKeepAlivenumber|false5000Keep-alive interval in ms for all opened sockets. Set false to disable.
randomPunchIntervalnumber20000Minimum ms between random holepunches.

CreateServerOptions

Options for node.createServer().

PropertyTypeDefaultDescription
firewallfunction(Buffer, object): booleanCalled with (remotePublicKey, remoteHandshakePayload). Return true to block, false to allow.
holepunchfunction(number, number, Array, Array): booleanCalled before holepunching begins. Return false to abort.
relayThroughBuffer|Array<Buffer>|function(): Buffer|nullOptionally relay through a specific peer — pass a public key (Buffer), an array of public keys to pick from, or a function returning one.
relayKeepAlivenumber5000Keep-alive interval in ms for the relay socket.

ConnectOptions

Options for node.connect().

PropertyTypeDefaultDescription
nodesArray<{host: string, port: number}>Known DHT nodes close to the remote — speeds up connecting.
relayAddressesArray<{host: string, port: number}>Relay server addresses to hole-punch through when a direct connection cannot be established.
keyPair{publicKey: Buffer, secretKey: Buffer}Key pair to use for this connection. Defaults to node.defaultKeyPair.
relayThroughBuffer|Array<Buffer>|function(): Buffer|nullOptionally relay through a specific peer — pass a public key (Buffer), an array of public keys to pick from, or a function returning one.

MutableGetOptions

Options for node.mutableGet().

PropertyTypeDefaultDescription
seqnumber0Only return values whose seq is >= this number.
latestbooleantrueIf true, scan the whole query and return the highest seq seen.
refreshfunction(object): booleanCalled with the latest result; return true to re-store it, extending its TTL.

MutablePutOptions

Options for node.mutablePut().

PropertyTypeDefaultDescription
seqnumber0Sequence number for this value. Must be greater than the current stored seq to overwrite.
signMutablefunction(number, Buffer, object): Promise<Buffer>Custom signing function. Defaults to the built-in Ed25519 signer.

Errors

Coded errors this module can throw — catch them via err.code.

ErrorThrown when
ALREADY_LISTENINGif the server is already listening.
NODE_DESTROYEDif the DHT node has been destroyed.

See also

On this page