P2P Docs
ReferenceHelpers

blind-peer

Server library that stores and serves Hypercores for other peers without reading them: embed one in your process, set its limits and observe it.

Documented against v3.15.1
stable

blind-peer is the server half of blind peering. A blind peer accepts requests from blind-peering clients to keep Hypercores available, replicates and stores them, and serves them to other peers, without holding the keys that decrypt their content. To run one as a standalone process, use pear blind-peer start or the blind-peer-cli package. Both wrap this library. Use this page when you embed a blind peer in your own process, and for the limits, events, and metrics that apply either way. For upstream source and releases, see the blind-peer repository.

Install

npm i blind-peer

Quickstart

import BlindPeer from 'blind-peer'

const blindPeer = new BlindPeer('./blind-peer-storage', {
  maxBytes: 10_000_000_000 // keep about 10 GB
})

blindPeer.on('add-core', (entry) => {
  console.log('keeping a core, priority', entry.priority)
})

await blindPeer.listen()

// Clients put this key in `blindPeers`
console.log(blindPeer.publicKey.toString('hex'))

API Reference

Constructor and lifecycle

new BlindPeer(rocks[, opts])

src

Create a blind peer. Nothing opens until you call ready() or listen().

ParameterTypeDefaultDescription
rocksstring|RocksDB—A storage directory, or a rocksdb-native instance. The blind peer keeps its database there, and its Corestore too unless you pass store.
optsBlindPeerOptions{}Server options.

await blindPeer.ready()

Open the storage, the database and the store, and create the swarm if you didn't pass one. listen() calls it for you. blindPeer.opened and blindPeer.closed report the state. Getters such as publicKey need the blind peer to be open.

await blindPeer.listen()

src

Open the blind peer if needed and start accepting connections.

await blindPeer.close()

Stop accepting requests, write the database, and close what the blind peer owns: the swarm, store, and wakeup it created, plus the RocksDB handle, even when you passed it in. A swarm, store or wakeup you passed in stays open.

Properties

blindPeer.publicKey

src

The key clients put in blindPeers: the public key of the blind peer's swarm. When the blind peer creates its own swarm, the key pair comes from a seed stored in its database, so the key stays the same across restarts of the same storage.

  • Returns: Buffer
  • Throws: a TypeError before the blind peer is open.

blindPeer.encryptionPublicKey

src

The public key of the encryption key pair that the blind peer keeps in its database.

blindPeer.digest

src

What the blind peer stores, as of the last database write.

blindPeer.nrAnnouncedCores

src

How many announced cores the blind peer has joined the swarm for.

  • Returns: number

blindPeer.stats

src

Counters since the process started. They are also the source of the Prometheus gauges.

  • Returns: object
CounterCounts
addCoresRxadd-cores requests received.
coresAddedCores added or activated by requests.
activationsCore activations.
activatedReplicationsReplication sessions opened for requests.
wakeupsWakeups received for a core.
muxerPairedblind-peer-muxer sessions paired.
muxerErrorsFailures handling add-cores requests.
notificationsRxNotification requests received.
notificationsSentNotifications forwarded to a push gateway.
notificationErrorsNotification requests that failed.
referrerRateLimitedRequests dropped by perReferrerRateLimitParams.
coreTrackersCreated / coreTrackersDestroyedCore trackers created / destroyed.
coreResetDownloadCores whose cleared blocks were scheduled for download again by a priority 2 request.
bytesGcdBytes cleared by garbage collection.
gc.coresGcdCores cleared.
gc.firstTimeCoresGcdCores cleared for the first time.
gc.prio0Gcd / gc.prio1Gcd / gc.prio2GcdCores cleared, by priority.

blindPeer.store / blindPeer.swarm / blindPeer.wakeup

The Corestore, Hyperswarm and protomux-wakeup instance the blind peer uses. They're the ones you passed in or the ones it created. A store it creates is passive unless you set activeCorestore, and it's opened with alwaysLatestBlock on, so its replicator asks for the latest block in every upgrade request. A store you pass in is used as given.

Trusted peers

blindPeer.addTrustedPubKey(key)

src

