@serfab/cadre-core 1.7.0 → 1.8.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 (162) hide show
  1. package/README.md +8 -6
  2. package/dist/cadre-node.d.ts +386 -114
  3. package/dist/cadre-node.js +887 -359
  4. package/dist/cadre-node.js.map +1 -1
  5. package/dist/control-authorization.d.ts +2 -2
  6. package/dist/control-authorization.js +1 -0
  7. package/dist/control-authorization.js.map +1 -1
  8. package/dist/control-cohort.d.ts +6 -0
  9. package/dist/control-cohort.js +6 -0
  10. package/dist/control-cohort.js.map +1 -1
  11. package/dist/control-database.d.ts +46 -8
  12. package/dist/control-database.js +97 -18
  13. package/dist/control-database.js.map +1 -1
  14. package/dist/control-formation-recorder.d.ts +37 -14
  15. package/dist/control-formation-recorder.js +60 -10
  16. package/dist/control-formation-recorder.js.map +1 -1
  17. package/dist/control-read-retry.d.ts +13 -12
  18. package/dist/control-read-retry.js +14 -12
  19. package/dist/control-read-retry.js.map +1 -1
  20. package/dist/control-schema.d.ts +1 -1
  21. package/dist/control-schema.js +67 -5
  22. package/dist/control-schema.js.map +1 -1
  23. package/dist/control-stream.d.ts +9 -1
  24. package/dist/control-stream.js +24 -1
  25. package/dist/control-stream.js.map +1 -1
  26. package/dist/control-write-retry.d.ts +12 -2
  27. package/dist/control-write-retry.js +13 -2
  28. package/dist/control-write-retry.js.map +1 -1
  29. package/dist/delegate-admission.d.ts +11 -8
  30. package/dist/delegate-admission.js +19 -11
  31. package/dist/delegate-admission.js.map +1 -1
  32. package/dist/diagnostics/connection-path.d.ts +0 -7
  33. package/dist/diagnostics/connection-path.js +0 -7
  34. package/dist/diagnostics/connection-path.js.map +1 -1
  35. package/dist/formation-approval.d.ts +7 -0
  36. package/dist/formation-approval.js +9 -3
  37. package/dist/formation-approval.js.map +1 -1
  38. package/dist/fs-atomic.d.ts +11 -2
  39. package/dist/fs-atomic.js +13 -3
  40. package/dist/fs-atomic.js.map +1 -1
  41. package/dist/hibernation-manager.d.ts +111 -8
  42. package/dist/hibernation-manager.js +234 -49
  43. package/dist/hibernation-manager.js.map +1 -1
  44. package/dist/index.d.ts +8 -6
  45. package/dist/index.js +15 -9
  46. package/dist/index.js.map +1 -1
  47. package/dist/joined-strand-store.d.ts +90 -25
  48. package/dist/joined-strand-store.js +223 -55
  49. package/dist/joined-strand-store.js.map +1 -1
  50. package/dist/key-store-file.js +14 -5
  51. package/dist/key-store-file.js.map +1 -1
  52. package/dist/link-budget.d.ts +367 -69
  53. package/dist/link-budget.js +375 -72
  54. package/dist/link-budget.js.map +1 -1
  55. package/dist/membership-connection-gater.d.ts +30 -43
  56. package/dist/membership-connection-gater.js +31 -42
  57. package/dist/membership-connection-gater.js.map +1 -1
  58. package/dist/peer-dial.d.ts +24 -4
  59. package/dist/peer-dial.js +74 -5
  60. package/dist/peer-dial.js.map +1 -1
  61. package/dist/peer-join-backfill.d.ts +2 -1
  62. package/dist/peer-join-backfill.js +6 -5
  63. package/dist/peer-join-backfill.js.map +1 -1
  64. package/dist/push-fanout.d.ts +6 -0
  65. package/dist/push-fanout.js +6 -0
  66. package/dist/push-fanout.js.map +1 -1
  67. package/dist/relay-reservation.d.ts +3 -1
  68. package/dist/relay-reservation.js +3 -1
  69. package/dist/relay-reservation.js.map +1 -1
  70. package/dist/relay-server.d.ts +79 -0
  71. package/dist/relay-server.js +74 -0
  72. package/dist/relay-server.js.map +1 -0
  73. package/dist/seed-bootstrap.d.ts +57 -4
  74. package/dist/seed-bootstrap.js +107 -83
  75. package/dist/seed-bootstrap.js.map +1 -1
  76. package/dist/storage-scope.d.ts +33 -17
  77. package/dist/storage-scope.js +44 -23
  78. package/dist/storage-scope.js.map +1 -1
  79. package/dist/strand-addr-protocol.d.ts +58 -21
  80. package/dist/strand-addr-protocol.js +133 -85
  81. package/dist/strand-addr-protocol.js.map +1 -1
  82. package/dist/strand-database.d.ts +20 -2
  83. package/dist/strand-database.js +42 -5
  84. package/dist/strand-database.js.map +1 -1
  85. package/dist/strand-first-sync-gate.d.ts +59 -21
  86. package/dist/strand-first-sync-gate.js +58 -19
  87. package/dist/strand-first-sync-gate.js.map +1 -1
  88. package/dist/strand-formation-deadlines.d.ts +151 -0
  89. package/dist/strand-formation-deadlines.js +171 -0
  90. package/dist/strand-formation-deadlines.js.map +1 -0
  91. package/dist/strand-formation-manager.d.ts +63 -30
  92. package/dist/strand-formation-manager.js +67 -41
  93. package/dist/strand-formation-manager.js.map +1 -1
  94. package/dist/strand-formation-protocol.d.ts +45 -35
  95. package/dist/strand-formation-protocol.js +59 -122
  96. package/dist/strand-formation-protocol.js.map +1 -1
  97. package/dist/strand-instance-manager.d.ts +113 -6
  98. package/dist/strand-instance-manager.js +257 -41
  99. package/dist/strand-instance-manager.js.map +1 -1
  100. package/dist/strand-membership-writer.d.ts +6 -0
  101. package/dist/strand-membership-writer.js +11 -0
  102. package/dist/strand-membership-writer.js.map +1 -1
  103. package/dist/strand-peer-book-protocol.d.ts +13 -4
  104. package/dist/strand-peer-book-protocol.js +3 -7
  105. package/dist/strand-peer-book-protocol.js.map +1 -1
  106. package/dist/strand-peer-book.d.ts +1 -1
  107. package/dist/strand-peer-book.js +8 -1
  108. package/dist/strand-peer-book.js.map +1 -1
  109. package/dist/strand-peer-observer.js +1 -1
  110. package/dist/strand-revocation-enforcer.js +2 -1
  111. package/dist/strand-revocation-enforcer.js.map +1 -1
  112. package/dist/strand-solicitation.d.ts +36 -20
  113. package/dist/strand-solicitation.js +4 -4
  114. package/dist/strand-solicitation.js.map +1 -1
  115. package/dist/strand-wake-protocol.d.ts +80 -23
  116. package/dist/strand-wake-protocol.js +89 -64
  117. package/dist/strand-wake-protocol.js.map +1 -1
  118. package/dist/types.d.ts +128 -38
  119. package/dist/types.js +4 -3
  120. package/dist/types.js.map +1 -1
  121. package/package.json +7 -7
  122. package/src/cadre-node.ts +8367 -7776
  123. package/src/control-authorization.ts +2 -1
  124. package/src/control-cohort.ts +6 -0
  125. package/src/control-database.ts +122 -18
  126. package/src/control-formation-recorder.ts +354 -305
  127. package/src/control-read-retry.ts +14 -12
  128. package/src/control-schema.ts +67 -5
  129. package/src/control-stream.ts +25 -1
  130. package/src/control-write-retry.ts +13 -2
  131. package/src/delegate-admission.ts +23 -13
  132. package/src/diagnostics/connection-path.ts +0 -7
  133. package/src/formation-approval.ts +610 -604
  134. package/src/fs-atomic.ts +14 -4
  135. package/src/hibernation-manager.ts +638 -437
  136. package/src/index.ts +28 -10
  137. package/src/joined-strand-store.ts +247 -57
  138. package/src/key-store-file.ts +14 -5
  139. package/src/link-budget.ts +393 -74
  140. package/src/membership-connection-gater.ts +31 -45
  141. package/src/peer-dial.ts +81 -7
  142. package/src/peer-join-backfill.ts +8 -6
  143. package/src/push-fanout.ts +6 -0
  144. package/src/relay-reservation.ts +3 -1
  145. package/src/relay-server.ts +98 -0
  146. package/src/seed-bootstrap.ts +133 -91
  147. package/src/storage-scope.ts +44 -23
  148. package/src/strand-addr-protocol.ts +547 -458
  149. package/src/strand-database.ts +287 -242
  150. package/src/strand-first-sync-gate.ts +58 -19
  151. package/src/strand-formation-deadlines.ts +227 -0
  152. package/src/strand-formation-manager.ts +700 -647
  153. package/src/strand-formation-protocol.ts +103 -150
  154. package/src/strand-instance-manager.ts +287 -44
  155. package/src/strand-membership-writer.ts +12 -0
  156. package/src/strand-peer-book-protocol.ts +16 -13
  157. package/src/strand-peer-book.ts +8 -1
  158. package/src/strand-peer-observer.ts +1 -1
  159. package/src/strand-revocation-enforcer.ts +2 -1
  160. package/src/strand-solicitation.ts +519 -500
  161. package/src/strand-wake-protocol.ts +401 -355
  162. package/src/types.ts +130 -38
