@le-space/aleph-bootstrap 0.6.24 → 0.6.26

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.
Files changed (4) hide show
  1. package/README.md +6 -5
  2. package/index.d.ts +3 -11
  3. package/index.js +103 -73
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -27,20 +27,21 @@ The package defaults to the shared relay-bootstrap namespace:
27
27
 
28
28
  - channel: `simple-todo`
29
29
  - ref: `simple-todo-bootstrap`
30
- - post type: `relay-bootstrap`
30
+ - post type: `relay-bootstrap-v2`
31
31
 
32
- All values are overrideable per app or environment.
32
+ The channel and ref are overrideable per app or environment. The post type is
33
+ fixed to `relay-bootstrap-v2`.
33
34
 
34
35
  ## Discovery Trust Modes
35
36
 
36
- The package accepts both:
37
+ The package accepts two signing modes for v2 records:
37
38
 
38
- - legacy wallet-signed bootstrap posts
39
+ - wallet-signed bootstrap posts
39
40
  - dual-key-attested bootstrap posts
40
41
 
41
42
  By default, discovery will:
42
43
 
43
- - accept legacy posts
44
+ - accept wallet-signed v2 posts
44
45
  - verify dual-key records when they are present
45
46
  - ignore malformed or invalid dual-key records
46
47
 
package/index.d.ts CHANGED
@@ -3,8 +3,7 @@ import { bootstrap } from '@libp2p/bootstrap';
3
3
  declare const DEFAULT_ALEPH_API_HOST = "https://api.aleph.im";
4
4
  declare const DEFAULT_ALEPH_BOOTSTRAP_CHANNEL = "simple-todo";
5
5
  declare const DEFAULT_ALEPH_BOOTSTRAP_REF = "simple-todo-bootstrap";
6
- declare const DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap";
7
- declare const DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE = "relay-bootstrap-v2";
6
+ declare const DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap-v2";
8
7
  declare const DEFAULT_BOOTSTRAP_MAX_AGE_MS: number;
9
8
  declare const DEFAULT_BOOTSTRAP_PAGINATION = 50;
10
9
  declare const DEFAULT_BOOTSTRAP_MAX_PAGES = 5;
@@ -80,8 +79,6 @@ interface DiscoverAlephBootstrapOptions {
80
79
  apiHost?: string;
81
80
  channel?: string;
82
81
  ref?: string;
83
- postType?: string;
84
- postTypes?: string[];
85
82
  page?: number;
86
83
  pagination?: number;
87
84
  maxPages?: number;
@@ -104,19 +101,17 @@ interface CreateRelayBootstrapPostOptions {
104
101
  registrationId?: string;
105
102
  ref?: string;
106
103
  channel?: string;
107
- postType?: string;
108
104
  profile?: string;
109
105
  version?: string;
110
106
  ownerAddress?: string;
111
107
  publisherAddress?: string;
112
108
  authorization?: RelayBootstrapAuthorizationRecord;
113
109
  relayProof?: RelayBootstrapProofRecord;
114
- compact?: boolean;
115
110
  compactMultiaddrLimit?: number;
116
111
  now?: number;
117
112
  hasher: (payload: string) => Promise<string> | string;
118
113
  }
119
- type RelayBootstrapTrustMode = "legacy-wallet-signed" | "dual-key-attested";
114
+ type RelayBootstrapTrustMode = "wallet-signed" | "dual-key-attested";
120
115
  declare function relayBootstrapMultiaddrsHash(addrs: readonly string[]): string;
121
116
  declare function selectCompactRelayBootstrapMultiaddrs(addrs: readonly string[], limit?: number): string[];
122
117
  declare function dedupeMultiaddrs(addrs: readonly string[]): string[];
@@ -129,14 +124,12 @@ declare function buildRelayBootstrapPostContent(args: {
129
124
  browserMultiaddrs?: string[];
130
125
  registrationId?: string;
131
126
  ref?: string;
132
- postType?: string;
133
127
  profile?: string;
134
128
  version?: string;
135
129
  ownerAddress?: string;
136
130
  publisherAddress?: string;
137
131
  authorization?: RelayBootstrapAuthorizationRecord;
138
132
  relayProof?: RelayBootstrapProofRecord;
139
- compact?: boolean;
140
133
  compactMultiaddrLimit?: number;
141
134
  now?: number;
142
135
  }): RelayBootstrapPostContent;
@@ -172,7 +165,6 @@ declare function signRelayBootstrapProof(args: {
172
165
  profile?: string;
173
166
  version?: string;
174
167
  updatedAt?: number;
175
- compact?: boolean;
176
168
  compactMultiaddrLimit?: number;
177
169
  signer: RelayBootstrapProofSigner;
178
170
  }): Promise<RelayBootstrapProofRecord>;
@@ -196,4 +188,4 @@ declare function createLibp2pAlephBootstrap(options?: DiscoverAlephBootstrapOpti
196
188
  tagName?: string;
197
189
  }): Promise<ReturnType<typeof bootstrap>>;