Trust a peer. The trustedPubKeys option does the same at construction. See announce and trusted peers for what trust allows.

ParameterTypeDescription
keyBuffer|stringThe peer's public key, as a Buffer or a hex or z32 string. It's the key pair of the peer's HyperDHT node. For a blind-peering client that's dht.defaultKeyPair.publicKey. On a Pear machine, pear identity blind-peer-client prints it.
blindPeer.addTrustedPubKey(clientDht.defaultKeyPair.publicKey)

Maintenance

await blindPeer.flush()

src

Write pending database updates, and run garbage collection first when the blind peer is over budget. It runs every 10 seconds and right after an add request that records a new core, and close() runs it too. It doesn't throw: a failure emits flush-error.

blindPeer.needsGc()

src

true when digest.bytesAllocated has reached maxBytes.

  • Returns: boolean

blindPeer.getActiveReplicationSessions()

src

How many cores the blind peer is replicating for connected clients right now. A core counts once for each connection it's replicated over.

  • Returns: number

blindPeer.registerMetrics(promClient)

src

Register Prometheus gauges for the blind peer, its wakeup protocol and, when push gateways are configured, their client pool.

ParameterTypeDescription
promClientobjectA prom-client compatible module. Use bare-prom-client under Bare.

The blind peer registers these gauges:

GroupGauges
Storageblind_peer_bytes_allocated, blind_peer_cores, blind_peer_announced_cores, blind_peer_db_flushes
Requestsblind_peer_add_cores_rx, blind_peer_cores_added, blind_peer_core_activations, blind_peer_active_replication_sessions, blind_peer_replication_sessions_opened, blind_peer_wakeups, blind_peer_muxer_paired, blind_peer_muxer_errors, blind_peer_referrer_rate_limited
Garbage collectionblind_peer_bytes_gcd, blind_peer_gc_prio_0, blind_peer_gc_prio_1, blind_peer_gc_prio_2, blind_peer_gc_cores_total, blind_peer_gc_cores_first_time_total
Busiest sendersblind_peer_add_cores_top5_by_remote_key, blind_peer_add_cores_top5_by_referrer, blind_peer_add_cores_top5_by_remote_ip: the requests from the top senders in the topK window

These are registered only when the RocksDB handle exposes stats: blind_peer_rocks_gets, blind_peer_rocks_puts, blind_peer_rocks_deletes, blind_peer_rocks_range_deletes, blind_peer_rocks_read_batches, blind_peer_rocks_write_batches, blind_peer_corestore_active (1 for an active store), blind_peer_push_notifications_active (1 when a gateway is configured), blind_peer_push_notifications_rx, blind_peer_push_notifications_sent, blind_peer_push_notifications_errors, blind_peer_core_trackers_created, blind_peer_core_trackers_destroyed and blind_peer_core_reset_download.

import promClient from 'prom-client'

blindPeer.registerMetrics(promClient)

Events

A blind peer is an event emitter. The events below are listed by what they report.

Requests and storage

src

EventArgumentsEmitted when
add-cores-received(stream, request)An add-cores request arrives, before the blind peer handles it. request has cores (each { key, length }), referrer, priority and announce.
add-new-core(record, true, stream)A core record is written to the database, because the core is new, announced, or has no local data yet. record has key, priority, announce and referrer. The second argument is always true.
add-core(entry, true, stream)The blind peer starts replicating a core with the client. entry has key, discoveryKey, remoteLength, priority, announce and referrer, plus ownLength and ownContigLength for what it already stores. The second argument is always true.
add-cores-done(stream, request)Every core in the request is being replicated.
add-cores-downgrade-announce({ request, remotePublicKey })A peer that isn't trusted asked for announce, and the blind peer reset it to false.
downgrade-announce({ record, remotePublicKey })The same, for a single-core request.
per-referrer-rate-limited(referrerKey)perReferrerRateLimitParams dropped a request. referrerKey is a hex string.
connection-banned(stream)A request came from an address on an IP ban list.
delete-core(stream, { key, existing })A trusted peer asked to delete a core. existing is false when the blind peer didn't have it.
delete-core-end(stream, { key, announced })The core is deleted.
delete-blocked(stream, { key })A peer that isn't trusted asked to delete a core. The request fails.

