@bcts/hubert 1.0.0-alpha.17
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/LICENSE +48 -0
- package/README.md +18 -0
- package/dist/arid-derivation-1CJuU-kZ.cjs +150 -0
- package/dist/arid-derivation-1CJuU-kZ.cjs.map +1 -0
- package/dist/arid-derivation-CbqACjdg.mjs +126 -0
- package/dist/arid-derivation-CbqACjdg.mjs.map +1 -0
- package/dist/bin/hubert.cjs +384 -0
- package/dist/bin/hubert.cjs.map +1 -0
- package/dist/bin/hubert.d.cts +1 -0
- package/dist/bin/hubert.d.mts +1 -0
- package/dist/bin/hubert.mjs +383 -0
- package/dist/bin/hubert.mjs.map +1 -0
- package/dist/chunk-CbDLau6x.cjs +34 -0
- package/dist/hybrid/index.cjs +14 -0
- package/dist/hybrid/index.d.cts +3 -0
- package/dist/hybrid/index.d.mts +3 -0
- package/dist/hybrid/index.mjs +6 -0
- package/dist/hybrid-BZhumygj.mjs +356 -0
- package/dist/hybrid-BZhumygj.mjs.map +1 -0
- package/dist/hybrid-dX5JLumO.cjs +410 -0
- package/dist/hybrid-dX5JLumO.cjs.map +1 -0
- package/dist/index-BEzpUC7r.d.mts +380 -0
- package/dist/index-BEzpUC7r.d.mts.map +1 -0
- package/dist/index-C2F6ugLL.d.mts +210 -0
- package/dist/index-C2F6ugLL.d.mts.map +1 -0
- package/dist/index-CUnDouMb.d.mts +215 -0
- package/dist/index-CUnDouMb.d.mts.map +1 -0
- package/dist/index-CV6lZJqY.d.cts +380 -0
- package/dist/index-CV6lZJqY.d.cts.map +1 -0
- package/dist/index-CY3TCzIm.d.cts +217 -0
- package/dist/index-CY3TCzIm.d.cts.map +1 -0
- package/dist/index-DEr4SR1J.d.cts +215 -0
- package/dist/index-DEr4SR1J.d.cts.map +1 -0
- package/dist/index-T1LHanIb.d.mts +217 -0
- package/dist/index-T1LHanIb.d.mts.map +1 -0
- package/dist/index-jyzuOhFB.d.cts +210 -0
- package/dist/index-jyzuOhFB.d.cts.map +1 -0
- package/dist/index.cjs +60 -0
- package/dist/index.d.cts +161 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +161 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +10 -0
- package/dist/ipfs/index.cjs +13 -0
- package/dist/ipfs/index.d.cts +3 -0
- package/dist/ipfs/index.d.mts +3 -0
- package/dist/ipfs/index.mjs +5 -0
- package/dist/ipfs-BRMMCBjv.mjs +1 -0
- package/dist/ipfs-CetOVQcO.cjs +0 -0
- package/dist/kv-BAmhmMOo.cjs +425 -0
- package/dist/kv-BAmhmMOo.cjs.map +1 -0
- package/dist/kv-C-emxv0w.mjs +375 -0
- package/dist/kv-C-emxv0w.mjs.map +1 -0
- package/dist/kv-DJiKvypY.mjs +403 -0
- package/dist/kv-DJiKvypY.mjs.map +1 -0
- package/dist/kv-store-DmngWWuw.d.mts +183 -0
- package/dist/kv-store-DmngWWuw.d.mts.map +1 -0
- package/dist/kv-store-ww-AUyLd.d.cts +183 -0
- package/dist/kv-store-ww-AUyLd.d.cts.map +1 -0
- package/dist/kv-yjvQa_LH.cjs +457 -0
- package/dist/kv-yjvQa_LH.cjs.map +1 -0
- package/dist/logging-hmzNzifq.mjs +158 -0
- package/dist/logging-hmzNzifq.mjs.map +1 -0
- package/dist/logging-qc9uMgil.cjs +212 -0
- package/dist/logging-qc9uMgil.cjs.map +1 -0
- package/dist/mainline/index.cjs +12 -0
- package/dist/mainline/index.d.cts +3 -0
- package/dist/mainline/index.d.mts +3 -0
- package/dist/mainline/index.mjs +5 -0
- package/dist/mainline-D_jfeFMh.cjs +0 -0
- package/dist/mainline-cFIuXbo-.mjs +1 -0
- package/dist/server/index.cjs +14 -0
- package/dist/server/index.d.cts +3 -0
- package/dist/server/index.d.mts +3 -0
- package/dist/server/index.mjs +3 -0
- package/dist/server-BBNRZ30D.cjs +912 -0
- package/dist/server-BBNRZ30D.cjs.map +1 -0
- package/dist/server-DVyk9gqU.mjs +836 -0
- package/dist/server-DVyk9gqU.mjs.map +1 -0
- package/package.json +125 -0
- package/src/arid-derivation.ts +155 -0
- package/src/bin/hubert.ts +667 -0
- package/src/error.ts +89 -0
- package/src/hybrid/error.ts +77 -0
- package/src/hybrid/index.ts +24 -0
- package/src/hybrid/kv.ts +236 -0
- package/src/hybrid/reference.ts +176 -0
- package/src/index.ts +145 -0
- package/src/ipfs/error.ts +83 -0
- package/src/ipfs/index.ts +24 -0
- package/src/ipfs/kv.ts +476 -0
- package/src/ipfs/value.ts +85 -0
- package/src/kv-store.ts +128 -0
- package/src/logging.ts +88 -0
- package/src/mainline/error.ts +108 -0
- package/src/mainline/index.ts +23 -0
- package/src/mainline/kv.ts +411 -0
- package/src/server/error.ts +83 -0
- package/src/server/index.ts +29 -0
- package/src/server/kv.ts +211 -0
- package/src/server/memory-kv.ts +191 -0
- package/src/server/server-kv.ts +92 -0
- package/src/server/server.ts +369 -0
- package/src/server/sqlite-kv.ts +295 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-CbDLau6x.cjs');
|
|
2
|
+
const require_logging = require('./logging-qc9uMgil.cjs');
|
|
3
|
+
const require_arid_derivation = require('./arid-derivation-1CJuU-kZ.cjs');
|
|
4
|
+
let _bcts_envelope = require("@bcts/envelope");
|
|
5
|
+
let node_crypto = require("node:crypto");
|
|
6
|
+
node_crypto = require_chunk.__toESM(node_crypto);
|
|
7
|
+
let bittorrent_dht = require("bittorrent-dht");
|
|
8
|
+
bittorrent_dht = require_chunk.__toESM(bittorrent_dht);
|
|
9
|
+
let _noble_curves_ed25519_js = require("@noble/curves/ed25519.js");
|
|
10
|
+
|
|
11
|
+
//#region src/mainline/error.ts
|
|
12
|
+
/**
|
|
13
|
+
* Mainline DHT-specific errors.
|
|
14
|
+
*
|
|
15
|
+
* Port of mainline/error.rs from hubert-rust.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Base class for Mainline DHT-specific errors.
|
|
21
|
+
*
|
|
22
|
+
* @category Mainline
|
|
23
|
+
*/
|
|
24
|
+
var MainlineError = class extends require_logging.HubertError {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "MainlineError";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Value size exceeds DHT limit.
|
|
32
|
+
*
|
|
33
|
+
* Port of `Error::ValueTooLarge { size }` from mainline/error.rs line 4-5.
|
|
34
|
+
*
|
|
35
|
+
* @category Mainline
|
|
36
|
+
*/
|
|
37
|
+
var ValueTooLargeError = class extends MainlineError {
|
|
38
|
+
size;
|
|
39
|
+
constructor(size) {
|
|
40
|
+
super(`Value size ${size} exceeds DHT limit of 1000 bytes`);
|
|
41
|
+
this.name = "ValueTooLargeError";
|
|
42
|
+
this.size = size;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* DHT operation error.
|
|
47
|
+
*
|
|
48
|
+
* Port of `Error::DhtError` from mainline/error.rs line 7-8.
|
|
49
|
+
*
|
|
50
|
+
* @category Mainline
|
|
51
|
+
*/
|
|
52
|
+
var DhtError = class extends MainlineError {
|
|
53
|
+
constructor(message) {
|
|
54
|
+
super(`DHT operation error: ${message}`);
|
|
55
|
+
this.name = "DhtError";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Put query error.
|
|
60
|
+
*
|
|
61
|
+
* Port of `Error::PutQueryError` from mainline/error.rs line 10-11.
|
|
62
|
+
*
|
|
63
|
+
* @category Mainline
|
|
64
|
+
*/
|
|
65
|
+
var PutQueryError = class extends MainlineError {
|
|
66
|
+
constructor(message) {
|
|
67
|
+
super(`Put query error: ${message}`);
|
|
68
|
+
this.name = "PutQueryError";
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Decode ID error.
|
|
73
|
+
*
|
|
74
|
+
* Port of `Error::DecodeIdError` from mainline/error.rs line 13-14.
|
|
75
|
+
*
|
|
76
|
+
* @category Mainline
|
|
77
|
+
*/
|
|
78
|
+
var DecodeIdError = class extends MainlineError {
|
|
79
|
+
constructor(message) {
|
|
80
|
+
super(`Decode ID error: ${message}`);
|
|
81
|
+
this.name = "DecodeIdError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Put mutable error.
|
|
86
|
+
*
|
|
87
|
+
* Port of `Error::PutMutableError` from mainline/error.rs line 16-17.
|
|
88
|
+
*
|
|
89
|
+
* @category Mainline
|
|
90
|
+
*/
|
|
91
|
+
var PutMutableError = class extends MainlineError {
|
|
92
|
+
constructor(message) {
|
|
93
|
+
super(`Put mutable error: ${message}`);
|
|
94
|
+
this.name = "PutMutableError";
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* I/O error.
|
|
99
|
+
*
|
|
100
|
+
* Port of `Error::Io` from mainline/error.rs line 19-20.
|
|
101
|
+
*
|
|
102
|
+
* @category Mainline
|
|
103
|
+
*/
|
|
104
|
+
var MainlineIoError = class extends MainlineError {
|
|
105
|
+
constructor(message) {
|
|
106
|
+
super(`IO error: ${message}`);
|
|
107
|
+
this.name = "MainlineIoError";
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/mainline/kv.ts
|
|
113
|
+
/**
|
|
114
|
+
* Mainline DHT-backed key-value store using ARID-based addressing.
|
|
115
|
+
*
|
|
116
|
+
* Port of mainline/kv.rs from hubert-rust.
|
|
117
|
+
*
|
|
118
|
+
* @module
|
|
119
|
+
*/
|
|
120
|
+
/**
|
|
121
|
+
* Mainline DHT-backed key-value store using ARID-based addressing.
|
|
122
|
+
*
|
|
123
|
+
* This implementation uses:
|
|
124
|
+
* - ARID → ed25519 signing key derivation (deterministic)
|
|
125
|
+
* - BEP-44 mutable storage (fixed location based on pubkey)
|
|
126
|
+
* - Mainline DHT (BitTorrent DHT) for decentralized storage
|
|
127
|
+
* - Write-once semantics (seq=1, put fails if already exists)
|
|
128
|
+
* - Maximum value size: 1000 bytes (DHT protocol limit)
|
|
129
|
+
*
|
|
130
|
+
* Port of `struct MainlineDhtKv` from mainline/kv.rs lines 60-64.
|
|
131
|
+
*
|
|
132
|
+
* # Storage Model
|
|
133
|
+
*
|
|
134
|
+
* Uses BEP-44 mutable items where:
|
|
135
|
+
* - Public key derived from ARID (deterministic ed25519)
|
|
136
|
+
* - Sequence number starts at 1 (write-once)
|
|
137
|
+
* - Optional salt for namespace separation
|
|
138
|
+
* - Location fixed by pubkey (not content hash)
|
|
139
|
+
*
|
|
140
|
+
* # Requirements
|
|
141
|
+
*
|
|
142
|
+
* No external daemon required - the DHT client runs embedded.
|
|
143
|
+
*
|
|
144
|
+
* # Size Limits
|
|
145
|
+
*
|
|
146
|
+
* The Mainline DHT has a practical limit of ~1KB per value. For larger
|
|
147
|
+
* envelopes, use `IpfsKv` or `HybridKv` instead.
|
|
148
|
+
*
|
|
149
|
+
* @category Mainline Backend
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* const store = await MainlineDhtKv.create();
|
|
154
|
+
* const arid = ARID.new();
|
|
155
|
+
* const envelope = Envelope.new("Small message");
|
|
156
|
+
*
|
|
157
|
+
* // Put envelope (write-once)
|
|
158
|
+
* await store.put(arid, envelope);
|
|
159
|
+
*
|
|
160
|
+
* // Get envelope with verbose logging
|
|
161
|
+
* const retrieved = await store.get(arid, undefined, true);
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
var MainlineDhtKv = class MainlineDhtKv {
|
|
165
|
+
dht;
|
|
166
|
+
maxValueSize;
|
|
167
|
+
salt;
|
|
168
|
+
_isBootstrapped;
|
|
169
|
+
/**
|
|
170
|
+
* Private constructor - use `create()` factory method.
|
|
171
|
+
*/
|
|
172
|
+
constructor(dht) {
|
|
173
|
+
this.dht = dht;
|
|
174
|
+
this.maxValueSize = 1e3;
|
|
175
|
+
this.salt = void 0;
|
|
176
|
+
this._isBootstrapped = false;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Check if the DHT is bootstrapped.
|
|
180
|
+
*/
|
|
181
|
+
get isBootstrapped() {
|
|
182
|
+
return this._isBootstrapped;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Create a new Mainline DHT KV store with default settings.
|
|
186
|
+
*
|
|
187
|
+
* Port of `MainlineDhtKv::new()` from mainline/kv.rs lines 68-79.
|
|
188
|
+
*/
|
|
189
|
+
static async create() {
|
|
190
|
+
const dht = new bittorrent_dht.default();
|
|
191
|
+
const instance = new MainlineDhtKv(dht);
|
|
192
|
+
await new Promise((resolve, reject) => {
|
|
193
|
+
const timeout = setTimeout(() => {
|
|
194
|
+
reject(new DhtError("Bootstrap timeout"));
|
|
195
|
+
}, 3e4);
|
|
196
|
+
dht.on("ready", () => {
|
|
197
|
+
clearTimeout(timeout);
|
|
198
|
+
instance._isBootstrapped = true;
|
|
199
|
+
resolve();
|
|
200
|
+
});
|
|
201
|
+
dht.on("error", (err) => {
|
|
202
|
+
clearTimeout(timeout);
|
|
203
|
+
reject(new DhtError(err.message));
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
return instance;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Set the maximum value size (default: 1000 bytes).
|
|
210
|
+
*
|
|
211
|
+
* Note: Values larger than ~1KB may not be reliably stored in the DHT.
|
|
212
|
+
*
|
|
213
|
+
* Port of `MainlineDhtKv::with_max_size()` from mainline/kv.rs lines 84-87.
|
|
214
|
+
*/
|
|
215
|
+
withMaxSize(size) {
|
|
216
|
+
this.maxValueSize = size;
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Set a salt for namespace separation.
|
|
221
|
+
*
|
|
222
|
+
* Different salts will create separate namespaces for the same ARID.
|
|
223
|
+
*
|
|
224
|
+
* Port of `MainlineDhtKv::with_salt()` from mainline/kv.rs lines 92-95.
|
|
225
|
+
*/
|
|
226
|
+
withSalt(salt) {
|
|
227
|
+
this.salt = salt;
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Derive an ed25519 signing key from an ARID.
|
|
232
|
+
*
|
|
233
|
+
* Uses the ARID-derived key material extended to 32 bytes for ed25519.
|
|
234
|
+
*
|
|
235
|
+
* Port of `MainlineDhtKv::derive_signing_key()` from mainline/kv.rs lines 100-112.
|
|
236
|
+
*
|
|
237
|
+
* @internal
|
|
238
|
+
*/
|
|
239
|
+
static deriveSigningKey(arid) {
|
|
240
|
+
const keyBytes = require_arid_derivation.deriveMainlineKey(arid);
|
|
241
|
+
const seed = new Uint8Array(32);
|
|
242
|
+
seed.set(keyBytes.slice(0, 20));
|
|
243
|
+
for (let i = 20; i < 32; i++) seed[i] = keyBytes[i % 20] * i & 255;
|
|
244
|
+
return {
|
|
245
|
+
privateKey: seed,
|
|
246
|
+
publicKey: _noble_curves_ed25519_js.ed25519.getPublicKey(seed)
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get mutable item from DHT.
|
|
251
|
+
*
|
|
252
|
+
* @internal
|
|
253
|
+
*/
|
|
254
|
+
getMutable(publicKey, salt) {
|
|
255
|
+
return new Promise((resolve) => {
|
|
256
|
+
const target = this.computeTarget(publicKey, salt);
|
|
257
|
+
this.dht.get(target, (err, res) => {
|
|
258
|
+
if (err || !res?.v) resolve(null);
|
|
259
|
+
else resolve(res.v);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Put mutable item to DHT.
|
|
265
|
+
*
|
|
266
|
+
* @internal
|
|
267
|
+
*/
|
|
268
|
+
putMutable(privateKey, publicKey, value, seq, salt) {
|
|
269
|
+
return new Promise((resolve, reject) => {
|
|
270
|
+
const opts = {
|
|
271
|
+
k: Buffer.from(publicKey),
|
|
272
|
+
v: Buffer.from(value),
|
|
273
|
+
seq,
|
|
274
|
+
sign: (buf) => {
|
|
275
|
+
return Buffer.from(_noble_curves_ed25519_js.ed25519.sign(buf, privateKey));
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
if (salt) opts.salt = Buffer.from(salt);
|
|
279
|
+
this.dht.put(opts, (err) => {
|
|
280
|
+
if (err) reject(new PutMutableError(err.message));
|
|
281
|
+
else resolve();
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Compute DHT target hash from public key and optional salt.
|
|
287
|
+
*
|
|
288
|
+
* @internal
|
|
289
|
+
*/
|
|
290
|
+
computeTarget(publicKey, salt) {
|
|
291
|
+
const hash = node_crypto.default.createHash("sha1");
|
|
292
|
+
hash.update(publicKey);
|
|
293
|
+
if (salt) hash.update(salt);
|
|
294
|
+
return hash.digest();
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Store an envelope at the given ARID.
|
|
298
|
+
*
|
|
299
|
+
* Port of `KvStore::put()` implementation from mainline/kv.rs lines 144-220.
|
|
300
|
+
*/
|
|
301
|
+
async put(arid, envelope, _ttlSeconds, verbose) {
|
|
302
|
+
if (verbose) require_logging.verbosePrintln("Starting Mainline DHT put operation");
|
|
303
|
+
const bytes = envelope.taggedCborData();
|
|
304
|
+
if (verbose) require_logging.verbosePrintln(`Envelope size: ${bytes.length} bytes`);
|
|
305
|
+
const obfuscated = require_arid_derivation.obfuscateWithArid(arid, bytes);
|
|
306
|
+
if (obfuscated.length > this.maxValueSize) throw new ValueTooLargeError(obfuscated.length);
|
|
307
|
+
if (verbose) require_logging.verbosePrintln("Obfuscated envelope data");
|
|
308
|
+
if (verbose) require_logging.verbosePrintln("Deriving DHT signing key from ARID");
|
|
309
|
+
const { privateKey, publicKey } = MainlineDhtKv.deriveSigningKey(arid);
|
|
310
|
+
if (verbose) require_logging.verbosePrintln("Checking for existing value (write-once check)");
|
|
311
|
+
if (await this.getMutable(publicKey, this.salt) !== null) throw new require_logging.AlreadyExistsError(arid.urString());
|
|
312
|
+
if (verbose) require_logging.verbosePrintln("Creating mutable DHT item");
|
|
313
|
+
if (verbose) require_logging.verbosePrintln("Putting value to DHT");
|
|
314
|
+
await this.putMutable(privateKey, publicKey, obfuscated, 1, this.salt);
|
|
315
|
+
if (verbose) require_logging.verbosePrintln("Mainline DHT put operation completed");
|
|
316
|
+
return `dht://${Buffer.from(publicKey).toString("hex")}`;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Retrieve an envelope for the given ARID.
|
|
320
|
+
*
|
|
321
|
+
* Port of `KvStore::get()` implementation from mainline/kv.rs lines 223-303.
|
|
322
|
+
*/
|
|
323
|
+
async get(arid, timeoutSeconds, verbose) {
|
|
324
|
+
if (verbose) require_logging.verbosePrintln("Starting Mainline DHT get operation");
|
|
325
|
+
if (verbose) require_logging.verbosePrintln("Deriving DHT public key from ARID");
|
|
326
|
+
const { publicKey } = MainlineDhtKv.deriveSigningKey(arid);
|
|
327
|
+
const timeout = (timeoutSeconds ?? 30) * 1e3;
|
|
328
|
+
const deadline = Date.now() + timeout;
|
|
329
|
+
const pollInterval = 1e3;
|
|
330
|
+
if (verbose) require_logging.verbosePrintln("Polling DHT for value");
|
|
331
|
+
while (true) {
|
|
332
|
+
const item = await this.getMutable(publicKey, this.salt);
|
|
333
|
+
if (item !== null) {
|
|
334
|
+
if (verbose) {
|
|
335
|
+
require_logging.verboseNewline();
|
|
336
|
+
require_logging.verbosePrintln("Value found in DHT");
|
|
337
|
+
}
|
|
338
|
+
const deobfuscated = require_arid_derivation.obfuscateWithArid(arid, new Uint8Array(item));
|
|
339
|
+
if (verbose) require_logging.verbosePrintln("Deobfuscated envelope data");
|
|
340
|
+
const envelope = _bcts_envelope.EnvelopeDecoder.tryFromCborData(deobfuscated);
|
|
341
|
+
if (verbose) require_logging.verbosePrintln("Mainline DHT get operation completed");
|
|
342
|
+
return envelope;
|
|
343
|
+
}
|
|
344
|
+
if (Date.now() >= deadline) {
|
|
345
|
+
if (verbose) {
|
|
346
|
+
require_logging.verboseNewline();
|
|
347
|
+
require_logging.verbosePrintln("Timeout reached, value not found");
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
if (verbose) require_logging.verbosePrintDot();
|
|
352
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Check if an envelope exists at the given ARID.
|
|
357
|
+
*
|
|
358
|
+
* Port of `KvStore::exists()` implementation from mainline/kv.rs lines 306-314.
|
|
359
|
+
*/
|
|
360
|
+
async exists(arid) {
|
|
361
|
+
const { publicKey } = MainlineDhtKv.deriveSigningKey(arid);
|
|
362
|
+
return await this.getMutable(publicKey, this.salt) !== null;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Destroy the DHT client and release resources.
|
|
366
|
+
*/
|
|
367
|
+
destroy() {
|
|
368
|
+
return new Promise((resolve) => {
|
|
369
|
+
this.dht.destroy(() => {
|
|
370
|
+
resolve();
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
//#endregion
|
|
377
|
+
Object.defineProperty(exports, 'DecodeIdError', {
|
|
378
|
+
enumerable: true,
|
|
379
|
+
get: function () {
|
|
380
|
+
return DecodeIdError;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
Object.defineProperty(exports, 'DhtError', {
|
|
384
|
+
enumerable: true,
|
|
385
|
+
get: function () {
|
|
386
|
+
return DhtError;
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
Object.defineProperty(exports, 'MainlineDhtKv', {
|
|
390
|
+
enumerable: true,
|
|
391
|
+
get: function () {
|
|
392
|
+
return MainlineDhtKv;
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
Object.defineProperty(exports, 'MainlineError', {
|
|
396
|
+
enumerable: true,
|
|
397
|
+
get: function () {
|
|
398
|
+
return MainlineError;
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
Object.defineProperty(exports, 'MainlineIoError', {
|
|
402
|
+
enumerable: true,
|
|
403
|
+
get: function () {
|
|
404
|
+
return MainlineIoError;
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
Object.defineProperty(exports, 'PutMutableError', {
|
|
408
|
+
enumerable: true,
|
|
409
|
+
get: function () {
|
|
410
|
+
return PutMutableError;
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
Object.defineProperty(exports, 'PutQueryError', {
|
|
414
|
+
enumerable: true,
|
|
415
|
+
get: function () {
|
|
416
|
+
return PutQueryError;
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
Object.defineProperty(exports, 'ValueTooLargeError', {
|
|
420
|
+
enumerable: true,
|
|
421
|
+
get: function () {
|
|
422
|
+
return ValueTooLargeError;
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
//# sourceMappingURL=kv-BAmhmMOo.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-BAmhmMOo.cjs","names":["HubertError","DHT","deriveMainlineKey","ed25519","crypto","obfuscateWithArid","AlreadyExistsError","EnvelopeDecoder"],"sources":["../src/mainline/error.ts","../src/mainline/kv.ts"],"sourcesContent":["/**\n * Mainline DHT-specific errors.\n *\n * Port of mainline/error.rs from hubert-rust.\n *\n * @module\n */\n\nimport { HubertError } from \"../error.js\";\n\n/**\n * Base class for Mainline DHT-specific errors.\n *\n * @category Mainline\n */\nexport class MainlineError extends HubertError {\n constructor(message: string) {\n super(message);\n this.name = \"MainlineError\";\n }\n}\n\n/**\n * Value size exceeds DHT limit.\n *\n * Port of `Error::ValueTooLarge { size }` from mainline/error.rs line 4-5.\n *\n * @category Mainline\n */\nexport class ValueTooLargeError extends MainlineError {\n readonly size: number;\n\n constructor(size: number) {\n super(`Value size ${size} exceeds DHT limit of 1000 bytes`);\n this.name = \"ValueTooLargeError\";\n this.size = size;\n }\n}\n\n/**\n * DHT operation error.\n *\n * Port of `Error::DhtError` from mainline/error.rs line 7-8.\n *\n * @category Mainline\n */\nexport class DhtError extends MainlineError {\n constructor(message: string) {\n super(`DHT operation error: ${message}`);\n this.name = \"DhtError\";\n }\n}\n\n/**\n * Put query error.\n *\n * Port of `Error::PutQueryError` from mainline/error.rs line 10-11.\n *\n * @category Mainline\n */\nexport class PutQueryError extends MainlineError {\n constructor(message: string) {\n super(`Put query error: ${message}`);\n this.name = \"PutQueryError\";\n }\n}\n\n/**\n * Decode ID error.\n *\n * Port of `Error::DecodeIdError` from mainline/error.rs line 13-14.\n *\n * @category Mainline\n */\nexport class DecodeIdError extends MainlineError {\n constructor(message: string) {\n super(`Decode ID error: ${message}`);\n this.name = \"DecodeIdError\";\n }\n}\n\n/**\n * Put mutable error.\n *\n * Port of `Error::PutMutableError` from mainline/error.rs line 16-17.\n *\n * @category Mainline\n */\nexport class PutMutableError extends MainlineError {\n constructor(message: string) {\n super(`Put mutable error: ${message}`);\n this.name = \"PutMutableError\";\n }\n}\n\n/**\n * I/O error.\n *\n * Port of `Error::Io` from mainline/error.rs line 19-20.\n *\n * @category Mainline\n */\nexport class MainlineIoError extends MainlineError {\n constructor(message: string) {\n super(`IO error: ${message}`);\n this.name = \"MainlineIoError\";\n }\n}\n","/**\n * Mainline DHT-backed key-value store using ARID-based addressing.\n *\n * Port of mainline/kv.rs from hubert-rust.\n *\n * @module\n */\n\nimport crypto from \"node:crypto\";\nimport { type ARID } from \"@bcts/components\";\nimport { type Envelope, EnvelopeDecoder } from \"@bcts/envelope\";\n// @ts-expect-error - bittorrent-dht has no type declarations\nimport DHT from \"bittorrent-dht\";\nimport { ed25519 } from \"@noble/curves/ed25519.js\";\n\nimport { AlreadyExistsError } from \"../error.js\";\nimport { type KvStore } from \"../kv-store.js\";\nimport { deriveMainlineKey, obfuscateWithArid } from \"../arid-derivation.js\";\nimport { verboseNewline, verbosePrintDot, verbosePrintln } from \"../logging.js\";\nimport { DhtError, PutMutableError, ValueTooLargeError } from \"./error.js\";\n\n/**\n * Mainline DHT-backed key-value store using ARID-based addressing.\n *\n * This implementation uses:\n * - ARID → ed25519 signing key derivation (deterministic)\n * - BEP-44 mutable storage (fixed location based on pubkey)\n * - Mainline DHT (BitTorrent DHT) for decentralized storage\n * - Write-once semantics (seq=1, put fails if already exists)\n * - Maximum value size: 1000 bytes (DHT protocol limit)\n *\n * Port of `struct MainlineDhtKv` from mainline/kv.rs lines 60-64.\n *\n * # Storage Model\n *\n * Uses BEP-44 mutable items where:\n * - Public key derived from ARID (deterministic ed25519)\n * - Sequence number starts at 1 (write-once)\n * - Optional salt for namespace separation\n * - Location fixed by pubkey (not content hash)\n *\n * # Requirements\n *\n * No external daemon required - the DHT client runs embedded.\n *\n * # Size Limits\n *\n * The Mainline DHT has a practical limit of ~1KB per value. For larger\n * envelopes, use `IpfsKv` or `HybridKv` instead.\n *\n * @category Mainline Backend\n *\n * @example\n * ```typescript\n * const store = await MainlineDhtKv.create();\n * const arid = ARID.new();\n * const envelope = Envelope.new(\"Small message\");\n *\n * // Put envelope (write-once)\n * await store.put(arid, envelope);\n *\n * // Get envelope with verbose logging\n * const retrieved = await store.get(arid, undefined, true);\n * ```\n */\nexport class MainlineDhtKv implements KvStore {\n private readonly dht: DHT;\n private maxValueSize: number;\n private salt: Uint8Array | undefined;\n private _isBootstrapped: boolean;\n\n /**\n * Private constructor - use `create()` factory method.\n */\n private constructor(dht: DHT) {\n this.dht = dht;\n this.maxValueSize = 1000; // DHT protocol limit\n this.salt = undefined;\n this._isBootstrapped = false;\n }\n\n /**\n * Check if the DHT is bootstrapped.\n */\n get isBootstrapped(): boolean {\n return this._isBootstrapped;\n }\n\n /**\n * Create a new Mainline DHT KV store with default settings.\n *\n * Port of `MainlineDhtKv::new()` from mainline/kv.rs lines 68-79.\n */\n static async create(): Promise<MainlineDhtKv> {\n const dht = new DHT();\n\n const instance = new MainlineDhtKv(dht);\n\n // Wait for bootstrap\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new DhtError(\"Bootstrap timeout\"));\n }, 30000);\n\n dht.on(\"ready\", () => {\n clearTimeout(timeout);\n instance._isBootstrapped = true;\n resolve();\n });\n\n dht.on(\"error\", (err: Error) => {\n clearTimeout(timeout);\n reject(new DhtError(err.message));\n });\n });\n\n return instance;\n }\n\n /**\n * Set the maximum value size (default: 1000 bytes).\n *\n * Note: Values larger than ~1KB may not be reliably stored in the DHT.\n *\n * Port of `MainlineDhtKv::with_max_size()` from mainline/kv.rs lines 84-87.\n */\n withMaxSize(size: number): this {\n this.maxValueSize = size;\n return this;\n }\n\n /**\n * Set a salt for namespace separation.\n *\n * Different salts will create separate namespaces for the same ARID.\n *\n * Port of `MainlineDhtKv::with_salt()` from mainline/kv.rs lines 92-95.\n */\n withSalt(salt: Uint8Array): this {\n this.salt = salt;\n return this;\n }\n\n /**\n * Derive an ed25519 signing key from an ARID.\n *\n * Uses the ARID-derived key material extended to 32 bytes for ed25519.\n *\n * Port of `MainlineDhtKv::derive_signing_key()` from mainline/kv.rs lines 100-112.\n *\n * @internal\n */\n private static deriveSigningKey(arid: ARID): { privateKey: Uint8Array; publicKey: Uint8Array } {\n const keyBytes = deriveMainlineKey(arid);\n\n // Extend to 32 bytes if needed (ARID gives us 20, we need 32)\n const seed = new Uint8Array(32);\n seed.set(keyBytes.slice(0, 20));\n // Use simple derivation for remaining 12 bytes\n for (let i = 20; i < 32; i++) {\n seed[i] = (keyBytes[i % 20] * i) & 0xff;\n }\n\n // Get public key from private key seed\n const publicKey = ed25519.getPublicKey(seed);\n\n return { privateKey: seed, publicKey };\n }\n\n /**\n * Get mutable item from DHT.\n *\n * @internal\n */\n private getMutable(publicKey: Uint8Array, salt?: Uint8Array): Promise<Buffer | null> {\n return new Promise((resolve) => {\n const target = this.computeTarget(publicKey, salt);\n\n this.dht.get(target, (err: Error | null, res: { v?: Buffer } | null) => {\n if (err || !res?.v) {\n resolve(null);\n } else {\n resolve(res.v);\n }\n });\n });\n }\n\n /**\n * Put mutable item to DHT.\n *\n * @internal\n */\n private putMutable(\n privateKey: Uint8Array,\n publicKey: Uint8Array,\n value: Uint8Array,\n seq: number,\n salt?: Uint8Array,\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n const opts: {\n k: Buffer;\n v: Buffer;\n seq: number;\n sign: (buf: Buffer) => Buffer;\n salt?: Buffer;\n } = {\n k: Buffer.from(publicKey),\n v: Buffer.from(value),\n seq,\n sign: (buf: Buffer) => {\n return Buffer.from(ed25519.sign(buf, privateKey));\n },\n };\n\n if (salt) {\n opts.salt = Buffer.from(salt);\n }\n\n this.dht.put(opts, (err: Error | null) => {\n if (err) {\n reject(new PutMutableError(err.message));\n } else {\n resolve();\n }\n });\n });\n }\n\n /**\n * Compute DHT target hash from public key and optional salt.\n *\n * @internal\n */\n private computeTarget(publicKey: Uint8Array, salt?: Uint8Array): Buffer {\n // For BEP-44 mutable items, the target is sha1(publicKey + salt)\n // The bittorrent-dht library handles this internally when using get/put with k/salt\n // But for direct get() calls we need to compute it ourselves\n const hash = crypto.createHash(\"sha1\");\n hash.update(publicKey);\n if (salt) {\n hash.update(salt);\n }\n return hash.digest();\n }\n\n /**\n * Store an envelope at the given ARID.\n *\n * Port of `KvStore::put()` implementation from mainline/kv.rs lines 144-220.\n */\n async put(\n arid: ARID,\n envelope: Envelope,\n _ttlSeconds?: number, // Ignored - DHT has no TTL support\n verbose?: boolean,\n ): Promise<string> {\n if (verbose) {\n verbosePrintln(\"Starting Mainline DHT put operation\");\n }\n\n // Serialize envelope\n const bytes = envelope.taggedCborData();\n\n if (verbose) {\n verbosePrintln(`Envelope size: ${bytes.length} bytes`);\n }\n\n // Obfuscate with ARID-derived key so it appears as random data\n const obfuscated = obfuscateWithArid(arid, bytes);\n\n // Check size after obfuscation (same size, but check anyway)\n if (obfuscated.length > this.maxValueSize) {\n throw new ValueTooLargeError(obfuscated.length);\n }\n\n if (verbose) {\n verbosePrintln(\"Obfuscated envelope data\");\n }\n\n // Derive signing key from ARID\n if (verbose) {\n verbosePrintln(\"Deriving DHT signing key from ARID\");\n }\n const { privateKey, publicKey } = MainlineDhtKv.deriveSigningKey(arid);\n\n // Check if already exists (write-once semantics)\n if (verbose) {\n verbosePrintln(\"Checking for existing value (write-once check)\");\n }\n const existing = await this.getMutable(publicKey, this.salt);\n if (existing !== null) {\n throw new AlreadyExistsError(arid.urString());\n }\n\n // Create mutable item with seq=1 (first write) using obfuscated data\n if (verbose) {\n verbosePrintln(\"Creating mutable DHT item\");\n }\n\n // Put to DHT\n if (verbose) {\n verbosePrintln(\"Putting value to DHT\");\n }\n await this.putMutable(privateKey, publicKey, obfuscated, 1, this.salt);\n\n if (verbose) {\n verbosePrintln(\"Mainline DHT put operation completed\");\n }\n\n return `dht://${Buffer.from(publicKey).toString(\"hex\")}`;\n }\n\n /**\n * Retrieve an envelope for the given ARID.\n *\n * Port of `KvStore::get()` implementation from mainline/kv.rs lines 223-303.\n */\n async get(arid: ARID, timeoutSeconds?: number, verbose?: boolean): Promise<Envelope | null> {\n if (verbose) {\n verbosePrintln(\"Starting Mainline DHT get operation\");\n }\n\n // Derive public key from ARID\n if (verbose) {\n verbosePrintln(\"Deriving DHT public key from ARID\");\n }\n const { publicKey } = MainlineDhtKv.deriveSigningKey(arid);\n\n const timeout = (timeoutSeconds ?? 30) * 1000; // Default 30 seconds\n const deadline = Date.now() + timeout;\n // Changed to 1000ms for verbose mode polling\n const pollInterval = 1000;\n\n if (verbose) {\n verbosePrintln(\"Polling DHT for value\");\n }\n\n while (true) {\n // Get mutable item\n const item = await this.getMutable(publicKey, this.salt);\n\n if (item !== null) {\n if (verbose) {\n verboseNewline();\n verbosePrintln(\"Value found in DHT\");\n }\n\n // Deobfuscate the data using ARID-derived key\n const obfuscatedBytes = new Uint8Array(item);\n const deobfuscated = obfuscateWithArid(arid, obfuscatedBytes);\n\n if (verbose) {\n verbosePrintln(\"Deobfuscated envelope data\");\n }\n\n // Deserialize envelope from deobfuscated data\n const envelope = EnvelopeDecoder.tryFromCborData(deobfuscated);\n\n if (verbose) {\n verbosePrintln(\"Mainline DHT get operation completed\");\n }\n\n return envelope;\n }\n\n // Not found yet - check if we should keep polling\n if (Date.now() >= deadline) {\n // Timeout reached\n if (verbose) {\n verboseNewline();\n verbosePrintln(\"Timeout reached, value not found\");\n }\n return null;\n }\n\n // Print polling dot if verbose\n if (verbose) {\n verbosePrintDot();\n }\n\n // Wait before retrying (now 1000ms)\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n }\n\n /**\n * Check if an envelope exists at the given ARID.\n *\n * Port of `KvStore::exists()` implementation from mainline/kv.rs lines 306-314.\n */\n async exists(arid: ARID): Promise<boolean> {\n const { publicKey } = MainlineDhtKv.deriveSigningKey(arid);\n\n // Check if mutable item exists\n const item = await this.getMutable(publicKey, this.salt);\n return item !== null;\n }\n\n /**\n * Destroy the DHT client and release resources.\n */\n destroy(): Promise<void> {\n return new Promise((resolve) => {\n this.dht.destroy(() => {\n resolve();\n });\n });\n }\n}\n"],"mappings":"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"}
|