198
190
 
199
- export { type CreateRelayBootstrapPostOptions, DEFAULT_ALEPH_API_HOST, DEFAULT_ALEPH_BOOTSTRAP_CHANNEL, DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE, DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE, DEFAULT_ALEPH_BOOTSTRAP_REF, DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT, DEFAULT_BOOTSTRAP_MAX_AGE_MS, DEFAULT_BOOTSTRAP_MAX_PAGES, DEFAULT_BOOTSTRAP_PAGINATION, type DiscoverAlephBootstrapOptions, type FilterPublicMultiaddrsOptions, RELAY_BOOTSTRAP_SIGNATURE_SCHEME, type RelayBootstrapAuthorizationPayload, type RelayBootstrapAuthorizationRecord, type RelayBootstrapContent, type RelayBootstrapPostContent, type RelayBootstrapPostRecord, type RelayBootstrapProofPayload, type RelayBootstrapProofRecord, type RelayBootstrapProofSigner, type RelayBootstrapTrustMode, type RelayBootstrapVerificationResult, buildRelayBootstrapPostContent, createLibp2pAlephBootstrap, createRelayBootstrapPost, dedupeMultiaddrs, discoverAlephBootstrapMultiaddrs, fetchAlephBootstrapPosts, filterPublicMultiaddrs, isPublicMultiaddr, relayBootstrapMultiaddrsHash, relayBootstrapTrustMode, selectCompactRelayBootstrapMultiaddrs, selectCurrentRelayBootstrapPosts, signRelayBootstrapAuthorization, signRelayBootstrapProof, verifyRelayBootstrapAuthorization, verifyRelayBootstrapDualKeyContent, verifyRelayBootstrapProof };
191
+ export { type CreateRelayBootstrapPostOptions, DEFAULT_ALEPH_API_HOST, DEFAULT_ALEPH_BOOTSTRAP_CHANNEL, DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE, DEFAULT_ALEPH_BOOTSTRAP_REF, DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT, DEFAULT_BOOTSTRAP_MAX_AGE_MS, DEFAULT_BOOTSTRAP_MAX_PAGES, DEFAULT_BOOTSTRAP_PAGINATION, type DiscoverAlephBootstrapOptions, type FilterPublicMultiaddrsOptions, RELAY_BOOTSTRAP_SIGNATURE_SCHEME, type RelayBootstrapAuthorizationPayload, type RelayBootstrapAuthorizationRecord, type RelayBootstrapContent, type RelayBootstrapPostContent, type RelayBootstrapPostRecord, type RelayBootstrapProofPayload, type RelayBootstrapProofRecord, type RelayBootstrapProofSigner, type RelayBootstrapTrustMode, type RelayBootstrapVerificationResult, buildRelayBootstrapPostContent, createLibp2pAlephBootstrap, createRelayBootstrapPost, dedupeMultiaddrs, discoverAlephBootstrapMultiaddrs, fetchAlephBootstrapPosts, filterPublicMultiaddrs, isPublicMultiaddr, relayBootstrapMultiaddrsHash, relayBootstrapTrustMode, selectCompactRelayBootstrapMultiaddrs, selectCurrentRelayBootstrapPosts, signRelayBootstrapAuthorization, signRelayBootstrapProof, verifyRelayBootstrapAuthorization, verifyRelayBootstrapDualKeyContent, verifyRelayBootstrapProof };
package/index.js CHANGED
@@ -4,8 +4,7 @@ import { keccak256, recoverMessageAddress, stringToHex } from "viem";
4
4
  var DEFAULT_ALEPH_API_HOST = "https://api.aleph.im";
