@serfab/cadre-core 0.12.0 → 1.0.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 (155) hide show
  1. package/README.md +23 -8
  2. package/dist/bootstrap-peer-store-file.js +2 -2
  3. package/dist/bootstrap-peer-store.d.ts +32 -12
  4. package/dist/bootstrap-peer-store.js +41 -15
  5. package/dist/bootstrap-peer-store.js.map +1 -1
  6. package/dist/cadre-node.d.ts +883 -102
  7. package/dist/cadre-node.js +1800 -209
  8. package/dist/cadre-node.js.map +1 -1
  9. package/dist/control-authorization.d.ts +2 -2
  10. package/dist/control-authorization.js +1 -0
  11. package/dist/control-authorization.js.map +1 -1
  12. package/dist/control-cohort.d.ts +13 -26
  13. package/dist/control-cohort.js +0 -26
  14. package/dist/control-cohort.js.map +1 -1
  15. package/dist/control-database.d.ts +318 -44
  16. package/dist/control-database.js +516 -74
  17. package/dist/control-database.js.map +1 -1
  18. package/dist/control-read-retry.d.ts +40 -11
  19. package/dist/control-read-retry.js +48 -15
  20. package/dist/control-read-retry.js.map +1 -1
  21. package/dist/control-retry.d.ts +58 -3
  22. package/dist/control-retry.js +58 -3
  23. package/dist/control-retry.js.map +1 -1
  24. package/dist/control-schema.d.ts +1 -1
  25. package/dist/control-schema.js +133 -21
  26. package/dist/control-schema.js.map +1 -1
  27. package/dist/control-write-retry.d.ts +80 -11
  28. package/dist/control-write-retry.js +168 -29
  29. package/dist/control-write-retry.js.map +1 -1
  30. package/dist/enrolled-machine-store-file.d.ts +13 -0
  31. package/dist/enrolled-machine-store-file.js +41 -0
  32. package/dist/enrolled-machine-store-file.js.map +1 -0
  33. package/dist/enrolled-machine-store.d.ts +81 -0
  34. package/dist/enrolled-machine-store.js +225 -0
  35. package/dist/enrolled-machine-store.js.map +1 -0
  36. package/dist/file-durable-slot.js +5 -3
  37. package/dist/file-durable-slot.js.map +1 -1
  38. package/dist/index.d.ts +17 -8
  39. package/dist/index.js +54 -9
  40. package/dist/index.js.map +1 -1
  41. package/dist/membership-connection-gater.d.ts +15 -1
  42. package/dist/membership-connection-gater.js +9 -3
  43. package/dist/membership-connection-gater.js.map +1 -1
  44. package/dist/node-local-snapshot.d.ts +21 -7
  45. package/dist/node-local-snapshot.js +30 -9
  46. package/dist/node-local-snapshot.js.map +1 -1
  47. package/dist/peer-dial.d.ts +143 -0
  48. package/dist/peer-dial.js +176 -0
  49. package/dist/peer-dial.js.map +1 -0
  50. package/dist/{strand-backfill.d.ts → peer-join-backfill.d.ts} +49 -20
  51. package/dist/{strand-backfill.js → peer-join-backfill.js} +133 -52
  52. package/dist/peer-join-backfill.js.map +1 -0
  53. package/dist/relay-addrs.d.ts +160 -49
  54. package/dist/relay-addrs.js +287 -60
  55. package/dist/relay-addrs.js.map +1 -1
  56. package/dist/relay-reservation.d.ts +71 -3
  57. package/dist/relay-reservation.js +116 -8
  58. package/dist/relay-reservation.js.map +1 -1
  59. package/dist/seed-bootstrap.d.ts +30 -1
  60. package/dist/seed-bootstrap.js +62 -17
  61. package/dist/seed-bootstrap.js.map +1 -1
  62. package/dist/storage-scope.d.ts +62 -0
  63. package/dist/storage-scope.js +75 -0
  64. package/dist/storage-scope.js.map +1 -0
  65. package/dist/strand-database.d.ts +41 -11
  66. package/dist/strand-database.js +48 -18
  67. package/dist/strand-database.js.map +1 -1
  68. package/dist/strand-first-sync-gate.d.ts +152 -0
  69. package/dist/strand-first-sync-gate.js +208 -0
  70. package/dist/strand-first-sync-gate.js.map +1 -0
  71. package/dist/strand-formation-manager.d.ts +99 -5
  72. package/dist/strand-formation-manager.js +119 -8
  73. package/dist/strand-formation-manager.js.map +1 -1
  74. package/dist/strand-formation-protocol.d.ts +97 -3
  75. package/dist/strand-formation-protocol.js +108 -2
  76. package/dist/strand-formation-protocol.js.map +1 -1
  77. package/dist/strand-instance-manager.d.ts +389 -19
  78. package/dist/strand-instance-manager.js +678 -65
  79. package/dist/strand-instance-manager.js.map +1 -1
  80. package/dist/strand-member-key.d.ts +20 -12
  81. package/dist/strand-member-key.js +20 -12
  82. package/dist/strand-member-key.js.map +1 -1
  83. package/dist/strand-member-registry.js +3 -0
  84. package/dist/strand-member-registry.js.map +1 -1
  85. package/dist/strand-membership-reconciler.d.ts +361 -0
  86. package/dist/strand-membership-reconciler.js +548 -0
  87. package/dist/strand-membership-reconciler.js.map +1 -0
  88. package/dist/strand-membership-writer.d.ts +143 -11
  89. package/dist/strand-membership-writer.js +419 -135
  90. package/dist/strand-membership-writer.js.map +1 -1
  91. package/dist/strand-network-config.d.ts +116 -0
  92. package/dist/strand-network-config.js +192 -0
  93. package/dist/strand-network-config.js.map +1 -0
  94. package/dist/strand-revocation-enforcer.d.ts +392 -0
  95. package/dist/strand-revocation-enforcer.js +533 -0
  96. package/dist/strand-revocation-enforcer.js.map +1 -0
  97. package/dist/strand-solicitation.d.ts +17 -1
  98. package/dist/strand-solicitation.js +13 -2
  99. package/dist/strand-solicitation.js.map +1 -1
  100. package/dist/strand-wake-protocol.d.ts +11 -7
  101. package/dist/strand-wake-protocol.js +20 -52
  102. package/dist/strand-wake-protocol.js.map +1 -1
  103. package/dist/strand-watcher.js +12 -1
  104. package/dist/strand-watcher.js.map +1 -1
  105. package/dist/timed-step.d.ts +12 -0
  106. package/dist/timed-step.js +28 -0
  107. package/dist/timed-step.js.map +1 -0
  108. package/dist/timeout-scheduler.d.ts +19 -0
  109. package/dist/timeout-scheduler.js +22 -0
  110. package/dist/timeout-scheduler.js.map +1 -0
  111. package/dist/types.d.ts +472 -46
  112. package/dist/types.js +7 -1
  113. package/dist/types.js.map +1 -1
  114. package/package.json +16 -8
  115. package/src/bootstrap-peer-store-file.ts +2 -2
  116. package/src/bootstrap-peer-store.ts +69 -27
  117. package/src/cadre-node.ts +1926 -228
  118. package/src/control-authorization.ts +2 -1
  119. package/src/control-cohort.ts +122 -130
  120. package/src/control-database.ts +586 -74
  121. package/src/control-read-retry.ts +202 -167
  122. package/src/control-retry.ts +297 -190
  123. package/src/control-schema.ts +133 -21
  124. package/src/control-write-retry.ts +525 -383
  125. package/src/enrolled-machine-store-file.ts +43 -0
  126. package/src/enrolled-machine-store.ts +280 -0
  127. package/src/file-durable-slot.ts +5 -3
  128. package/src/index.ts +134 -15
  129. package/src/membership-connection-gater.ts +9 -3
  130. package/src/node-local-snapshot.ts +38 -9
  131. package/src/peer-addr-book.ts +251 -250
  132. package/src/peer-dial.ts +215 -0
  133. package/src/{strand-backfill.ts → peer-join-backfill.ts} +478 -389
  134. package/src/relay-addrs.ts +458 -218
  135. package/src/relay-reservation.ts +951 -828
  136. package/src/seed-bootstrap.ts +76 -18
  137. package/src/storage-scope.ts +78 -0
  138. package/src/strand-database.ts +61 -21
  139. package/src/strand-first-sync-gate.ts +258 -0
  140. package/src/strand-formation-manager.ts +162 -8
  141. package/src/strand-formation-protocol.ts +850 -692
  142. package/src/strand-instance-manager.ts +877 -74
  143. package/src/strand-member-key.ts +20 -12
  144. package/src/strand-member-registry.ts +3 -0
  145. package/src/strand-membership-reconciler.ts +680 -0
  146. package/src/strand-membership-writer.ts +550 -190
  147. package/src/strand-network-config.ts +250 -0
  148. package/src/strand-revocation-enforcer.ts +653 -0
  149. package/src/strand-solicitation.ts +499 -471
  150. package/src/strand-wake-protocol.ts +21 -66
  151. package/src/strand-watcher.ts +325 -314
  152. package/src/timed-step.ts +28 -0
  153. package/src/timeout-scheduler.ts +28 -0
  154. package/src/types.ts +487 -52
  155. package/dist/strand-backfill.js.map +0 -1
