@serfab/cadre-core 1.14.0 → 1.16.0

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 (171) hide show
  1. package/README.md +293 -274
  2. package/dist/cadre-invite-protocol.d.ts +64 -29
  3. package/dist/cadre-invite-protocol.js +207 -38
  4. package/dist/cadre-invite-protocol.js.map +1 -1
  5. package/dist/cadre-join-request.d.ts +42 -0
  6. package/dist/cadre-join-request.js +144 -0
  7. package/dist/cadre-join-request.js.map +1 -0
  8. package/dist/cadre-node.d.ts +375 -79
  9. package/dist/cadre-node.js +758 -186
  10. package/dist/cadre-node.js.map +1 -1
  11. package/dist/control-database.d.ts +92 -12
  12. package/dist/control-database.js +323 -142
  13. package/dist/control-database.js.map +1 -1
  14. package/dist/control-schema.d.ts +1 -1
  15. package/dist/control-schema.js +19 -3
  16. package/dist/control-schema.js.map +1 -1
  17. package/dist/ed25519-key.d.ts +7 -9
  18. package/dist/ed25519-key.js +20 -10
  19. package/dist/ed25519-key.js.map +1 -1
  20. package/dist/fetch-impl.d.ts +12 -0
  21. package/dist/fetch-impl.js +23 -0
  22. package/dist/fetch-impl.js.map +1 -0
  23. package/dist/file-durable-slot.js +2 -1
  24. package/dist/file-durable-slot.js.map +1 -1
  25. package/dist/file-safe-name.d.ts +25 -0
  26. package/dist/file-safe-name.js +29 -0
  27. package/dist/file-safe-name.js.map +1 -0
  28. package/dist/formation-approval.js +5 -11
  29. package/dist/formation-approval.js.map +1 -1
  30. package/dist/fs-atomic.d.ts +0 -19
  31. package/dist/fs-atomic.js +3 -24
  32. package/dist/fs-atomic.js.map +1 -1
  33. package/dist/index.d.ts +12 -5
  34. package/dist/index.js +31 -6
  35. package/dist/index.js.map +1 -1
  36. package/dist/invitation-bootstrap.d.ts +50 -12
  37. package/dist/invitation-bootstrap.js +76 -13
  38. package/dist/invitation-bootstrap.js.map +1 -1
  39. package/dist/joined-strand-store.d.ts +7 -0
  40. package/dist/joined-strand-store.js +9 -0
  41. package/dist/joined-strand-store.js.map +1 -1
  42. package/dist/key-store-file.js +2 -1
  43. package/dist/key-store-file.js.map +1 -1
  44. package/dist/link-budget.d.ts +9 -4
  45. package/dist/link-budget.js +9 -4
  46. package/dist/link-budget.js.map +1 -1
  47. package/dist/machine-removal.d.ts +78 -0
  48. package/dist/machine-removal.js +166 -0
  49. package/dist/machine-removal.js.map +1 -0
  50. package/dist/membership-connection-gater.d.ts +6 -5
  51. package/dist/membership-connection-gater.js +6 -5
  52. package/dist/membership-connection-gater.js.map +1 -1
  53. package/dist/node-claim-payload.d.ts +55 -0
  54. package/dist/node-claim-payload.js +108 -4
  55. package/dist/node-claim-payload.js.map +1 -1
  56. package/dist/node-default-transports.d.ts +27 -0
  57. package/dist/node-default-transports.js +35 -0
  58. package/dist/node-default-transports.js.map +1 -0
  59. package/dist/node-transports.d.ts +30 -0
  60. package/dist/node-transports.js +40 -0
  61. package/dist/node-transports.js.map +1 -0
  62. package/dist/peer-dial.d.ts +124 -27
  63. package/dist/peer-dial.js +254 -30
  64. package/dist/peer-dial.js.map +1 -1
  65. package/dist/peer-join-backfill.js +6 -0
  66. package/dist/peer-join-backfill.js.map +1 -1
  67. package/dist/peer-record.d.ts +6 -45
  68. package/dist/peer-record.js +8 -50
  69. package/dist/peer-record.js.map +1 -1
  70. package/dist/primary-lan-address.d.ts +8 -0
  71. package/dist/primary-lan-address.js +46 -0
  72. package/dist/primary-lan-address.js.map +1 -0
  73. package/dist/provider/client.d.ts +131 -0
  74. package/dist/provider/client.js +226 -0
  75. package/dist/provider/client.js.map +1 -0
  76. package/dist/provider/descriptor.d.ts +41 -0
  77. package/dist/provider/descriptor.js +103 -0
  78. package/dist/provider/descriptor.js.map +1 -0
  79. package/dist/provider/index.d.ts +9 -0
  80. package/dist/provider/index.js +10 -0
  81. package/dist/provider/index.js.map +1 -0
  82. package/dist/provider/oauth.d.ts +125 -0
  83. package/dist/provider/oauth.js +199 -0
  84. package/dist/provider/oauth.js.map +1 -0
  85. package/dist/provider/rent-node.d.ts +101 -0
  86. package/dist/provider/rent-node.js +220 -0
  87. package/dist/provider/rent-node.js.map +1 -0
  88. package/dist/relay-addrs.d.ts +9 -4
  89. package/dist/relay-addrs.js +9 -4
  90. package/dist/relay-addrs.js.map +1 -1
  91. package/dist/relay-reservation.d.ts +51 -16
  92. package/dist/relay-reservation.js +107 -24
  93. package/dist/relay-reservation.js.map +1 -1
  94. package/dist/seed-bootstrap.d.ts +22 -5
  95. package/dist/seed-bootstrap.js +48 -25
  96. package/dist/seed-bootstrap.js.map +1 -1
  97. package/dist/strand-commit-durability.d.ts +54 -0
  98. package/dist/strand-commit-durability.js +74 -0
  99. package/dist/strand-commit-durability.js.map +1 -0
  100. package/dist/strand-database.d.ts +23 -8
  101. package/dist/strand-database.js +41 -13
  102. package/dist/strand-database.js.map +1 -1
  103. package/dist/strand-first-sync-gate.d.ts +7 -1
  104. package/dist/strand-first-sync-gate.js +7 -1
  105. package/dist/strand-first-sync-gate.js.map +1 -1
  106. package/dist/strand-formation-protocol.js +4 -1
  107. package/dist/strand-formation-protocol.js.map +1 -1
  108. package/dist/strand-fret-addrs.d.ts +19 -1
  109. package/dist/strand-fret-addrs.js +27 -9
  110. package/dist/strand-fret-addrs.js.map +1 -1
  111. package/dist/strand-instance-manager.d.ts +81 -19
  112. package/dist/strand-instance-manager.js +216 -105
  113. package/dist/strand-instance-manager.js.map +1 -1
  114. package/dist/strand-move.d.ts +124 -0
  115. package/dist/strand-move.js +438 -0
  116. package/dist/strand-move.js.map +1 -0
  117. package/dist/strand-network-config.js +7 -6
  118. package/dist/strand-network-config.js.map +1 -1
  119. package/dist/strand-peer-redial.d.ts +113 -0
  120. package/dist/strand-peer-redial.js +236 -0
  121. package/dist/strand-peer-redial.js.map +1 -0
  122. package/dist/strand-wake-protocol.js +10 -7
  123. package/dist/strand-wake-protocol.js.map +1 -1
  124. package/dist/types.d.ts +212 -4
  125. package/dist/types.js +10 -0
  126. package/dist/types.js.map +1 -1
  127. package/package.json +18 -10
  128. package/src/cadre-invite-protocol.ts +1052 -864
  129. package/src/cadre-join-request.ts +167 -0
  130. package/src/cadre-node.ts +10003 -9381
  131. package/src/control-database.ts +4663 -4451
  132. package/src/control-schema.ts +19 -3
  133. package/src/ed25519-key.ts +20 -10
  134. package/src/fetch-impl.ts +23 -0
  135. package/src/file-durable-slot.ts +64 -63
  136. package/src/file-safe-name.ts +32 -0
  137. package/src/formation-approval.ts +604 -610
  138. package/src/fs-atomic.ts +92 -117
  139. package/src/index.ts +71 -4
  140. package/src/invitation-bootstrap.ts +91 -13
  141. package/src/joined-strand-store.ts +508 -498
  142. package/src/key-store-file.ts +142 -141
  143. package/src/link-budget.ts +9 -4
  144. package/src/machine-removal.ts +216 -0
  145. package/src/membership-connection-gater.ts +638 -637
  146. package/src/node-claim-payload.ts +138 -4
  147. package/src/node-default-transports.ts +38 -0
  148. package/src/node-transports.ts +49 -0
  149. package/src/peer-dial.ts +604 -336
  150. package/src/peer-join-backfill.ts +6 -0
  151. package/src/peer-record.ts +159 -202
  152. package/src/primary-lan-address.ts +47 -0
  153. package/src/provider/client.ts +326 -0
  154. package/src/provider/descriptor.ts +129 -0
  155. package/src/provider/index.ts +59 -0
  156. package/src/provider/oauth.ts +285 -0
  157. package/src/provider/rent-node.ts +275 -0
  158. package/src/relay-addrs.ts +9 -4
  159. package/src/relay-reservation.ts +1211 -1121
  160. package/src/seed-bootstrap.ts +1502 -1471
  161. package/src/strand-commit-durability.ts +116 -0
  162. package/src/strand-database.ts +46 -14
  163. package/src/strand-first-sync-gate.ts +7 -1
  164. package/src/strand-formation-protocol.ts +1039 -1036
  165. package/src/strand-fret-addrs.ts +179 -145
  166. package/src/strand-instance-manager.ts +2026 -1883
  167. package/src/strand-move.ts +585 -0
  168. package/src/strand-network-config.ts +7 -6
  169. package/src/strand-peer-redial.ts +307 -0
  170. package/src/strand-wake-protocol.ts +10 -7
  171. package/src/types.ts +220 -4