5
5
  var DEFAULT_ALEPH_BOOTSTRAP_CHANNEL = "simple-todo";
6
6
  var DEFAULT_ALEPH_BOOTSTRAP_REF = "simple-todo-bootstrap";
7
- var DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap";
8
- var DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE = "relay-bootstrap-v2";
7
+ var DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap-v2";
9
8
  var DEFAULT_BOOTSTRAP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
10
9
  var DEFAULT_BOOTSTRAP_PAGINATION = 50;
11
10
  var DEFAULT_BOOTSTRAP_MAX_PAGES = 5;
@@ -40,9 +39,6 @@ function serializeRelayProofPayload(payload) {
40
39
  function relayBootstrapMultiaddrsHash(addrs) {
41
40
  return keccak256(stringToHex(JSON.stringify(dedupeMultiaddrs(addrs))));
42
41
  }
43
- function compactPostType(postType) {
44
- return (postType ?? DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE) === DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
45
- }
46
42
  function browserMultiaddrPreference(addr) {
47
43
  const normalized = addr.toLowerCase();
48
44
  if (normalized.includes("/tcp/443/tls/ws/")) return 0;
@@ -56,7 +52,8 @@ function browserMultiaddrTransport(addr) {
56
52
  const normalized = addr.toLowerCase();
57
53
  if (normalized.includes("/webtransport/")) return "webtransport";
58
54
  if (normalized.includes("/webrtc-direct/")) return "webrtc";
59
- if (normalized.includes("/ws") || normalized.includes("/wss")) return "websocket";
55
+ if (normalized.includes("/ws") || normalized.includes("/wss"))
56
+ return "websocket";
60
57
  return "other";
61
58
  }
62
59
  function selectCompactRelayBootstrapMultiaddrs(addrs, limit = DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT) {
@@ -139,7 +136,10 @@ function hasPeerId(addr) {
139
136
  }
140
137
  function isBrowserDialableMultiaddr(addr) {
141
138
  const normalized = addr.toLowerCase();
142
- return normalized.includes("/ws") || normalized.includes("/wss") || normalized.includes("/webtransport") || normalized.includes("/webrtc-direct");
139
+ if (normalized.includes("/webtransport") || normalized.includes("/webrtc-direct")) {
140
+ return normalized.includes("/certhash/");
141
+ }
142
+ return normalized.includes("/ws") || normalized.includes("/wss");
143
143
  }
144
144
  function dedupeMultiaddrs(addrs) {
145
145
  const seen = /* @__PURE__ */ new Set();
@@ -185,8 +185,12 @@ function normalizeRelayBootstrapContent(value) {
185
185
  const peerId = asTrimmedString(content.peerId);
186
186
  const updatedAt = asNumber(content.updatedAt);
187
187
  if (!peerId || updatedAt == null) return null;
188
- const multiaddrs = Array.isArray(content.multiaddrs) ? content.multiaddrs.filter((entry) => typeof entry === "string") : [];
189
- const browserMultiaddrs = Array.isArray(content.browserMultiaddrs) ? content.browserMultiaddrs.filter((entry) => typeof entry === "string") : void 0;
188
+ const multiaddrs = Array.isArray(content.multiaddrs) ? content.multiaddrs.filter(
189
+ (entry) => typeof entry === "string"
190
+ ) : [];
191
+ const browserMultiaddrs = Array.isArray(content.browserMultiaddrs) ? content.browserMultiaddrs.filter(
192
+ (entry) => typeof entry === "string"
193
+ ) : void 0;
190
194
  return {
191
195
  peerId,
192
196
  multiaddrs: dedupeMultiaddrs(multiaddrs),
@@ -239,8 +243,12 @@ function normalizeRelayBootstrapProofPayload(value) {
239
243
  const peerId = asTrimmedString(payload.peerId);
240
244
  const updatedAt = asNumber(payload.updatedAt);
241
245
  if (!peerId || updatedAt == null) return null;
242
- const multiaddrs = Array.isArray(payload.multiaddrs) ? payload.multiaddrs.filter((entry) => typeof entry === "string") : [];
243
- const browserMultiaddrs = Array.isArray(payload.browserMultiaddrs) ? payload.browserMultiaddrs.filter((entry) => typeof entry === "string") : void 0;
246
+ const multiaddrs = Array.isArray(payload.multiaddrs) ? payload.multiaddrs.filter(
247
+ (entry) => typeof entry === "string"
248
+ ) : [];
249
+ const browserMultiaddrs = Array.isArray(payload.browserMultiaddrs) ? payload.browserMultiaddrs.filter(
250
+ (entry) => typeof entry === "string"
251
+ ) : void 0;
244
252
  return {
245
253
  peerId,
246
254
  multiaddrs: multiaddrs.length > 0 ? dedupeMultiaddrs(multiaddrs) : void 0,
@@ -278,8 +286,6 @@ function normalizeRelayBootstrapPostRecord(value) {
278
286
  function buildRelayBootstrapPostContent(args) {
279
287
  const now = args.now ?? Date.now() / 1e3;
280
288
  const updatedAt = args.now ?? Date.now();
281
- const postType = args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE;
282
- const compact = args.compact ?? compactPostType(postType);
283
289
  const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
284
290
  const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
285
291
  browserDialableOnly: true
@@ -289,13 +295,13 @@ function buildRelayBootstrapPostContent(args) {
289
295
  args.compactMultiaddrLimit
290
296
  );
291
297
  return {
292
- type: postType,
298
+ type: DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
293
299
  address: args.sender,
294
300
  ...args.ref ? { ref: args.ref } : {},
295
301
  content: {
296
302
  peerId: args.peerId,
297
- multiaddrs: compact ? compactMultiaddrs : fullMultiaddrs,
298
- browserMultiaddrs: compact ? void 0 : fullBrowserMultiaddrs,
303
+ multiaddrs: compactMultiaddrs,
304
+ browserMultiaddrs: void 0,
299
305
  registrationId: args.registrationId,
300
306
  profile: args.profile,
301
307
  version: args.version,
@@ -318,14 +324,12 @@ async function createRelayBootstrapPost(args) {
318
324
  browserMultiaddrs: args.browserMultiaddrs,
319
325
  registrationId: args.registrationId,
320
326
  ref: args.ref ?? DEFAULT_ALEPH_BOOTSTRAP_REF,
321
- postType: args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
322
327
  profile: args.profile,
323
328
  version: args.version,
324
329
  ownerAddress: args.ownerAddress,
325
330
  publisherAddress: args.publisherAddress,
326
331
  authorization: args.authorization,
327
332
  relayProof: args.relayProof,
328
- compact: args.compact,
329
333
  compactMultiaddrLimit: args.compactMultiaddrLimit,
330
334
  now: nowMillis
331
335
  });
@@ -347,7 +351,7 @@ function relayBootstrapTrustMode(content) {
347
351
  if (content?.authorization && content?.relayProof && content.authorization.payload.publisherAddress && content.relayProof.payload.peerId) {
348
352
  return "dual-key-attested";
349
353
  }
350
- return "legacy-wallet-signed";
354
+ return "wallet-signed";
351
355
  }
352
356
  async function signRelayBootstrapAuthorization(args) {
353
357
  const payload = {
@@ -378,12 +382,11 @@ async function signRelayBootstrapProof(args) {
378
382
  fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
379
383
  args.compactMultiaddrLimit
380
384
  );
381
- const compact = args.compact ?? false;
382
385
  const payload = {
383
386
  peerId: args.peerId,
384
- multiaddrs: compact ? void 0 : fullMultiaddrs,
385
- browserMultiaddrs: compact ? void 0 : fullBrowserMultiaddrs,
386
- multiaddrsHash: compact ? relayBootstrapMultiaddrsHash(compactMultiaddrs) : void 0,
387
+ multiaddrs: void 0,
388
+ browserMultiaddrs: void 0,
389
+ multiaddrsHash: relayBootstrapMultiaddrsHash(compactMultiaddrs),
387
390
  registrationId: args.registrationId,
388
391
  profile: args.profile,
389
392
  version: args.version,
@@ -404,7 +407,9 @@ async function verifyRelayBootstrapAuthorization(authorization, options = {}) {
404
407
  return { ok: false, errors };
405
408
  }
406
409
  if (authorization.scheme !== RELAY_BOOTSTRAP_SIGNATURE_SCHEME) {
407
- errors.push(`Unsupported owner authorization scheme: ${authorization.scheme}`);
410
+ errors.push(
411
+ `Unsupported owner authorization scheme: ${authorization.scheme}`
412
+ );
408
413
  return { ok: false, errors };
409
414
  }
410
415
  const payload = authorization.payload;
@@ -415,7 +420,9 @@ async function verifyRelayBootstrapAuthorization(authorization, options = {}) {
415
420
  authorization.signature
416
421
  );
417
422
  if (normalizeAddress(recovered) !== normalizeAddress(payload.ownerAddress)) {
418
- errors.push("Owner authorization signature does not recover the owner address.");
423
+ errors.push(
424
+ "Owner authorization signature does not recover the owner address."
425
+ );
419
426
  }
420
427
  } catch (error) {
421
428
  errors.push(
@@ -441,9 +448,14 @@ async function verifyRelayBootstrapProof(proof, options = {}) {
441
448
  const payload = proof.payload;
442
449
  const serialized = serializeRelayProofPayload(payload);
443
450
  try {
444
- const recovered = await recoverAddressForSignature(serialized, proof.signature);
451
+ const recovered = await recoverAddressForSignature(
452
+ serialized,
453
+ proof.signature
454
+ );
445
455
  if (options.expectedPublisherAddress && normalizeAddress(recovered) !== normalizeAddress(options.expectedPublisherAddress)) {
446
- errors.push("Relay proof signature does not recover the expected publisher address.");
456
+ errors.push(
457
+ "Relay proof signature does not recover the expected publisher address."
458
+ );
447
459
  }
448
460
  } catch (error) {
449
461
  errors.push(
@@ -474,7 +486,9 @@ async function verifyRelayBootstrapDualKeyContent(content, options = {}) {
474
486
  errors.push(...proof.errors);
475
487
  if (content.ownerAddress && content.authorization) {
476
488
  if (normalizeAddress(content.ownerAddress) !== normalizeAddress(content.authorization.payload.ownerAddress)) {
477
- errors.push("Content ownerAddress does not match the owner authorization payload.");
489
+ errors.push(
490
+ "Content ownerAddress does not match the owner authorization payload."
491
+ );
478
492
  }
479
493
  }
480
494
  if (expectedPublisherAddress && content.authorization) {
@@ -485,12 +499,16 @@ async function verifyRelayBootstrapDualKeyContent(content, options = {}) {
485
499
  }
486
500
  }
487
501
  if (content.authorization && content.authorization.payload.peerId !== content.peerId) {
488
- errors.push("Owner authorization peer ID does not match the bootstrap content peer ID.");
502
+ errors.push(
503
+ "Owner authorization peer ID does not match the bootstrap content peer ID."
504
+ );
489
505
  }
490
506
  const proofPayload = content.relayProof?.payload;
491
507
  if (proofPayload) {
492
508
  if (proofPayload.registrationId !== content.registrationId) {
493
- errors.push("Relay proof registrationId does not match the bootstrap content.");
509
+ errors.push(
510
+ "Relay proof registrationId does not match the bootstrap content."
511
+ );
494
512
  }
495
513
  if (proofPayload.profile !== content.profile) {
496
514
  errors.push("Relay proof profile does not match the bootstrap content.");
@@ -499,11 +517,15 @@ async function verifyRelayBootstrapDualKeyContent(content, options = {}) {
499
517
  errors.push("Relay proof version does not match the bootstrap content.");
500
518
  }
501
519
  if (proofPayload.updatedAt !== content.updatedAt) {
502
- errors.push("Relay proof updatedAt does not match the bootstrap content.");
520
+ errors.push(
521
+ "Relay proof updatedAt does not match the bootstrap content."
522
+ );
503
523
  }
504
524
  if (proofPayload.multiaddrsHash) {
505
525
  if (proofPayload.multiaddrsHash !== relayBootstrapMultiaddrsHash(content.multiaddrs)) {
506
- errors.push("Relay proof multiaddrs hash does not match the bootstrap content.");
526
+ errors.push(
527
+ "Relay proof multiaddrs hash does not match the bootstrap content."
528
+ );
507
529
  }
508
530
  } else if (JSON.stringify(dedupeMultiaddrs(proofPayload.multiaddrs ?? [])) !== JSON.stringify(dedupeMultiaddrs(content.multiaddrs))) {
509
531
  errors.push("Relay proof multiaddrs do not match the bootstrap content.");
@@ -515,8 +537,12 @@ async function verifyRelayBootstrapDualKeyContent(content, options = {}) {
515
537
  );
516
538
  }
517
539
  } else if (proofPayload.browserMultiaddrs || content.browserMultiaddrs) {
518
- if (JSON.stringify(dedupeMultiaddrs(proofPayload.browserMultiaddrs ?? [])) !== JSON.stringify(dedupeMultiaddrs(content.browserMultiaddrs ?? []))) {
519
- errors.push("Relay proof browserMultiaddrs do not match the bootstrap content.");
540
+ if (JSON.stringify(
541
+ dedupeMultiaddrs(proofPayload.browserMultiaddrs ?? [])
542
+ ) !== JSON.stringify(dedupeMultiaddrs(content.browserMultiaddrs ?? []))) {
543
+ errors.push(
544
+ "Relay proof browserMultiaddrs do not match the bootstrap content."
545
+ );
520
546
  }
521
547
  }
522
548
  }
@@ -538,10 +564,7 @@ async function fetchAlephBootstrapPosts(options = {}) {
538
564
  options.channel ?? DEFAULT_ALEPH_BOOTSTRAP_CHANNEL
539
565
  );
540
566
  url.searchParams.set("refs", options.ref ?? DEFAULT_ALEPH_BOOTSTRAP_REF);
541
- url.searchParams.set(
542
- "types",
543
- options.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE
544
- );
567
+ url.searchParams.set("types", DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE);
545
568
  url.searchParams.set(
546
569
  "pagination",
547
570
  String(options.pagination ?? DEFAULT_BOOTSTRAP_PAGINATION)
@@ -555,7 +578,21 @@ async function fetchAlephBootstrapPosts(options = {}) {
555
578
  throw new Error(`Aleph bootstrap post lookup failed: ${response.status}`);
556
579
  }
557
580
  const payload = await response.json();
558
- return (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null).sort((left, right) => compareRelayBootstrapPostRecency(right, left));
581
+ const posts = (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null).sort((left, right) => compareRelayBootstrapPostRecency(right, left));
582
+ posts.forEach(assertSupportedRelayBootstrapPost);
583
+ return posts;
584
+ }
585
+ function assertSupportedRelayBootstrapPost(post) {
586
+ if (post.type === "relay-bootstrap") {
587
+ throw new Error(
588
+ "Legacy relay-bootstrap record encountered. Only relay-bootstrap-v2 is supported."
589
+ );
590
+ }
591
+ if (post.type && post.type !== DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE) {
592
+ throw new Error(
593
+ `Unsupported relay bootstrap post type: ${post.type}. Expected ${DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE}.`
594
+ );
595
+ }
559
596
  }
560
597
  function compareRelayBootstrapPostRecency(left, right) {
561
598
  const leftUpdatedAt = left.content?.updatedAt ?? 0;
@@ -577,6 +614,7 @@ function selectCurrentRelayBootstrapPosts(posts, options = {}) {
577
614
  const now = options.now ?? Date.now();
578
615
  const selected = /* @__PURE__ */ new Map();
579
616
  for (const post of posts) {
617
+ assertSupportedRelayBootstrapPost(post);
580
618
  const content = post.content;
581
619
  if (!content) continue;
582
620
  if (now - content.updatedAt > maxAgeMs) continue;
@@ -599,7 +637,7 @@ async function filterTrustedRelayBootstrapPosts(posts, options = {}) {
599
637
  const content = post.content;
600
638
  if (!content) continue;
601
639
  const trustMode = relayBootstrapTrustMode(content);
602
- if (trustMode === "legacy-wallet-signed") {
640
+ if (trustMode === "wallet-signed") {
603
641
  if (!requireDualKeyAttestation) {
604
642
  trusted.push(post);
605
643
  }
@@ -621,38 +659,31 @@ async function discoverAlephBootstrapMultiaddrs(options = {}) {
621
659
  const pagination = options.pagination ?? DEFAULT_BOOTSTRAP_PAGINATION;
622
660
  const startPage = options.page ?? 1;
623
661
  const maxPages = Math.max(1, options.maxPages ?? DEFAULT_BOOTSTRAP_MAX_PAGES);
624
- const postTypes = options.postType != null ? [options.postType] : options.postTypes && options.postTypes.length > 0 ? options.postTypes : [
625
- DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE,
626
- DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE
627
- ];
628
- for (const postType of postTypes) {
629
- const collectedPosts = [];
630
- for (let offset = 0; offset < maxPages; offset += 1) {
631
- const page = startPage + offset;
632
- const pagePosts = await fetchAlephBootstrapPosts({
633
- ...options,
634
- postType,
635
- page,
636
- pagination
637
- });
638
- collectedPosts.push(...pagePosts);
639
- const selectedPosts = selectCurrentRelayBootstrapPosts(collectedPosts, {
640
- maxAgeMs: options.maxAgeMs
641
- });
642
- const trustedPosts = await filterTrustedRelayBootstrapPosts(selectedPosts, {
643
- requireDualKeyAttestation: options.requireDualKeyAttestation,
644
- verifyDualKeyAttestation: options.verifyDualKeyAttestation
645
- });
646
- const addrs = relayBootstrapPostsToMultiaddrs(
647
- trustedPosts,
648
- browserDialableOnly
649
- );
650
- if (addrs.length > 0) {
651
- return addrs;
652
- }
653
- if (pagePosts.length < pagination) {
654
- break;
655
- }
662
+ const collectedPosts = [];
663
+ for (let offset = 0; offset < maxPages; offset += 1) {
664
+ const page = startPage + offset;
665
+ const pagePosts = await fetchAlephBootstrapPosts({
666
+ ...options,
667
+ page,
668
+ pagination
669
+ });
670
+ collectedPosts.push(...pagePosts);
671
+ const selectedPosts = selectCurrentRelayBootstrapPosts(collectedPosts, {
672
+ maxAgeMs: options.maxAgeMs
673
+ });
674
+ const trustedPosts = await filterTrustedRelayBootstrapPosts(selectedPosts, {
675
+ requireDualKeyAttestation: options.requireDualKeyAttestation,
676
+ verifyDualKeyAttestation: options.verifyDualKeyAttestation
677
+ });
678
+ const addrs = relayBootstrapPostsToMultiaddrs(
679
+ trustedPosts,
680
+ browserDialableOnly
681
+ );
682
+ if (addrs.length > 0) {
683
+ return addrs;
684
+ }
685
+ if (pagePosts.length < pagination) {
686
+ break;
656
687
  }
657
688
  }
658
689
  return [];
@@ -682,7 +713,6 @@ async function createLibp2pAlephBootstrap(options = {}) {
682
713
  export {
683
714
  DEFAULT_ALEPH_API_HOST,
684
715
  DEFAULT_ALEPH_BOOTSTRAP_CHANNEL,
685
- DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE,
686
716
  DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
687
717
  DEFAULT_ALEPH_BOOTSTRAP_REF,
688
718
  DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/aleph-bootstrap",
3
- "version": "0.6.24",
3
+ "version": "0.6.26",
4
4
  "description": "Aleph-backed libp2p bootstrap discovery and relay registration helpers.",
5
5
  "license": "MIT",
6
6
  "type": "module",