@@ -30,7 +30,8 @@ import type { Libp2p, Connection } from '@libp2p/interface';
30
30
  import type { Multiaddr } from '@multiformats/multiaddr';
31
31
  import type { StrandInstance, WakeRequest, WakeAck } from './types.js';
32
32
  import { decodeLengthPrefixedFrame } from './seed-bootstrap.js';
33
- import { type ControlStream, writeFrame, withDeadline, exchangeFrame, readStreamToEnd } from './control-stream.js';
33
+ import { type ControlStream, writeFrame, exchangeFrame, readStreamToEnd } from './control-stream.js';
34
+ import { tryAddrsInTurn } from './peer-dial.js';
34
35
 
35
36
  const log = debug('sereus:cadre:strand-wake');
36
37
 
@@ -60,8 +61,10 @@ const DEFAULT_WAKE_TIMEOUT_MS = 10_000;
60
61
  *
61
62
  * This makes the TARGET PEER the unit rather than the address — the same
62
63
  * decision, for the same reason, as
63
- * `DEFAULT_CONTROL_COHORT_DIAL_TIMEOUT_MS` in `control-cohort.ts`, and the same
64
- * chosen number. 20 s is deliberately wider than one attempt timeout so a
64
+ * `DEFAULT_CONTROL_COHORT_DIAL_TIMEOUT_MS` in `peer-dial.ts`, though not the same
65
+ * number: that one is sized to fit several dead addresses ahead of a working one,
66
+ * while a wake target is expected to be awake and reachable on its first or second
67
+ * address. 20 s is deliberately wider than one attempt timeout so a
65
68
  * reachable peer whose signaling address is stale still gets a genuine try at