@@ -1,145 +1,179 @@
1
- /**
2
- * Reading the signed address records a running strand node's FRET routing table holds,
3
- * as per-peer addresses ready for that node's own address book (`peer-addr-book.ts`).
4
- *
5
- * Why a strand node needs this. libp2p's peerStore hides every address one hour after
6
- * it was first observed (the `NOTE:` on `mergePeerAddrs`), and FRET does not re-apply
7
- * the records it holds: it hands a record to the peerStore when a neighbour's snapshot
8
- * delivers it or a saved table is imported, and the peerStore refuses a record whose
9
- * sequence number it already has. So a strand node that has been up for more than an
10
- * hour would have no address for a peer whose connection then drops, although FRET
11
- * still holds that peer's record. `CadreNode.refreshStrandPeerAddrs` re-merges the
12
- * records on every pass, which is what keeps another party's strand nodes dialable;
13
- * own-party siblings are re-resolved separately, over the strand-addr RPC.
14
- *
15
- * How long a record is available here is FRET's rule, not this module's: while FRET
16
- * holds the entry, and at most 14 days after the record was last confirmed (a peer
17
- * connected at export time counts as confirmed then).
18
- *
19
- * Every record is opened and verified here rather than taken on FRET's word. A record
20
- * is self-certifying, the table is reached through a structural type rather than an
21
- * interface this package depends on, and the address book write is attributed from what
22
- * the record proves, not from the id the table filed it under.
23
- */
24
-
25
- import debug from 'debug';
26
- import { PeerRecord, RecordEnvelope } from '@libp2p/peer-record';
27
- import { peerIdFromPublicKey } from '@libp2p/peer-id';
28
- import { multiaddr, type Multiaddr } from '@multiformats/multiaddr';
29
- import { fromString as uint8ArrayFromString } from 'uint8arrays';
30
- import { withTrailingPeerId } from './peer-record.js';
31
-
32
- const log = debug('sereus:cadre:strand-fret-addrs');
33
-
34
- /** The slice of FRET's exported routing table read here (`SerializedTable` in `p2p-fret`). */
35
- interface FretTableSlice {
36
- entries: ReadonlyArray<{
37
- id: string;
38
- /** `envelope` is the base64url of a marshaled libp2p record envelope. */
39
- addressRecord?: { envelope: string };
40
- }>;
41
- }
42
-
43
- /** The slice of FRET's libp2p service called here. */
44
- interface FretTableSource {
45
- exportTable(): FretTableSlice;
46
- }
47
-
48
- /**
49
- * The slice of a strand's libp2p node this module touches. Structurally satisfied by
50
- * `Libp2p`; db-p2p registers FRET as `services.fret`.
51
- */
52
- export interface FretAddrHost {
53
- peerId: { toString(): string };
54
- services?: Record<string, unknown>;
55
- }
56
-
57
- /** What one read of a strand node's FRET table yields. */
58
- export interface FretPeerAddrs {
59
- /** Peer id → the addresses its record lists, each bound to that peer. Table order. */
60
- peers: Map<string, Multiaddr[]>;
61
- /**
62
- * Records left out because they did not decode, did not verify, or were not signed
63
- * by and about the peer the table filed them under.
64
- */
65
- rejected: number;
66
- }
67
-
68
- /**
69
- * The addresses of every peer `host`'s FRET table holds a usable signed record for,
70
- * `host` itself excluded. A node with no FRET service (a db-p2p build without it, a
71
- * test double) reads as an empty table. One bad record never costs the others: it is
72
- * counted in `rejected` and skipped.
73
- *
74
- * Throws only what `exportTable()` throws, which a caller treats like any other
75
- * failure of a pass over a node that may be mid-teardown.
76
- */
77
- export async function strandFretPeerAddrs(host: FretAddrHost): Promise<FretPeerAddrs> {
78
- const result: FretPeerAddrs = { peers: new Map(), rejected: 0 };
79
- const fret = host.services?.fret;
80
- if (!isFretTableSource(fret)) {
81
- return result;
82
- }
83
- const selfId = host.peerId.toString();
84
- for (const entry of fret.exportTable().entries) {
85
- if (entry.id === selfId || entry.addressRecord === undefined) {
86
- continue;
87
- }
88
- const addrs = await verifiedRecordAddrs(entry.id, entry.addressRecord.envelope);
89
- if (addrs === null) {
90
- result.rejected++;
91
- } else if (addrs.length > 0) {
92
- result.peers.set(entry.id, addrs);
93
- }
94
- }
95
- return result;
96
- }
97
-
98
- function isFretTableSource(service: unknown): service is FretTableSource {
99
- return typeof service === 'object' && service !== null
100
- && typeof (service as { exportTable?: unknown }).exportTable === 'function';
101
- }
102
-
103
- /**
104
- * The addresses one record lists, bound to `peerId`; null when the record must not be
105
- * used. `openAndCertify` proves only that the envelope's own key signed it, so the
106
- * signer and the peer the payload describes are each held against `peerId` here:
107
- * without that, any peer could sign a record rewriting another peer's addresses.
108
- */
109
- async function verifiedRecordAddrs(peerId: string, envelope: string): Promise<Multiaddr[] | null> {
110
- try {
111
- const opened = await RecordEnvelope.openAndCertify(uint8ArrayFromString(envelope, 'base64url'), PeerRecord.DOMAIN);
112
- const record = PeerRecord.createFromProtobuf(opened.payload);
113
- const signer = peerIdFromPublicKey(opened.publicKey).toString();
114
- const subject = record.peerId.toString();
115
- if (signer !== peerId || subject !== peerId) {
116
- log('skipping the address record filed under %s: signed by %s about %s', peerId, signer, subject);
117
- return null;
118
- }
119
- return bindToPeer(record.multiaddrs.map((addr) => addr.toString()), peerId);
120
- } catch (error) {
121
- log('skipping the address record filed under %s: %o', peerId, error);
122
- return null;
123
- }
124
- }
125
-
126
- /**
127
- * A record names its peer once, in `peerId`, so its addresses carry no trailing
128
- * `/p2p/<peerId>` and a relay-only peer's circuit address ends at the relay hop
129
- * (`…/p2p/<relay>/p2p-circuit`). Each is given the suffix that makes it a complete
130
- * dial target; one already terminating in a different peer id does not reach this peer
131
- * and is dropped.
132
- *
133
- * Takes strings because `@libp2p/peer-record` carries its own `@multiformats/multiaddr`
134
- * copy, a structurally different type from the one `withTrailingPeerId` takes.
135
- */
136
- function bindToPeer(addrs: string[], peerId: string): Multiaddr[] {
137
- const bound: Multiaddr[] = [];
138
- for (const addr of addrs) {
139
- const withPeer = withTrailingPeerId(multiaddr(addr), peerId);
140
- if (withPeer !== null) {
141
- bound.push(withPeer);
142
- }
143
- }
144
- return bound;
145
- }
1
+ /**
2
+ * Reading the signed address records a running strand node's FRET routing table holds,
3
+ * as per-peer addresses ready for that node's own address book (`peer-addr-book.ts`).
4
+ *
5
+ * Why a strand node needs this. libp2p's peerStore hides every address one hour after
6
+ * it was first observed (the `NOTE:` on `mergePeerAddrs`), and FRET does not re-apply
7
+ * the records it holds: it hands a record to the peerStore when a neighbour's snapshot
8
+ * delivers it or a saved table is imported, and the peerStore refuses a record whose
9
+ * sequence number it already has. So a strand node that has been up for more than an
10
+ * hour would have no address for a peer whose connection then drops, although FRET
11
+ * still holds that peer's record. `CadreNode.refreshStrandPeerAddrs` re-merges the
12
+ * records on every pass, which is what keeps another party's strand nodes dialable;
13
+ * own-party siblings are re-resolved separately, over the strand-addr RPC. The strand
14
+ * peer redial (`strand-peer-redial.ts`) reads the same records, and FRET's membership
15
+ * labels, to choose whom to dial.
16
+ *
17
+ * How long a record is available here is FRET's rule, not this module's: while FRET
18
+ * holds the entry, and at most 14 days after the record was last confirmed (a peer
19
+ * connected at export time counts as confirmed then).
20
+ *
21
+ * Every record is opened and verified here rather than taken on FRET's word. A record
22
+ * is self-certifying, the table is reached through a structural type rather than an
23
+ * interface this package depends on, and the address book write is attributed from what
24
+ * the record proves, not from the id the table filed it under.
25
+ */
26
+
27
+ import debug from 'debug';
28
+ import { PeerRecord, RecordEnvelope } from '@libp2p/peer-record';
29
+ import { peerIdFromPublicKey } from '@libp2p/peer-id';
30
+ import { multiaddr, type Multiaddr } from '@multiformats/multiaddr';
31
+ import { fromString as uint8ArrayFromString } from 'uint8arrays';
32
+ import { withTrailingPeerId } from './peer-dial.js';
33
+
34
+ const log = debug('sereus:cadre:strand-fret-addrs');
35
+
36
+ /** The slice of FRET's exported routing table read here (`SerializedTable` in `p2p-fret`). */
37
+ interface FretTableSlice {
38
+ entries: ReadonlyArray<{
39
+ id: string;
40
+ /** `envelope` is the base64url of a marshaled libp2p record envelope. */
41
+ addressRecord?: { envelope: string };
42
+ /** FRET's `MembershipState`: `'member'`, `'foreign'` or `'unknown'`; absent in tables that predate it. */
43
+ membership?: string;
44
+ }>;
45
+ }
46
+
47
+ /** The slice of FRET's libp2p service called here. */
48
+ interface FretTableSource {
49
+ exportTable(): FretTableSlice;
50
+ }
51
+
52
+ /**
53
+ * The slice of a strand's libp2p node this module touches. Structurally satisfied by
54
+ * `Libp2p`; db-p2p registers FRET as `services.fret`.
55
+ */
56
+ export interface FretAddrHost {
57
+ peerId: { toString(): string };
58
+ services?: Record<string, unknown>;
59
+ }
60
+
61
+ /** What one read of a strand node's FRET table yields. */
62
+ export interface FretPeerAddrs {
63
+ /** Peer id → the addresses its record lists, each bound to that peer. Table order. */
64
+ peers: Map<string, Multiaddr[]>;
65
+ /**
66
+ * Records left out because they did not decode, did not verify, or were not signed
67
+ * by and about the peer the table filed them under.
68
+ */
69
+ rejected: number;
70
+ }
71
+
72
+ /**
73
+ * Whether FRET has seen a peer speak this strand network's protocols (`'member'`), seen it
74
+ * list protocols without any of them (`'foreign'`), or not judged it yet (`'unknown'`).
75
+ *
76
+ * The relay a strand node reserves on is `'foreign'`, and that label is the only thing in
77
+ * the table that tells it from a member: FRET files every connected peer, and adopts the
78
+ * signed record identify delivers for it, so the relay's entry carries a record as a
79
+ * member's does.
80
+ */
81
+ export type FretMembership = 'member' | 'foreign' | 'unknown';
82
+
83
+ /**
84
+ * The addresses of every peer `host`'s FRET table holds a usable signed record for,
85
+ * `host` itself excluded. A node with no FRET service (a db-p2p build without it, a
86
+ * test double) reads as an empty table. One bad record never costs the others: it is
87
+ * counted in `rejected` and skipped.
88
+ *
89
+ * Throws only what `exportTable()` throws, which a caller treats like any other
90
+ * failure of a pass over a node that may be mid-teardown.
91
+ */
92
+ export async function strandFretPeerAddrs(host: FretAddrHost): Promise<FretPeerAddrs> {
93
+ const result: FretPeerAddrs = { peers: new Map(), rejected: 0 };
94
+ for (const entry of fretTableEntries(host)) {
95
+ if (entry.addressRecord === undefined) {
96
+ continue;
97
+ }
98
+ const addrs = await verifiedRecordAddrs(entry.id, entry.addressRecord.envelope);
99
+ if (addrs === null) {
100
+ result.rejected++;
101
+ } else if (addrs.length > 0) {
102
+ result.peers.set(entry.id, addrs);
103
+ }
104
+ }
105
+ return result;
106
+ }
107
+
108
+ /**
109
+ * FRET's membership label for every peer `host`'s table holds, `host` itself excluded.
110
+ * Checks no signature, so a caller that needs only the labels pays for one table export
111
+ * rather than one record verification per peer. Throws what `exportTable()` throws.
112
+ */
113
+ export function strandFretMembership(host: FretAddrHost): Map<string, FretMembership> {
114
+ return new Map(fretTableEntries(host).map((entry) => [entry.id, asMembership(entry.membership)]));
115
+ }
116
+
117
+ /** The table's entries other than `host`'s own; none when the node runs no FRET service. */
118
+ function fretTableEntries(host: FretAddrHost): FretTableSlice['entries'] {
119
+ const fret = host.services?.fret;
120
+ if (!isFretTableSource(fret)) {
121
+ return [];
122
+ }
123
+ const selfId = host.peerId.toString();
124
+ return fret.exportTable().entries.filter((entry) => entry.id !== selfId);
125
+ }
126
+
127
+ /** A label FRET did not write (a table that predates it, or a value this module does not know) is no judgement. */
128
+ function asMembership(label: string | undefined): FretMembership {
129
+ return label === 'member' || label === 'foreign' ? label : 'unknown';
130
+ }
131
+
132
+ function isFretTableSource(service: unknown): service is FretTableSource {
133
+ return typeof service === 'object' && service !== null
134
+ && typeof (service as { exportTable?: unknown }).exportTable === 'function';
135
+ }
136
+
137
+ /**
138
+ * The addresses one record lists, bound to `peerId`; null when the record must not be
139
+ * used. `openAndCertify` proves only that the envelope's own key signed it, so the
140
+ * signer and the peer the payload describes are each held against `peerId` here:
141
+ * without that, any peer could sign a record rewriting another peer's addresses.
142
+ */
143
+ async function verifiedRecordAddrs(peerId: string, envelope: string): Promise<Multiaddr[] | null> {
144
+ try {
145
+ const opened = await RecordEnvelope.openAndCertify(uint8ArrayFromString(envelope, 'base64url'), PeerRecord.DOMAIN);
146
+ const record = PeerRecord.createFromProtobuf(opened.payload);
147
+ const signer = peerIdFromPublicKey(opened.publicKey).toString();
148
+ const subject = record.peerId.toString();
149
+ if (signer !== peerId || subject !== peerId) {
150
+ log('skipping the address record filed under %s: signed by %s about %s', peerId, signer, subject);
151
+ return null;
152
+ }
153
+ return bindToPeer(record.multiaddrs.map((addr) => addr.toString()), peerId);
154
+ } catch (error) {
155
+ log('skipping the address record filed under %s: %o', peerId, error);
156
+ return null;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * A record names its peer once, in `peerId`, so its addresses carry no trailing
162
+ * `/p2p/<peerId>` and a relay-only peer's circuit address ends at the relay hop
163
+ * (`…/p2p/<relay>/p2p-circuit`). Each is given the suffix that makes it a complete
164
+ * dial target; one already terminating in a different peer id does not reach this peer
165
+ * and is dropped.
166
+ *
167
+ * Takes strings because `@libp2p/peer-record` carries its own `@multiformats/multiaddr`
168
+ * copy, a structurally different type from the one `withTrailingPeerId` takes.
169
+ */
170
+ function bindToPeer(addrs: string[], peerId: string): Multiaddr[] {
171
+ const bound: Multiaddr[] = [];
172
+ for (const addr of addrs) {
173
+ const withPeer = withTrailingPeerId(multiaddr(addr), peerId);
174
+ if (withPeer !== null) {
175
+ bound.push(withPeer);
176
+ }
177
+ }
178
+ return bound;
179
+ }