Announced cores

src

EventArgumentsEmitted when
announce-core(core)The blind peer joined the swarm for an announced core.
announced-initial-cores()After a restart, every stored announced core has been announced.
core-client-mode-changed(core, isClient)An announced core fell behind by more than replicationLagThreshold blocks and now also looks for peers to download from (true), or caught up and serves only (false).
core-append(core)An announced core grew.
core-downloaded(core)An announced core has all its blocks.

Activity and garbage collection

src

EventArgumentsEmitted when
core-activity(core, record)A stored core uploaded or downloaded data. record is null until the core's record has loaded.
gc-start({ bytesToClear })Garbage collection starts.
gc-done({ bytesCleared })Garbage collection finished.
flush-error(error)flush() failed.

Notifications and routing

src

EventArgumentsEmitted when
notification-rx(request, stream)A notification request arrives. request has block ({ key, index }), destination ({ key, discoveryKey }), extra and appId.
notification-sent(request, payload, stream, ms)The notification went to a push gateway. ms is how long it took.
notification-error(error, stream, request)Handling a notification failed. See errors.
notification-error-snapshot(snapshot)Diagnostics gathered after a failed notification. Upstream marks this event as temporary, with no semver guarantees.
resolve-peers({ key, result })The router answered for a referrer. See routerKey.
resolve-peers-error({ key, error })The router couldn't be reached or failed.

Connections and diagnostics

src

EventArgumentsEmitted when
muxer-paired(stream)A client opened its blind-peer-muxer channel.
muxer-error(error, stream)Handling an add-cores request failed.
invalid-request(session, error, request, from)A peer sent an invalid Hypercore request for a stored core.
warn(error)A diagnostic step failed without affecting requests.

How a blind peer handles requests

A blind peer accepts these requests over a client's connection:

RequestFromEffect
add-coresblind-peering clients, through addCore() and addAutobase()Records each core, replicates it with the client and keeps the request's priority, announce and referrer.
Notificationblind-peering clients, through sendNotification()Builds a blind-push notification from a stored block and forwards it to a push gateway.
add-coreA single-core request over protomux-rpcSame as add-cores for one core. It caps priority at 1.
delete-coreTrusted peers onlyStops replicating the core, clears its data and removes its record.

Priority

A core record has a priority of 0 (low), 1 (normal) or 2 (high). The client sends 0 for a core and 1 for an autobase unless you set priority. An add-cores request is capped at 2.

A record's priority only goes up: a later request with a lower priority leaves it as it is. Priority decides what garbage collection clears first. Raising a core to 2 also makes the blind peer download blocks that garbage collection had cleared.

Announce and trusted peers

An announced core is one the blind peer joins the swarm for, as a server, and keeps downloading in full, so any peer can find the core and fetch it from the blind peer. Announced cores are never garbage collected, and the blind peer announces them again after a restart.

Only trusted peers can announce. A peer is trusted when its public key is in trustedPubKeys or was added with addTrustedPubKey(). announce: true from anyone else is reset to false, and the blind peer emits add-cores-downgrade-announce. A trusted peer can also switch an existing record to announced, delete cores, and query the busiest senders when you set adminRouter.

Garbage collection

The blind peer keeps digest.bytesAllocated under maxBytes. When enableGc is on and usage reaches the budget, flush() clears cores until usage is back under it. Candidates go lowest priority first, and within a priority the core inactive for longest first. Announced cores are skipped.

A cleared core loses the blocks it had at that moment. The blind peer keeps downloading blocks appended later, and its record keeps how much was cleared. gc-start and gc-done report each run.

Referrers and wakeups

A request can name a referrer key. The blind peer stores it with the core. When the core grows, the blind peer wakes peers that follow the referrer and aren't already replicating the core, through protomux-wakeup. A peer that starts following a referrer hears about the 32 cores most recently updated under it. For an autobase, the client uses the autobase's wakeup capability key as the referrer.

Push notifications