66
69
  * its direct one; a peer needing longer than that is not "asleep and reachable",
67
70
  * which is the only case a wake is for. The last attempt inside the budget gets
@@ -271,12 +274,6 @@ export interface DialWakeOptions {
271
274
  protocolId?: string;
272
275
  }
273
276
 
274
- /** One candidate's outcome, kept so the thrown error can name every attempt. */
275
- interface WakeDialFailure {
276
- addr: Multiaddr;
277
- error: Error;
278
- }
279
-
280
277
  /**
281
278
  * Sender side: dial a target's control-network address(es), send a
282
279
  * {@link WakeRequest} over `WAKE_PROTOCOL`, and return the peer's {@link WakeAck}.
@@ -289,16 +286,18 @@ interface WakeDialFailure {
289
286
  * dropped.
290
287
  *
291
288
  * Throws if no address is dialable — with an error naming EVERY candidate and
292
- * why it failed, not merely the last one. That distinction is not cosmetic:
293
- * with only the last candidate's message the failure that mattered (usually the
294
- * signaling address, tried first) is invisible outside a debug log, and the
295
- * surfaced message points at whichever address happened to be tried last.
289
+ * why it failed, not merely the last one ({@link tryAddrsInTurn}). That
290
+ * distinction is not cosmetic: with only the last candidate's message the
291
+ * failure that mattered (usually the signaling address, tried first) is
292
+ * invisible outside a debug log, and the surfaced message points at whichever
293
+ * address happened to be tried last.
296
294
  *
297
295
  * The candidate loop is deliberately explicit rather than one
298
296
  * `dialProtocol(addrs)` call. libp2p sorts any multi-address dial with
299
297
  * `defaultAddressSorter`, whose `circuitRelayAddressesLast` pass would demote
300
298
  * exactly the signaling address this ordering puts first — silently inverting
301
- * it, with no per-dial sorter override to opt out of.
299
+ * it, with no per-dial sorter override to opt out of. For the same reason this
300
+ * does not use `dialPeerAddrs`, which puts relayed addresses last.
302
301
  */