@@ -20,7 +20,7 @@
20
20
  *
21
21
  * **Authorization (v1):** like wake, the receiver defers entirely to the injected
22
22
  * `isMember` predicate and requires no further signature; a peer it rejects gets
23
- * an empty address list. `CadreNode` injects its AUTHORIZED-membership predicate
23
+ * a `refused` reply with no addresses. `CadreNode` injects its AUTHORIZED-membership predicate
24
24
  * (`isAuthorizedMember`: voucher on the requester's `CadrePeer` row verified
25
25
  * against the node-local trusted-owner anchor), so an outsider that published its
26
26
  * own rows into the replicated control DB cannot harvest live strand addresses.
@@ -64,7 +64,7 @@ export interface StrandAddrServiceOptions {
64
64
  readTimeoutMs?: number;
65
65
  /**
66
66
  * Cap on concurrent inbound strand-addr streams (defaults to
67
- * {@link DEFAULT_MAX_CONCURRENT_ADDRS}). Over the cap, an empty response is
67
+ * {@link DEFAULT_MAX_CONCURRENT_ADDRS}). Over the cap, an `unavailable` reply is
68
68
  * returned without looking up any strand address.
69
69
  */
70
70
  maxConcurrent?: number;
@@ -73,7 +73,8 @@ export interface StrandAddrServiceOptions {
73
73
  * Receiver side of the strand-address RPC. Registers a `STRAND_ADDR_PROTOCOL`
74
74
  * handler on the control node and, for each inbound {@link StrandAddrRequest},
75
75
  * gates on cadre membership, then replies with the local strand instance's live
76
- * multiaddrs (or an empty list when not a member / not running).
76
+ * multiaddrs — `ok` with an empty list when the strand is not running, `refused`
77
+ * for a non-member, `unavailable` when it could not answer at all.
77
78
  */
78
79
  export declare class StrandAddrService {
79
80
  private readonly options;
@@ -85,30 +86,40 @@ export declare class StrandAddrService {
85
86
  constructor(options: StrandAddrServiceOptions);
86
87
  /** Number of in-flight inbound strand-addr streams. */
87
88
  get activeCount(): number;
88
- /** Register the strand-addr protocol handler on the control node. */
89
- initialize(node: Libp2p): void;
89
+ /**
90
+ * Register the strand-addr protocol handler on the control node. Rejects when
91
+ * libp2p refuses the registration (a duplicate handler, a peer-store write
92
+ * failure); the node reference is kept only once the handler is in place.
93
+ */
94
+ initialize(node: Libp2p): Promise<void>;
90
95
  /** Unregister the handler and release the node reference. */
91
96
  shutdown(): Promise<void>;
92
97
  /**
93
98
  * Read the inbound request, decide the response, and write it back.
94
99
  *
95
- * Three hardening layers, all reported as an empty {@link StrandAddrResponse}
96
- * rather than a dropped/hung stream: a concurrency cap (over
97
- * {@link maxConcurrent}, reply without looking up any address), a read timeout
98
- * (a peer that never half-closes is aborted inside {@link readFrame}/
99
- * `readStreamToEnd`), and the existing malformed/oversized-frame guard.
100
+ * Three hardening layers, all reported as an `unavailable`
101
+ * {@link StrandAddrResponse} rather than a dropped/hung stream: a concurrency cap
102
+ * (over {@link maxConcurrent}, reply without looking up any address), a read
103
+ * timeout (a peer that never half-closes is aborted inside {@link readFrame}/
104
+ * `readStreamToEnd`), and the existing malformed/oversized-frame guard. A
105
+ * lookup that throws — the membership read failing, say — is `unavailable` too,
106
+ * never a `refused` or an empty `ok` the asker would wait ten minutes on.
100
107
  */
101
108
  private handleStream;
109
+ /** Read and decide one inbound request; any failure becomes an `unavailable` response. */
110
+ private answerStream;
102
111
  /**
103
112
  * Decide the response for a decoded request. Exposed (not private) so the
104
113
  * decision matrix can be unit-tested directly (mirrors wake's
105
114
  * `processWakeRequest`).
106
115
  *
107
- * - Non-member sender → empty `multiaddrs` (refused), no delegate grant.
116
+ * - Non-member sender → `refused`, no delegate grant.
108
117
  * - Member request carrying a `delegatePeerId` → recorded via
109
118
  * {@link StrandAddrServiceOptions.onDelegateAnnounce} before the lookup.
110
- * - Strand not running locally → empty `multiaddrs` (`getStrandMultiaddrs` → `[]`).
111
- * - Member + running strand → the strand's live, signaling-first multiaddrs.
119
+ * - Strand not running locally → `ok` with empty `multiaddrs` (`getStrandMultiaddrs` → `[]`).
120
+ * - Member + running strand → `ok` with the strand's live, signaling-first multiaddrs.
121
+ *
122
+ * A throwing `isMember` propagates; {@link handleStream} answers it `unavailable`.
112
123
  */
113
124
  processAddrRequest(request: StrandAddrRequest, remotePeerId: string): Promise<StrandAddrResponse>;
114
125
  /**
@@ -132,7 +143,13 @@ export interface StrandAddrPeer {
132
143
  }
133
144
  /** Options for {@link collectStrandAddrs}. */
134
145
  export interface CollectStrandAddrsOptions {
135
- /** Per-dial timeout in ms (default {@link DEFAULT_ADDR_TIMEOUT_MS}). */
146
+ /**
147
+ * The asker's declared link round trip (`NetworkConfig.linkRoundTripMs`), from which the
148
+ * per-dial timeout is derived when {@link timeoutMs} is not given. Unset means the declared
149
+ * default.
150
+ */
151
+ linkRoundTripMs?: number;
152
+ /** Per-dial timeout in ms (default: {@link attemptTimeoutMs}). */
136
153
  timeoutMs?: number;
137
154
  /** Override the protocol id (defaults to {@link STRAND_ADDR_PROTOCOL}). */
138
155
  protocolId?: string;
@@ -144,18 +161,38 @@ export interface CollectStrandAddrsOptions {
144
161
  */
145
162
  delegatePeerId?: string;
146
163
  }
164
+ /**
165
+ * What asking one sibling produced, as {@link collectStrandAddrs} reports it.
166
+ *
167
+ * - `answered` — status `ok` with at least one address.
168
+ * - `empty` — status `ok` with no address: the sibling does not run the strand
169
+ * right now, a normal steady state.
170
+ * - `unavailable` — the sibling replied that it could not answer.
171
+ * - `refused` — the sibling does not (yet) count the asker as an authorized member.
172
+ * - `unreachable` — no reply at all: every dial target failed or timed out, or
173
+ * the reply was malformed.
174
+ */
175
+ export type StrandAddrOutcome = 'answered' | 'empty' | 'unavailable' | 'refused' | 'unreachable';
176
+ /** Result of {@link collectStrandAddrs}. */
177
+ export interface StrandAddrCollection {
178
+ /** Deduplicated union of every answer, signaling-first. */
179
+ addrs: string[];
180
+ /** One entry per candidate (self excluded), keyed by the sibling's control peerId. */
181
+ outcomes: Map<string, StrandAddrOutcome>;
182
+ }
147
183
  /**
148
184
  * Client side: ask each candidate sibling for its live strand-`strandId`
149
185
  * multiaddrs and return the **deduplicated union** of every answer, ordered
150
- * signaling-first.
186
+ * signaling-first, alongside each sibling's {@link StrandAddrOutcome}.
151
187
  *
152
- * Best-effort per peer: a failed/timed-out/empty sibling is logged and skipped,
153
- * never fatal — an empty union (no sibling online or running the strand) returns
154
- * `[]`, which the caller treats as an acceptable seed that self-heals on the next
155
- * resume/reconcile pass. The local node (`node.peerId`) is excluded so we never
156
- * RPC ourselves or seed with our own strand address.
188
+ * Best-effort per peer: a failed/timed-out/empty sibling contributes no address
189
+ * and never fails the collection — an empty union (no sibling online or running
190
+ * the strand) is an acceptable seed that self-heals on the next resume/reconcile
191
+ * pass. `outcomes` is what lets a caller retry the siblings that could not answer
192
+ * sooner than the ones that answered with nothing. The local node (`node.peerId`)
193
+ * is excluded so we never RPC ourselves or seed with our own strand address.
157
194
  *
158
195
  * Dials run concurrently but the union preserves candidate order, so the result
159
196
  * is deterministic regardless of which sibling answers first.
160
197
  */
161
- export declare function collectStrandAddrs(node: Libp2p, peers: StrandAddrPeer[], strandId: string, options?: CollectStrandAddrsOptions): Promise<string[]>;
198
+ export declare function collectStrandAddrs(node: Libp2p, peers: StrandAddrPeer[], strandId: string, options?: CollectStrandAddrsOptions): Promise<StrandAddrCollection>;
@@ -20,7 +20,7 @@
20
20
  *
21
21
  * **Authorization (v1):** like wake, the receiver defers entirely to the injected
22
22
  * `isMember` predicate and requires no further signature; a peer it rejects gets
23
- * an empty address list. `CadreNode` injects its AUTHORIZED-membership predicate
23
+ * a `refused` reply with no addresses. `CadreNode` injects its AUTHORIZED-membership predicate
24
24
  * (`isAuthorizedMember`: voucher on the requester's `CadrePeer` row verified
25
25
  * against the node-local trusted-owner anchor), so an outsider that published its
26
26
  * own rows into the replicated control DB cannot harvest live strand addresses.
@@ -31,7 +31,8 @@ import debug from 'debug';
31
31
  import { peerIdFromString } from '@libp2p/peer-id';
32
32
  import { decodeLengthPrefixedFrame } from './seed-bootstrap.js';
33
33
  import { orderSignalingFirst } from './peer-record.js';
34
- import { writeFrame, withDeadline, exchangeFrame, readStreamToEnd } from './control-stream.js';
34
+ import { withDeadline, exchangeFrame, readStreamToEnd, replyAndClose } from './control-stream.js';
35
+ import { relayedRequestBudgetMs } from './link-budget.js';
35
36
  const log = debug('sereus:cadre:strand-addr');
36
37
  /** Protocol id for the control-network strand-address RPC (parallel to `/sereus/strand-wake/1.0.0`). */
37
38
  export const STRAND_ADDR_PROTOCOL = '/sereus/strand-addr/1.0.0';
@@ -41,9 +42,8 @@ export const STRAND_ADDR_PROTOCOL = '/sereus/strand-addr/1.0.0';
41
42
  * that bounds the bytes a peer can make the receiver buffer per stream.
42
43
  */
43
44
  const MAX_ADDR_SIZE = 64 * 1024;
44
- /** Default time to wait for the response before abandoning a strand-addr dial (ms). */
45
- const DEFAULT_ADDR_TIMEOUT_MS = 10000;
46
45
  /** Default time the receiver waits for an inbound request frame before aborting (ms). */
46
+ // eslint-disable-next-line no-restricted-syntax -- link-independent: a receiver cap on one small request frame on a stream the peer already opened; it bounds a peer that opens a stream and never sends, not the dial
47
47
  const DEFAULT_ADDR_READ_TIMEOUT_MS = 10000;
48
48
  /** Default cap on concurrent inbound strand-addr streams a single peer can pin open. */
49
49
  const DEFAULT_MAX_CONCURRENT_ADDRS = 100;
@@ -58,15 +58,16 @@ async function readFrame(stream, timeoutMs) {
58
58
  const body = decodeLengthPrefixedFrame(data, MAX_ADDR_SIZE);
59
59
  return JSON.parse(new TextDecoder().decode(body));
60
60
  }
61
- /** Empty response used on every reject/error path (cap, malformed frame, non-member). */
62
- function emptyResponse(strandId) {
63
- return { strandId, multiaddrs: [] };
61
+ /** An address-free reply, for every path but a member's lookup: cap, unreadable request, lookup failure, non-member. */
62
+ function addrlessResponse(status, strandId) {
63
+ return { status, strandId, multiaddrs: [] };
64
64
  }
65
65
  /**
66
66
  * Receiver side of the strand-address RPC. Registers a `STRAND_ADDR_PROTOCOL`
67
67
  * handler on the control node and, for each inbound {@link StrandAddrRequest},
68
68
  * gates on cadre membership, then replies with the local strand instance's live
69
- * multiaddrs (or an empty list when not a member / not running).
69
+ * multiaddrs — `ok` with an empty list when the strand is not running, `refused`
70
+ * for a non-member, `unavailable` when it could not answer at all.
70
71
  */
71
72
  export class StrandAddrService {
72
73
  constructor(options) {
@@ -81,19 +82,23 @@ export class StrandAddrService {
81
82
  get activeCount() {
82
83
  return this.activeStreams;
83
84
  }
84
- /** Register the strand-addr protocol handler on the control node. */
85
- initialize(node) {
86
- this.node = node;
85
+ /**
86
+ * Register the strand-addr protocol handler on the control node. Rejects when
87
+ * libp2p refuses the registration (a duplicate handler, a peer-store write
88
+ * failure); the node reference is kept only once the handler is in place.
89
+ */
90
+ async initialize(node) {
87
91
  // `runOnLimitedConnection: true` is REQUIRED for the relay path: a NAT'd
88
92
  // sibling is reached over a circuit-relay connection, which libp2p marks
89
93
  // "limited" (the relay caps its data/duration). Without this the receiver
90
94
  // would refuse the inbound stream on exactly the connection the protocol is
91
95
  // designed to use (same reasoning as wake; see the relay note on
92
96
  // `collectStrandAddrs`).
93
- void node.handle(STRAND_ADDR_PROTOCOL, async (rawStream, rawConnection) => {
97
+ await node.handle(STRAND_ADDR_PROTOCOL, async (rawStream, rawConnection) => {
94
98
  const remotePeerId = rawConnection.remotePeer.toString();
95
99
  await this.handleStream(rawStream, remotePeerId);
96
100
  }, { runOnLimitedConnection: true });
101
+ this.node = node;
97
102
  log('StrandAddrService registered handler: %s', STRAND_ADDR_PROTOCOL);
98
103
  }
99
104
  /** Unregister the handler and release the node reference. */
@@ -107,58 +112,42 @@ export class StrandAddrService {
107
112
  /**
108
113
  * Read the inbound request, decide the response, and write it back.
109
114
  *
110
- * Three hardening layers, all reported as an empty {@link StrandAddrResponse}
111
- * rather than a dropped/hung stream: a concurrency cap (over
112
- * {@link maxConcurrent}, reply without looking up any address), a read timeout
113
- * (a peer that never half-closes is aborted inside {@link readFrame}/
114
- * `readStreamToEnd`), and the existing malformed/oversized-frame guard.
115
+ * Three hardening layers, all reported as an `unavailable`
116
+ * {@link StrandAddrResponse} rather than a dropped/hung stream: a concurrency cap
117
+ * (over {@link maxConcurrent}, reply without looking up any address), a read
118
+ * timeout (a peer that never half-closes is aborted inside {@link readFrame}/
119
+ * `readStreamToEnd`), and the existing malformed/oversized-frame guard. A
120
+ * lookup that throws — the membership read failing, say — is `unavailable` too,
121
+ * never a `refused` or an empty `ok` the asker would wait ten minutes on.
115
122
  */
116
123
  async handleStream(stream, remotePeerId) {
117
124
  log('Incoming strand-addr request from: %s', remotePeerId);
118
125
  if (this.activeStreams >= this.maxConcurrent) {
119
126
  log('Rejecting strand-addr from %s: %d concurrent streams at cap %d', remotePeerId, this.activeStreams, this.maxConcurrent);
120
127
  // The request frame is unread here, so the strand id is unknown — reply
121
- // with an empty list under an empty strand id; the client treats any empty
122
- // response as "no addrs" and skips this sibling.
123
- try {
124
- writeFrame(stream, emptyResponse(''));
125
- }
126
- catch {
127
- // Ignore send errors on the reject path.
128
- }
129
- try {
130
- await stream.close();
131
- }
132
- catch {
133
- // Ignore close errors.
134
- }
128
+ // under an empty strand id; the asker retries an `unavailable` sibling soon.
129
+ await replyAndClose(stream, addrlessResponse('unavailable', ''), 'Strand-addr');
135
130
  return;
136
131
  }
137
132
  this.activeStreams++;
133
+ try {
134
+ await replyAndClose(stream, await this.answerStream(stream, remotePeerId), 'Strand-addr');
135
+ }
136
+ finally {
137
+ this.activeStreams--;
138
+ }
139
+ }
140
+ /** Read and decide one inbound request; any failure becomes an `unavailable` response. */
141
+ async answerStream(stream, remotePeerId) {
138
142
  try {
139
143
  const request = await readFrame(stream, this.readTimeoutMs);
140
- const response = await this.processAddrRequest(request, remotePeerId);
141
- writeFrame(stream, response);
144
+ return await this.processAddrRequest(request, remotePeerId);
142
145
  }
143
146
  catch (err) {
144
147
  log('Error handling strand-addr request from %s: %o', remotePeerId, err);
145
- // Malformed/oversized/timed-out request: strand id is unknown, so reply
146
- // with an empty list under an empty strand id rather than hanging.
147
- try {
148
- writeFrame(stream, emptyResponse(''));
149
- }
150
- catch {
151
- // Ignore send errors on the error path.
152
- }
153
- }
154
- finally {
155
- this.activeStreams--;
156
- try {
157
- await stream.close();
158
- }
159
- catch {
160
- // Ignore close errors.
161
- }
148
+ // Malformed/oversized/timed-out request, or a lookup that threw: reply
149
+ // under an empty strand id rather than hanging.
150
+ return addrlessResponse('unavailable', '');
162
151
  }
163
152
  }
164
153
  /**
@@ -166,23 +155,25 @@ export class StrandAddrService {
166
155
  * decision matrix can be unit-tested directly (mirrors wake's
167
156
  * `processWakeRequest`).
168
157
  *
169
- * - Non-member sender → empty `multiaddrs` (refused), no delegate grant.
158
+ * - Non-member sender → `refused`, no delegate grant.
170
159
  * - Member request carrying a `delegatePeerId` → recorded via
171
160
  * {@link StrandAddrServiceOptions.onDelegateAnnounce} before the lookup.
172
- * - Strand not running locally → empty `multiaddrs` (`getStrandMultiaddrs` → `[]`).
173
- * - Member + running strand → the strand's live, signaling-first multiaddrs.
161
+ * - Strand not running locally → `ok` with empty `multiaddrs` (`getStrandMultiaddrs` → `[]`).
162
+ * - Member + running strand → `ok` with the strand's live, signaling-first multiaddrs.
163
+ *
164
+ * A throwing `isMember` propagates; {@link handleStream} answers it `unavailable`.
174
165
  */
175
166
  async processAddrRequest(request, remotePeerId) {
176
167
  // Control-network membership is the v1 authorization: only this party's
177
168
  // cadre peers may ask us for a strand address (or announce a delegate).
178
169
  if (!(await this.options.isMember(remotePeerId))) {
179
170
  log('Refusing strand-addr from non-member %s', remotePeerId);
180
- return emptyResponse(request.strandId);
171
+ return addrlessResponse('refused', request.strandId);
181
172
  }
182
173
  this.recordDelegateAnnounce(request, remotePeerId);
183
174
  const multiaddrs = this.options.getStrandMultiaddrs(request.strandId);
184
175
  log('Strand-addr for %s → %d addr(s)', request.strandId, multiaddrs.length);
185
- return { strandId: request.strandId, multiaddrs };
176
+ return { status: 'ok', strandId: request.strandId, multiaddrs };
186
177
  }
187
178
  /**
188
179
  * Validate and forward a member's delegate announcement. A malformed field
@@ -212,13 +203,14 @@ export class StrandAddrService {
212
203
  /**
213
204
  * Client side: ask each candidate sibling for its live strand-`strandId`
214
205
  * multiaddrs and return the **deduplicated union** of every answer, ordered
215
- * signaling-first.
206
+ * signaling-first, alongside each sibling's {@link StrandAddrOutcome}.
216
207
  *
217
- * Best-effort per peer: a failed/timed-out/empty sibling is logged and skipped,
218
- * never fatal — an empty union (no sibling online or running the strand) returns
219
- * `[]`, which the caller treats as an acceptable seed that self-heals on the next
220
- * resume/reconcile pass. The local node (`node.peerId`) is excluded so we never
221
- * RPC ourselves or seed with our own strand address.
208
+ * Best-effort per peer: a failed/timed-out/empty sibling contributes no address
209
+ * and never fails the collection — an empty union (no sibling online or running
210
+ * the strand) is an acceptable seed that self-heals on the next resume/reconcile
211
+ * pass. `outcomes` is what lets a caller retry the siblings that could not answer
212
+ * sooner than the ones that answered with nothing. The local node (`node.peerId`)
213
+ * is excluded so we never RPC ourselves or seed with our own strand address.
222
214
  *
223
215
  * Dials run concurrently but the union preserves candidate order, so the result
224
216
  * is deterministic regardless of which sibling answers first.
@@ -229,48 +221,54 @@ export async function collectStrandAddrs(node, peers, strandId, options = {}) {
229
221
  ? { strandId, delegatePeerId: options.delegatePeerId }
230
222
  : { strandId };
231
223
  const candidates = peers.filter(p => p.peerId !== selfId);
232
- // Concurrent dials; `dialOneSibling` swallows per-peer failure into `[]`, so
224
+ // Concurrent dials; `dialOneSibling` folds per-peer failure into an outcome, so
233
225
  // `Promise.all` never rejects and the result array stays in candidate order.
234
- const perPeer = await Promise.all(candidates.map(peer => dialOneSibling(node, peer, request, options)));
226
+ const answers = await Promise.all(candidates.map(async (peer) => ({ peerId: peer.peerId, ...await dialOneSibling(node, peer, request, options) })));
227
+ const outcomes = new Map();
235
228
  // Deduplicated union in candidate order, then signaling-first for a usable
236
229
  // dial sequence (relay/`p2p-circuit` ahead of direct addrs).
237
230
  const seen = new Set();
238
231
  const union = [];
239
- for (const addrs of perPeer) {
240
- for (const addr of addrs) {
232
+ for (const { peerId, outcome, multiaddrs } of answers) {
233
+ outcomes.set(peerId, outcome);
234
+ for (const addr of multiaddrs) {
241
235
  if (!seen.has(addr)) {
242
236
  seen.add(addr);
243
237
  union.push(addr);
244
238
  }
245
239
  }
246
240
  }
247
- return orderSignalingFirst(union);
241
+ return { addrs: orderSignalingFirst(union), outcomes };
248
242
  }
249
243
  /**
250
- * Ask one sibling for its strand address, returning its multiaddrs or `[]` on any
251
- * failure. Tries each dial target in order (peerId first to reuse an open control
252
- * connection, then explicit addrs) until one answers; a total failure is logged
253
- * and folded to `[]` so a single dead sibling never aborts the collection.
244
+ * Ask one sibling for its strand address. Tries each dial target in order (peerId
245
+ * first to reuse an open control connection, then explicit addrs) until one
246
+ * produces a well-formed reply, and reports that reply's outcome; a total failure
247
+ * is `unreachable`, so a single dead sibling never aborts the collection. Never
248
+ * throws.
249
+ *
250
+ * A reply of any status ends the loop: every target reaches the same responder,
251
+ * so asking it again by another address would get the same answer.
254
252
  *
255
- * A total failure logs ONE line naming every target and its cause. `[]` alone
256
- * cannot tell the caller "this sibling was unreachable" apart from "this sibling
257
- * answered and has no strand address" — and per-target lines scattered through a
258
- * concurrent fan-out do not reassemble into that answer either. The return
259
- * contract is unchanged: best-effort `[]`, never a throw.
253
+ * A total failure logs ONE line naming every target and its cause, because
254
+ * per-target lines scattered through a concurrent fan-out do not reassemble into
255
+ * "this sibling was unreachable".
260
256
  *
261
- * NOTE: cost is (targets × `timeoutMs`) with no whole-sibling budget, the same
262
- * shape `dialWake` bounds with `DEFAULT_WAKE_DIAL_BUDGET_MS`. Fine today —
263
- * `dialTargets` yields the peerId plus whatever `resolvePeerAddrs` returned,
264
- * which for a cadre device is one or two addresses. If a sibling's record ever
265
- * carries a long address list, give this the same whole-sibling budget.
257
+ * NOTE: cost is (targets × `timeoutMs`, 23 s each at the default declared link) with
258
+ * no whole-sibling budget, the same shape `dialWake` bounds with
259
+ * `DEFAULT_WAKE_DIAL_BUDGET_MS`. Siblings are asked concurrently, so a collection costs
260
+ * its slowest sibling, not the sum. Fine today — `dialTargets` yields the peerId plus
261
+ * whatever `resolvePeerAddrs` returned, which for a cadre device is one or two
262
+ * addresses. If a sibling's record ever carries a long address list, give this the same
263
+ * whole-sibling budget.
266
264
  */
267
265
  async function dialOneSibling(node, peer, request, options) {
268
266
  const protocolId = options.protocolId ?? STRAND_ADDR_PROTOCOL;
269
- const timeoutMs = options.timeoutMs ?? DEFAULT_ADDR_TIMEOUT_MS;
267
+ const timeoutMs = attemptTimeoutMs(options);
270
268
  const targets = dialTargets(peer);
271
269
  if (targets.length === 0) {
272
270
  log('No dial target for sibling %s', peer.peerId);
273
- return [];
271
+ return { outcome: 'unreachable', multiaddrs: [] };
274
272
  }
275
273
  const failures = [];
276
274
  for (const target of targets) {
@@ -278,7 +276,7 @@ async function dialOneSibling(node, peer, request, options) {
278
276
  // One deadline per attempt: its signal aborts the in-flight dialProtocol and
279
277
  // resets the live stream, so neither the connect nor the response-read leaks.
280
278
  const response = await withDeadline(timeoutMs, `Strand-addr dial ${peer.peerId}`, (signal) => sendStrandAddr(node, target, protocolId, request, timeoutMs, signal));
281
- return response.multiaddrs;
279
+ return siblingAnswer(response);
282
280
  }
283
281
  catch (err) {
284
282
  const error = err instanceof Error ? err : new Error(String(err));
@@ -287,7 +285,35 @@ async function dialOneSibling(node, peer, request, options) {
287
285
  }
288
286
  }
289
287
  log('Strand-addr dial to %s failed on all %d target(s), contributing no addrs: %s', peer.peerId, failures.length, failures.join('; '));
290
- return [];
288
+ return { outcome: 'unreachable', multiaddrs: [] };
289
+ }
290
+ /**
291
+ * Deadline for ONE attempt to ask a sibling — dial, request, response — unless the caller set
292
+ * its own: `relayedRequestBudgetMs` at the declared link (23 s at the default).
293
+ *
294
+ * An attempt is either a dial by peer id, which normally reuses an open control connection
295
+ * (`CIRCUIT_REQUEST_ROUND_TRIPS`, 2), or the fallback to the sibling's control addresses, a
296
+ * fresh and possibly relayed dial. One deadline covers both, so it is sized for the fallback.
297
+ *
298
+ * NOTE: the receiver's membership check (two live control reads) runs inside this deadline and
299
+ * is not counted. In steady state those reads touch only held blocks and do not consult the
300
+ * cohort. If a wake or address request is seen timing out while the receiver's membership read
301
+ * is consulting, count one membership decision in this deadline or answer the check from the
302
+ * materialized authorized-peer snapshot. See docs/cadre-consistency.md → "Deadlines Over
303
+ * Optimystic's Reads and Commits".
304
+ */
305
+ function attemptTimeoutMs(options) {
306
+ return options.timeoutMs ?? relayedRequestBudgetMs(options.linkRoundTripMs);
307
+ }
308
+ /** Map a validated reply to its outcome; only an `ok` reply contributes addresses. */
309
+ function siblingAnswer(response) {
310
+ if (response.status !== 'ok') {
311
+ return { outcome: response.status, multiaddrs: [] };
312
+ }
313
+ return {
314
+ outcome: response.multiaddrs.length > 0 ? 'answered' : 'empty',
315
+ multiaddrs: response.multiaddrs
316
+ };
291
317
  }
292
318
  /**
293
319
  * Dial targets for a sibling, peerId first. Dialing by peerId lets libp2p reuse
@@ -329,7 +355,29 @@ async function sendStrandAddr(node, target, protocolId, request, timeoutMs, sign
329
355
  // and is the whole point of dialing over the relay.
330
356
  const rawStream = await node.dialProtocol(target, protocolId, { runOnLimitedConnection: true, signal });
331
357
  const response = await exchangeFrame(rawStream, signal, request, (stream) => readFrame(stream, timeoutMs), 'Strand-addr dial aborted by timeout');
332
- log('Strand-addr response: %d addr(s) for %s', response.multiaddrs.length, response.strandId);
358
+ if (!isStrandAddrResponse(response)) {
359
+ throw new Error('Malformed strand-addr response');
360
+ }
361
+ log('Strand-addr response: %s, %d addr(s) for %s', response.status, response.multiaddrs.length, response.strandId);
333
362
  return response;
334
363
  }
364
+ const STRAND_ADDR_STATUSES = new Set(['ok', 'unavailable', 'refused']);
365
+ /**
366
+ * Shape check on a decoded reply: the responder is another machine, so a reply
367
+ * missing its status (an older responder, or a buggy one) or carrying anything but
368
+ * strings in `multiaddrs` is a failed exchange, not an answer.
369
+ *
370
+ * Deliberately not `sanitizeStrandAddrs` (strand-formation-protocol.ts): that drops
371
+ * bad entries and caps the list at 16, which suits a cross-party formation result
372
+ * but would silently truncate a sibling's full address list here.
373
+ */
374
+ function isStrandAddrResponse(value) {
375
+ if (typeof value !== 'object' || value === null) {
376
+ return false;
377
+ }
378
+ const { status, strandId, multiaddrs } = value;
379
+ return typeof status === 'string' && STRAND_ADDR_STATUSES.has(status)
380
+ && typeof strandId === 'string'
381
+ && Array.isArray(multiaddrs) && multiaddrs.every((addr) => typeof addr === 'string');
382
+ }
335
383
  //# sourceMappingURL=strand-addr-protocol.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"strand-addr-protocol.js","sourceRoot":"","sources":["../src/strand-addr-protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strand-addr-protocol.js","sourceRoot":"","sources":["../src/strand-addr-protocol.ts"],"names":[],"mappings":"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"}
@@ -6,8 +6,12 @@ import type { SAppConfig } from './types.js';
6
6
  export interface StrandDatabaseConfig {
7
7
  /** The strand ID */
8
8
  strandId: string;
9
- /** sApp configuration containing the schema */
10
- sAppConfig: SAppConfig;
9
+ /**
10
+ * sApp configuration containing the schema. Absent for a storage replica — a strand
11
+ * this node stores and serves without the app installed: only the `Strand` membership
12
+ * schema is applied, and such a database can never found (see {@link founderSApp}).
13
+ */
14
+ sAppConfig?: SAppConfig;
11
15
  /** Libp2p node for the strand network */
12
16
  libp2pNode: Libp2p;
13
17
  /** Coordinated repo from the libp2p node */
@@ -81,6 +85,12 @@ export declare class StrandDatabase {
81
85
  * no keypair (Header only). Idempotent — see {@link bootstrapFounderMembership}.
82
86
  */
83
87
  private bootstrapFounder;
88
+ /**
89
+ * The sApp a founder bootstrap records in the `Strand.Header`. Throws for a storage
90
+ * replica (no sApp config): its Header's sApp columns would have nothing to say.
91
+ * `CadreNode` always launches a replica as a joiner, so this guards a future caller.
92
+ */
93
+ private founderSApp;
84
94
  /**
85
95
  * Derive the founding keypair for a closed strand from the party's own
86
96
  * `partyMemberPrivateKey`, failing loudly when the key is absent (a closed strand
@@ -104,6 +114,14 @@ export declare class StrandDatabase {
104
114
  * exist: it is the identity this instance launched under.
105
115
  */
106
116
  ensureFounderBootstrap(partyMemberPrivateKey?: string): Promise<void>;
117
+ /**
118
+ * Give a live storage replica the app's schema: apply `App` to the already-composed
119
+ * database — same libp2p node, same store, so the new tables read the blocks this node
120
+ * already holds — and record the sApp, so a later {@link ensureFounderBootstrap} has it
121
+ * for the `Header`. Idempotent: the apply is a declarative diff, so re-applying the same
122
+ * schema emits nothing. A failed apply records nothing.
123
+ */
124
+ attachAppSchema(sAppConfig: SAppConfig): Promise<void>;
107
125
  /**
108
126
  * Get the underlying database for queries
109
127
  */