blind-peer 0.0.3 → 2.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -2
- package/bin.js +165 -6
- package/index.js +425 -87
- package/lib/db.js +164 -0
- package/package.json +39 -14
- package/.github/workflows/test-node.yml +0 -23
- package/build.js +0 -102
- package/client.js +0 -31
- package/example/autobase.mjs +0 -66
- package/example/blind-peer.mjs +0 -6
- package/example/post.mjs +0 -25
- package/spec/hyperdb/db.json +0 -19
- package/spec/hyperdb/index.js +0 -83
- package/spec/hyperdb/messages.js +0 -234
- package/spec/hyperschema/index.js +0 -199
- package/spec/hyperschema/schema.json +0 -107
package/README.md
CHANGED
|
@@ -1,13 +1,81 @@
|
|
|
1
1
|
# blind-peer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Blind peers help keep hypercores available.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install globally to use the `blind-peer` command:
|
|
4
8
|
|
|
5
9
|
```
|
|
6
|
-
npm install blind-peer
|
|
10
|
+
npm install -g blind-peer
|
|
7
11
|
```
|
|
8
12
|
|
|
9
13
|
## Usage
|
|
10
14
|
|
|
15
|
+
Run a blind peer:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
blind-peer
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Command Line Options
|
|
22
|
+
|
|
23
|
+
- `--storage|-s [path]` - Storage path, defaults to ./blind-peer
|
|
24
|
+
- `--port|-p [int]` - DHT Port to try to bind to. Only relevant when that port is not firewalled. (defaults to a random port)
|
|
25
|
+
- `--trusted-peer|-t [trusted-peer]` - Public key of a trusted peer (allowed to set announce: true). Can be specified multiple times.
|
|
26
|
+
- `--debug|-d` - Enable debug mode (more logs). Can be specified multiple times.
|
|
27
|
+
- `--max-storage|-m [int]` - Max storage usage, in Mb (defaults to 100000)
|
|
28
|
+
- `--autodiscovery-rpc-key [autodiscovery-rpc-key]` - Public key where the autodiscovery service is listening. When set, the autodiscovery-seed must also be set. Can be hex or z32.
|
|
29
|
+
- `--autodiscovery-seed [autodiscovery-seed]` - 64-byte seed used to authenticate to the autodiscovery service. Can be hex or z32.
|
|
30
|
+
- `--autodiscovery-service-name [autodiscovery-service-name]` - Name under which to register the service (default blind-peer)
|
|
31
|
+
- `--scraper-public-key [scraper-public-key]` - Public key of a dht-prometheus scraper. Can be hex or z32.
|
|
32
|
+
- `--scraper-secret [scraper-secret]` - Secret of the dht-prometheus scraper. Can be hex or z32.
|
|
33
|
+
- `--scraper-alias [scraper-alias]` - (optional) Alias with which to register to the scraper
|
|
34
|
+
|
|
35
|
+
### Output
|
|
36
|
+
|
|
37
|
+
When started, ndjson (pino) will be emitted for events. An example startup will look like:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
{"level":30,"time":1751662694931,"pid":96069,"hostname":"L293","msg":"Starting blind peer"}
|
|
41
|
+
{"level":30,"time":1751662694932,"pid":96069,"hostname":"L293","msg":"Using storage 'blind-peer'"}
|
|
42
|
+
{"level":30,"time":1751662696936,"pid":96069,"hostname":"L293","msg":"Blind peer listening, local address is 10.0.0.214:49741"}
|
|
43
|
+
{"level":30,"time":1751662696936,"pid":96069,"hostname":"L293","msg":"Bytes allocated: 0B of 100GB"}
|
|
44
|
+
{"level":30,"time":1751662696936,"pid":96069,"hostname":"L293","msg":"Listening at es4n7ty45odd1udfqyi9xz58mrbheuhdnxgdufsn9gz6e5uhsqco"}
|
|
45
|
+
{"level":30,"time":1751662696936,"pid":96069,"hostname":"L293","msg":"Encryption public key is ur7d9r7s3zf1ryibixt5139bep67y94s5bg4gckzo1p6qgtwwfyy"}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Using a Blind Peer
|
|
49
|
+
|
|
50
|
+
To use a blind peer, use [blind-peering](https://github.com/holepunchto/blind-peering)
|
|
51
|
+
|
|
52
|
+
Here is an example, using the key from above
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
import BlindPeering from '@holepunchto/blind-peering'
|
|
56
|
+
import Hyperswarm from 'hyperswarm'
|
|
57
|
+
import Corestore from 'corestore'
|
|
58
|
+
import Wakeup from 'protomux-wakeup'
|
|
59
|
+
|
|
60
|
+
const store = new Corestore(Pear.config.storage)
|
|
61
|
+
const swarm = new Hyperswarm()
|
|
62
|
+
const wakeup = new Wakeup()
|
|
63
|
+
|
|
64
|
+
const DEFAULT_BLIND_PEER_KEYS = ['es4n7ty45odd1udfqyi9xz58mrbheuhdnxgdufsn9gz6e5uhsqco']
|
|
65
|
+
const blind = new BlindPeering(swarm, store, { wakeup, mirrors: DEFAULT_BLIND_PEER_KEYS })
|
|
66
|
+
blind.addAutobaseBackground(autobase1)
|
|
67
|
+
blind.addCore(core1, autobase1.wakeupCapability.key)
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Related services:
|
|
72
|
+
|
|
73
|
+
https://github.com/holepunchto/autobase-discovery
|
|
74
|
+
https://github.com/HDegroote/dht-prometheus
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## Programmatic Usage
|
|
78
|
+
|
|
11
79
|
``` js
|
|
12
80
|
const BlindPeer = require('blind-peer')
|
|
13
81
|
```
|
package/bin.js
CHANGED
|
@@ -3,27 +3,186 @@
|
|
|
3
3
|
const { command, flag } = require('paparam')
|
|
4
4
|
const goodbye = require('graceful-goodbye')
|
|
5
5
|
const idEnc = require('hypercore-id-encoding')
|
|
6
|
+
const Instrumentation = require('hyper-instrument')
|
|
7
|
+
const RegisterClient = require('autobase-discovery/client/register')
|
|
8
|
+
const safetyCatch = require('safety-catch')
|
|
9
|
+
const byteSize = require('tiny-byte-size')
|
|
10
|
+
const pino = require('pino')
|
|
11
|
+
|
|
6
12
|
const BlindPeer = require('.')
|
|
7
13
|
|
|
14
|
+
const SERVICE_NAME = 'blind-peer'
|
|
15
|
+
const DEFAULT_STORAGE_LIMIT_MB = 100_000
|
|
16
|
+
|
|
8
17
|
const cmd = command('blind-peer',
|
|
9
|
-
flag('--storage|-s [path]', '
|
|
18
|
+
flag('--storage|-s [path]', 'Storage path, defaults to ./blind-peer'),
|
|
19
|
+
flag('--port|-p [int]', 'DHT Port to try to bind to. Only relevant when that port is not firewalled. (defaults to a random port)'),
|
|
20
|
+
flag('--trusted-peer|-t [trusted-peer]', 'Public key of a trusted peer (allowed to set announce: true). Can be more than 1.').multiple(),
|
|
21
|
+
flag('--debug|-d', 'Enable debug mode (more logs)').multiple(),
|
|
22
|
+
flag(`--max-storage|-m [int]', 'Max storage usage, in Mb (defaults to ${DEFAULT_STORAGE_LIMIT_MB})`),
|
|
23
|
+
flag('--autodiscovery-rpc-key [autodiscovery-rpc-key]', 'Public key where the autodiscovery service is listening. When set, the autodiscovery-seed must also be set. Can be hex or z32.'),
|
|
24
|
+
flag('--autodiscovery-seed [autodiscovery-seed]', '64-byte seed used to authenticate to the autodiscovery service. Can be hex or z32.'),
|
|
25
|
+
flag('--autodiscovery-service-name [autodiscovery-service-name]', `Name under which to register the service (default ${SERVICE_NAME})`),
|
|
26
|
+
flag('--scraper-public-key [scraper-public-key]', 'Public key of a dht-prometheus scraper. Can be hex or z32.'),
|
|
27
|
+
flag('--scraper-secret [scraper-secret]', 'Secret of the dht-prometheus scraper. Can be hex or z32.'),
|
|
28
|
+
flag('--scraper-alias [scraper-alias]', '(optional) Alias with which to register to the scraper'),
|
|
29
|
+
flag('--repl', 'Expose a repl-swarm (use for debugging only)'),
|
|
10
30
|
async function ({ flags }) {
|
|
11
|
-
|
|
31
|
+
const debug = flags.debug
|
|
32
|
+
const logger = pino({
|
|
33
|
+
level: debug ? 'debug' : 'info'
|
|
34
|
+
})
|
|
35
|
+
logger.info('Starting blind peer')
|
|
12
36
|
|
|
13
37
|
const storage = flags.storage || 'blind-peer'
|
|
14
|
-
const
|
|
38
|
+
const port = flags.port ? parseInt(flags.port) : null
|
|
39
|
+
|
|
40
|
+
const exposeRepl = flags.repl === true
|
|
41
|
+
const maxBytes = 1_000_000 * parseInt(flags.maxStorage || DEFAULT_STORAGE_LIMIT_MB)
|
|
42
|
+
const trustedPubKeys = (flags.trustedPeer || []).map(k => idEnc.decode(k))
|
|
43
|
+
|
|
44
|
+
const blindPeer = new BlindPeer(storage, { trustedPubKeys, maxBytes, port })
|
|
45
|
+
|
|
46
|
+
blindPeer.on('post-to-mailbox', req => {
|
|
47
|
+
try {
|
|
48
|
+
logger.info(`post-to-mailbox request received for mailbox: ${idEnc.normalize(req.mailbox)} with message ${idEnc.normalize(req.message)})`)
|
|
49
|
+
} catch {
|
|
50
|
+
logger.info('Invalid post-to-mailbox request received')
|
|
51
|
+
logger.info(req)
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
blindPeer.on('add-core', record => {
|
|
56
|
+
try {
|
|
57
|
+
logger.info(`add-core request received for record ${recordToStr(record)}`)
|
|
58
|
+
} catch (e) {
|
|
59
|
+
logger.info(`Invalid add-core request received: ${e.stack}`)
|
|
60
|
+
logger.info(record)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
blindPeer.on('downgrade-announce', ({ record, remotePublicKey }) => {
|
|
65
|
+
try {
|
|
66
|
+
logger.info(`Downgraded announce for peer ${idEnc.normalize(remotePublicKey)} because the peer is not trusted (Original: ${recordToStr(record)})`)
|
|
67
|
+
} catch (e) {
|
|
68
|
+
logger.error(`Unexpected error while logging downgrade-announce: ${e.stack}`)
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
blindPeer.on('announce-core', core => {
|
|
73
|
+
logger.info(`Started announcing core ${coreToInfo(core)}`)
|
|
74
|
+
})
|
|
75
|
+
blindPeer.on('core-downloaded', core => {
|
|
76
|
+
logger.info(`Announced core fully downloaded: ${coreToInfo(core)}`)
|
|
77
|
+
})
|
|
78
|
+
blindPeer.on('core-append', core => {
|
|
79
|
+
logger.info(`Detected announced-core length update: ${coreToInfo(core)}`)
|
|
80
|
+
})
|
|
15
81
|
|
|
16
|
-
|
|
82
|
+
blindPeer.on('gc-start', ({ bytesToClear }) => {
|
|
83
|
+
logger.info(`Starting GC, trying to clear ${byteSize(bytesToClear)} (bytes allocated: ${byteSize(blindPeer.digest.bytesAllocated)} of ${byteSize(blindPeer.maxBytes)})`)
|
|
84
|
+
})
|
|
85
|
+
blindPeer.on('gc-done', ({ bytesCleared }) => {
|
|
86
|
+
logger.info(`Completed GC, cleared ${byteSize(bytesCleared)} bytes (bytes allocated: ${byteSize(blindPeer.digest.bytesAllocated)} of ${byteSize(blindPeer.maxBytes)})`)
|
|
87
|
+
})
|
|
88
|
+
if (debug) {
|
|
89
|
+
blindPeer.on('core-activity', (core) => {
|
|
90
|
+
logger.debug(`Core activity for ${coreToInfo(core)}`)
|
|
91
|
+
})
|
|
92
|
+
}
|
|
17
93
|
|
|
94
|
+
logger.info(`Using storage '${storage}'`)
|
|
95
|
+
if (trustedPubKeys.length > 0) {
|
|
96
|
+
logger.info(`Trusted public keys:\n -${[...blindPeer.trustedPubKeys].map(idEnc.normalize).join('\n -')}`)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let instrumentation = null
|
|
18
100
|
goodbye(async () => {
|
|
19
|
-
|
|
101
|
+
if (instrumentation) {
|
|
102
|
+
logger.info('Closing instrumentation')
|
|
103
|
+
await instrumentation.close()
|
|
104
|
+
}
|
|
105
|
+
logger.info('Shutting down blind peer')
|
|
20
106
|
await blindPeer.close()
|
|
107
|
+
logger.info('Shut down blind peer')
|
|
21
108
|
})
|
|
22
109
|
|
|
110
|
+
if (exposeRepl) {
|
|
111
|
+
logger.warn('Setting up REPL swarm, enabling remote access to this process')
|
|
112
|
+
const replSwarm = require('repl-swarm')
|
|
113
|
+
const seed = replSwarm({ blindPeer, instrumentation })
|
|
114
|
+
setInterval(
|
|
115
|
+
() => {
|
|
116
|
+
logger.info(`REPL swarm available at ${seed}`)
|
|
117
|
+
}, 1000 * 60 * 60
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
23
121
|
await blindPeer.listen()
|
|
24
122
|
|
|
25
|
-
|
|
123
|
+
logger.info(`Blind peer listening, local address is ${blindPeer.swarm.dht.localAddress().host}:${blindPeer.swarm.dht.localAddress().port}`)
|
|
124
|
+
logger.info(`Bytes allocated: ${byteSize(blindPeer.digest.bytesAllocated)} of ${byteSize(blindPeer.maxBytes)}`)
|
|
125
|
+
|
|
126
|
+
if (debug) {
|
|
127
|
+
blindPeer.swarm.on('connection', (conn, peerInfo) => {
|
|
128
|
+
const key = idEnc.normalize(peerInfo.publicKey)
|
|
129
|
+
logger.debug(`Opened connection to ${key}`)
|
|
130
|
+
conn.on('close', () => logger.debug(`Closed connection to ${key}`))
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (flags.autodiscoveryRpcKey) {
|
|
135
|
+
const autodiscoveryRpcKey = idEnc.decode(flags.autodiscoveryRpcKey)
|
|
136
|
+
const seed = idEnc.decode(flags.autodiscoverySeed)
|
|
137
|
+
const serviceName = flags.autodiscoveryServiceName || SERVICE_NAME
|
|
138
|
+
const registerClient = new RegisterClient(autodiscoveryRpcKey, blindPeer.swarm.dht, seed)
|
|
139
|
+
|
|
140
|
+
// No need to block on this, so we run it in the background
|
|
141
|
+
logger.info(`Registering own RPC key rpc key ${idEnc.normalize(blindPeer.publicKey)} with service '${serviceName}' at autodiscovery service ${idEnc.normalize(autodiscoveryRpcKey)} (using public key ${idEnc.normalize(registerClient.keyPair.publicKey)})`)
|
|
142
|
+
registerClient.putService(blindPeer.publicKey, serviceName)
|
|
143
|
+
.then(() => { logger.info('Successfully requested to be added to the autodiscovery service') })
|
|
144
|
+
.catch(e => { logger.warn(`Failed to register to the autodiscovery service: ${e.stack}`) })
|
|
145
|
+
.finally(() => { registerClient.close().catch(safetyCatch) })
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (flags.scraperPublicKey) {
|
|
149
|
+
const swarm = blindPeer.swarm
|
|
150
|
+
logger.info('Setting up instrumentation')
|
|
151
|
+
|
|
152
|
+
const scraperPublicKey = idEnc.decode(flags.scraperPublicKey)
|
|
153
|
+
const scraperSecret = idEnc.decode(flags.scraperSecret)
|
|
154
|
+
|
|
155
|
+
let prometheusAlias = flags.scraperAlias
|
|
156
|
+
if (prometheusAlias && prometheusAlias.length > 99) throw new Error('The Prometheus alias must have length less than 100')
|
|
157
|
+
if (!prometheusAlias) {
|
|
158
|
+
prometheusAlias = `blind-peer-${idEnc.normalize(swarm.keyPair.publicKey)}`.slice(0, 99)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
instrumentation = new Instrumentation({
|
|
162
|
+
swarm,
|
|
163
|
+
corestore: blindPeer.store,
|
|
164
|
+
scraperPublicKey,
|
|
165
|
+
prometheusAlias,
|
|
166
|
+
scraperSecret,
|
|
167
|
+
prometheusServiceName: SERVICE_NAME
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
blindPeer.registerMetrics(instrumentation.promClient)
|
|
171
|
+
instrumentation.registerLogger(logger)
|
|
172
|
+
await instrumentation.ready()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
logger.info(`Listening at ${idEnc.normalize(blindPeer.publicKey)}`)
|
|
176
|
+
logger.info(`Encryption public key is ${idEnc.normalize(blindPeer.encryptionPublicKey)}`)
|
|
26
177
|
}
|
|
27
178
|
)
|
|
28
179
|
|
|
180
|
+
function recordToStr (record) {
|
|
181
|
+
return `DB Record for key ${idEnc.normalize(record.key)} with priority: ${record.priority}. Announcing? ${record.announce}`
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function coreToInfo (core) {
|
|
185
|
+
return `${idEnc.normalize(core.key)} (${core.contiguousLength} / ${core.length}, ${core.peers.length} peers)`
|
|
186
|
+
}
|
|
187
|
+
|
|
29
188
|
cmd.parse()
|