303
302
  export async function dialWake(
304
303
  node: Libp2p,
@@ -310,58 +309,14 @@ export async function dialWake(
310
309
  throw new Error('No dialable address for wake target');
311
310
  }
312
311
  const protocolId = options.protocolId ?? WAKE_PROTOCOL;
313
- const timeoutMs = options.timeoutMs ?? DEFAULT_WAKE_TIMEOUT_MS;
314
- const budgetMs = options.budgetMs ?? DEFAULT_WAKE_DIAL_BUDGET_MS;
315
- const deadline = Date.now() + budgetMs;
316
-
317
- const failures: WakeDialFailure[] = [];
318
- for (const addr of addrs) {
319
- // Whatever is left of the whole-call budget caps this attempt, so the last
320
- // candidate inside the budget still gets a real (if shortened) try.
321
- const remaining = deadline - Date.now();
322
- if (remaining <= 0) {
323
- failures.push({ addr, error: new Error(`not tried — the ${budgetMs}ms wake dial budget was spent on earlier addresses`) });
324
- continue;
325
- }
326
- const attemptMs = Math.min(timeoutMs, remaining);
327
- try {
328
- // One deadline per attempt: its signal aborts the in-flight dialProtocol and
329
- // resets the live stream, so neither the connect nor the ack-read leaks.
330
- return await withDeadline(
331
- attemptMs,
332
- `Wake dial ${addr.toString()}`,
333
- (signal) => sendWake(node, addr, protocolId, request, attemptMs, signal),
334
- );
335
- } catch (err) {
336
- failures.push({ addr, error: err instanceof Error ? err : new Error(String(err)) });
337
- log('Wake dial to %s failed: %o', addr.toString(), err);
338
- }
339
- }
340
- throw wakeDialError(failures);
341
- }
342
-
343
- /**
344
- * Fold every candidate's failure into the one error {@link dialWake} throws.
345
- *
346
- * A single candidate throws its own error unchanged, so a one-address wake reads
347
- * exactly as it always has (and `rejects.toThrow(/timed out/)` still means what
348
- * it says). Several candidates produce one message naming each address and its
349
- * cause, with the FIRST failure as `cause` — the first candidate is the
350
- * signaling address, which is the one that matters when a relayed peer goes
351
- * unreachable.
352
- */
353
- function wakeDialError(failures: WakeDialFailure[]): Error {
354
- if (failures.length === 0) {
355
- return new Error('Wake dial failed');
356
- }
357
- if (failures.length === 1) {
358
- return failures[0].error;
359
- }
360
- const detail = failures.map((f) => `${f.addr.toString()} — ${f.error.message}`).join('; ');
361
- return new Error(
362
- `Wake dial failed for all ${failures.length} candidate addresses: ${detail}`,
363
- { cause: failures[0].error },
364
- );
312
+ const budget = {
313
+ perAddressMs: options.timeoutMs ?? DEFAULT_WAKE_TIMEOUT_MS,
314
+ totalMs: options.budgetMs ?? DEFAULT_WAKE_DIAL_BUDGET_MS,
315
+ };
316
+ // The attempt's signal aborts the in-flight dialProtocol and resets the live
317
+ // stream, so neither the connect nor the ack-read leaks.
318
+ return await tryAddrsInTurn(addrs, budget, 'Wake dial', (addr, signal, attemptMs) =>
319
+ sendWake(node, addr, protocolId, request, attemptMs, signal));
365
320
  }
366
321
 
367
322
  /**