With pushGatewayKeys set, the blind peer turns a notification request into a blind-push payload from the stored core and sends a forward-push request to a push gateway. If it doesn't have the requested block yet, it replicates the core with the client first. A request for a core the blind peer doesn't know waits retryRecordLookupTimeout milliseconds for a record, retries once, then fails with UNKNOWN_CORE without closing the connection. Without gateways the blind peer counts the request and drops it.

Types

BlindPeerOptions

Options for the BlindPeer constructor.

PropertyTypeDefaultDescription
swarmHyperswarmCreatedThe swarm that accepts connections. The blind peer creates one with a key pair from its database seed when you omit it.
storeCorestoreCreatedThe store that holds the replicated cores. When you pass one, the blind peer doesn't call store.replicate() on connections, so do that yourself for other peers to fetch what it stores.
wakeupobjectCreatedA protomux-wakeup instance. When you pass one, add the connections to it yourself.
maxBytesnumber100_000_000_000The storage budget in bytes. See garbage collection.
enableGcbooleantrueClear cores when usage reaches maxBytes. With false the blind peer keeps everything.
trustedPubKeysArray<Buffer|string>[]Public keys of trusted peers.
bootstrapArray|nullnullDHT bootstrap nodes for the swarm it creates.
portnumber|Array<number>—A port, or a [min, max] range, for the swarm it creates. A number n becomes [n, n + 64].
announcingIntervalnumber100Milliseconds between announcing each stored announced core at startup.
replicationLagThresholdnumber100How many blocks an announced core can lag before it also looks for peers to download from.
activeCorestorebooleanfalseWhether the store it creates attaches loaded cores to active replication streams.
treeCacheobject—Sizes the tree-node cache of the store it creates, as CorestoreOptions.treeCache does.
ipBanListKeysArray<Buffer|string>[]Keys of IP ban lists to follow. A request from a banned address is refused and emits connection-banned.
banTimeoutnumber16000Milliseconds a refused single-core or delete request is held before it fails with Timed out.
routerKeyBuffer|stringnullThe public key of a blind-peer-router. For a request with a referrer, the blind peer asks the router to resolve peers and emits resolve-peers.
routerPoolOptsobject{}Options for the RPC client pool that talks to the router, such as totalTimeout, rpcTimeout and retries.
pushGatewayKeysArray<Buffer|string>[]Public keys of push gateways to forward notifications to.
pushGatewayPoolOptsobject{}Options for the RPC client pool that talks to the gateways.
notificationTimeoutnumber30000Milliseconds to get the requested block when building a notification.
notificationErrorSnapshotDelaynumber30000Milliseconds before a failed notification's diagnostic snapshot is taken. Upstream marks it temporary, with no semver guarantees.
retryRecordLookupTimeoutnumber5000Milliseconds to wait before the one retry for a notification about a core with no record yet.
perReferrerRateLimitParams{ capacity, intervalMs }—Limit add-cores requests per referrer. Each referrer can burst capacity requests, then one more every intervalMs milliseconds. The blind peer drops requests over the limit. Requests without a referrer aren't limited.
topKobjectSee belowThe window that tracks the busiest senders of add-cores requests, by peer key, referrer and IP address.
adminRouterobjectnullA protomux-rpc-router router. When set, trusted peers can ask it for the busiest senders with query-top-k.
wakeupGcTickTimenumbernullThe gcTickTime that the blind peer gives to the wakeup protocol it creates.

topK takes bucketCount (6), bucketTime (10000 milliseconds) and k (5). Together they track the last bucketCount × bucketTime milliseconds and keep the k busiest senders. It also takes peerThreshold and referrerThreshold (both 100), which blind-peer 3.15.1 accepts but doesn't act on.

BlindPeerDigest

What blindPeer.digest returns.

PropertyTypeDescription
coresnumberHow many cores the blind peer stores.
bytesAllocatednumberHow many bytes those cores use. Garbage collection compares it to maxBytes.
referrersnumberHow many stored cores were added with a referrer.
flushedbooleanfalse while the blind peer is open. close() sets it to true after the final write.

Errors

Coded errors this module raises. Catch them through err.code.

ErrorRaised when
UNKNOWN_COREA notification names a core the blind peer has no record of, after the retry. It arrives through the notification-error event. The connection stays open.

See also

Last updated on

Was this helpful?

On this page