@serfab/cadre-core 1.4.0 → 1.5.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 (63) hide show
  1. package/dist/cadre-node.d.ts +38 -15
  2. package/dist/cadre-node.js +77 -29
  3. package/dist/cadre-node.js.map +1 -1
  4. package/dist/control-database.d.ts +24 -10
  5. package/dist/control-database.js +48 -16
  6. package/dist/control-database.js.map +1 -1
  7. package/dist/control-read-retry.d.ts +12 -0
  8. package/dist/control-read-retry.js +12 -0
  9. package/dist/control-read-retry.js.map +1 -1
  10. package/dist/control-write-retry.d.ts +5 -4
  11. package/dist/control-write-retry.js +24 -7
  12. package/dist/control-write-retry.js.map +1 -1
  13. package/dist/index.d.ts +3 -2
  14. package/dist/index.js +5 -2
  15. package/dist/index.js.map +1 -1
  16. package/dist/link-budget.d.ts +182 -0
  17. package/dist/link-budget.js +195 -0
  18. package/dist/link-budget.js.map +1 -0
  19. package/dist/peer-dial.d.ts +36 -17
  20. package/dist/peer-dial.js +37 -17
  21. package/dist/peer-dial.js.map +1 -1
  22. package/dist/peer-join-backfill.d.ts +58 -4
  23. package/dist/peer-join-backfill.js +165 -20
  24. package/dist/peer-join-backfill.js.map +1 -1
  25. package/dist/relay-reservation.d.ts +59 -11
  26. package/dist/relay-reservation.js +169 -43
  27. package/dist/relay-reservation.js.map +1 -1
  28. package/dist/seed-bootstrap.js +2 -1
  29. package/dist/seed-bootstrap.js.map +1 -1
  30. package/dist/strand-first-sync-gate.d.ts +43 -5
  31. package/dist/strand-first-sync-gate.js +55 -7
  32. package/dist/strand-first-sync-gate.js.map +1 -1
  33. package/dist/strand-instance-manager.d.ts +27 -2
  34. package/dist/strand-instance-manager.js +56 -13
  35. package/dist/strand-instance-manager.js.map +1 -1
  36. package/dist/strand-membership-reconciler.d.ts +200 -45
  37. package/dist/strand-membership-reconciler.js +302 -86
  38. package/dist/strand-membership-reconciler.js.map +1 -1
  39. package/dist/strand-membership-writer.d.ts +3 -2
  40. package/dist/strand-membership-writer.js +67 -90
  41. package/dist/strand-membership-writer.js.map +1 -1
  42. package/dist/strand-network-config.d.ts +2 -2
  43. package/dist/strand-network-config.js +2 -2
  44. package/dist/types.d.ts +96 -9
  45. package/dist/types.js +14 -4
  46. package/dist/types.js.map +1 -1
  47. package/package.json +8 -8
  48. package/src/cadre-node.ts +7445 -7397
  49. package/src/control-database.ts +48 -16
  50. package/src/control-read-retry.ts +214 -202
  51. package/src/control-write-retry.ts +24 -7
  52. package/src/index.ts +581 -563
  53. package/src/link-budget.ts +217 -0
  54. package/src/peer-dial.ts +39 -17
  55. package/src/peer-join-backfill.ts +192 -23
  56. package/src/relay-reservation.ts +1119 -951
  57. package/src/seed-bootstrap.ts +2 -1
  58. package/src/strand-first-sync-gate.ts +55 -7
  59. package/src/strand-instance-manager.ts +1516 -1458
  60. package/src/strand-membership-reconciler.ts +334 -92
  61. package/src/strand-membership-writer.ts +1962 -1987
  62. package/src/strand-network-config.ts +2 -2
  63. package/src/types.ts +95 -8
@@ -40,8 +40,8 @@
40
40
  * absorbed by the per-relay entries when one is.
41
41
  *
42
42
  * Everything else in `NetworkConfig` — `transports`, `noiseCrypto`, `connectionMonitor`,
43
- * `connectionGater`, `enableRelay` — is inherited by the caller; this module only owns
44
- * the two host-endpoint fields above and the relay listen shape. One caveat on
43
+ * `cohortQueryTimeoutMs`, `connectionGater`, `enableRelay` — is inherited by the caller; this
44
+ * module only owns the two host-endpoint fields above and the relay listen shape. One caveat on
45
45
  * `connectionGater`: an OPEN strand's node gets it unchanged, while a CLOSED strand's
46
46
  * node composes revoked-peer denial onto it in `strand-instance-manager.ts` (every
47
47
  * supplied hook still honored — see `strand-revocation-enforcer.ts`).
package/src/types.ts CHANGED
@@ -316,7 +316,8 @@ export interface NetworkConfig {
316
316
  *
317
317
  * It buys a node that BOOTS, not a node that boots fast: `start()` still waits
318
318
  * out that first attempt, which costs the drive's whole timeout
319
- * (`DEFAULT_RELAY_RESERVE_TIMEOUT_MS`, 10 s) against a relay that is unreachable
319
+ * (`DEFAULT_RELAY_RESERVE_TIMEOUT_MS`, four link round trips at
320
+ * {@link linkRoundTripMs} — 8 s at its default) against a relay that is unreachable
320
321
  * rather than merely refusing (`relay-reservation.ts` polls to the deadline, in
321
322
  * case libp2p's own discovery lands a reservation independently).
322
323
  *
@@ -406,6 +407,56 @@ export interface NetworkConfig {
406
407
  * see {@link DEFAULT_CONNECTION_MONITOR} for why.
407
408
  */
408
409
  connectionMonitor?: Libp2pConnectionMonitorInit;
410
+ /**
411
+ * How long ONE cohort peer gets to answer ONE read-path request, in milliseconds, for the
412
+ * control node and every strand node — as {@link connectionMonitor} is, and for the same
413
+ * reason: the setting describes the LINK, and a phone's control node and its strand nodes
414
+ * ride the same one. Omitted takes {@link COHORT_READ_DEADLINE_MS} (5000 ms), chosen for two
415
+ * parties reaching each other only through a relay; Optimystic's own default is 1000 ms.
416
+ *
417
+ * Raise it for a link slower still, lower it for a deployment that is all LAN and wants a
418
+ * departed peer to stop holding up a read sooner. The cost of a larger value is that a peer
419
+ * which is truly gone holds a read of a block missing locally for that long before the read
420
+ * is declined and retried, and a joining machine's first sync runs several such consults —
421
+ * so a change here should be weighed against
422
+ * {@link CadreNodeConfig.strandFirstSync}'s budget. The measurement behind the default, and
423
+ * what it costs, are on {@link COHORT_READ_DEADLINE_MS}.
424
+ *
425
+ * Handed to db-p2p's `clusterPolicy.cohortQueryTimeoutMs` unchanged and NOT re-validated
426
+ * here. Optimystic refuses a value that is not a finite number above zero, or is above its
427
+ * `MAX_COHORT_QUERY_TIMEOUT_MS` (about 4.97 days — the ceiling exists because a unit mix-up
428
+ * is the one way to exceed it), by throwing where the libp2p node is built: inside
429
+ * `CadreNode.start()` for the control network, and inside `CadreNode.addStrand` for a strand.
430
+ * Fractional values are accepted — this is a duration, not a count of peers.
431
+ */
432
+ cohortQueryTimeoutMs?: number;
433
+ /**
434
+ * The round trip this node assumes between itself and another machine, in milliseconds, for
435
+ * the control node and every strand node — as {@link cohortQueryTimeoutMs} is, and for the
436
+ * same reason: the setting describes the LINK, and a phone's control node and its strand
437
+ * nodes ride the same one. Omitted takes {@link DECLARED_LINK_ROUND_TRIP_MS} (2000 ms).
438
+ *
439
+ * This is NOT a timeout. It is the one stated assumption that cadre's own dial and
440
+ * reservation deadlines are DERIVED from, each by the number of round trips that operation
441
+ * was measured to cost: a peer-join catch-up's dial to one peer, its push response, one relay
442
+ * reservation drive, and the control-cohort dial budgets. Reaching another machine through a
443
+ * relay costs a fixed number of exchanges, so a deadline written as milliseconds has a link
444
+ * speed above which it can never open a connection — which is the defect this declaration
445
+ * exists to make impossible to reintroduce one budget at a time. The counts, the measurement
446
+ * behind them, and the ceiling that no declaration here can lift are in `link-budget.ts`.
447
+ *
448
+ * Raise it for a link slower than the relayed phone-to-phone band sereus assumes; the cost is
449
+ * the ordinary cost of longer deadlines, a peer that is genuinely gone holding each operation
450
+ * that much longer before it is abandoned and retried. Above about 2500 it buys nothing: two
451
+ * libp2p budgets that sereus cannot reach abandon the connection first
452
+ * (`tickets/blocked/how-slow-a-relayed-link-does-sereus-carry`).
453
+ *
454
+ * Refused where the libp2p node is built — inside `CadreNode.start()` for the control
455
+ * network, inside `CadreNode.addStrand` for a strand — if it is not a finite number above
456
+ * zero, because every consumer multiplies it into a deadline where a zero means "give up at
457
+ * once" and a `NaN` means "never".
458
+ */
459
+ linkRoundTripMs?: number;
409
460
  /**
410
461
  * Optional async resolver returning the multiaddrs to embed in invites
411
462
  * (and other owner-address contexts). When unset, `libp2pNode.getMultiaddrs()`
@@ -594,12 +645,16 @@ export interface ControlNetworkConfig {
594
645
  *
595
646
  * The two `*ClusterPolicy` BUILDERS are the same objects with the block-repair
596
647
  * corroboration yardstick declared from the machines enrolled in this party
597
- * ({@link resolveRepairYardstick}); handed no count, each returns its frozen base
598
- * constant unchanged. A network picks the derived number up when its libp2p node is
599
- * built, which for a strand is every wake from hibernation.
648
+ * ({@link resolveRepairYardstick}) and, if the host set one, its own per-peer read
649
+ * deadline in place of {@link COHORT_READ_DEADLINE_MS}. Both arrive in one named
650
+ * declarations object, because both are plain numbers meaning unrelated things;
651
+ * declaring neither returns the frozen base constant unchanged. A network picks the
652
+ * derived numbers up when its libp2p node is built, which for a strand is every wake
653
+ * from hibernation.
600
654
  */
601
655
  export {
602
656
  MIN_CLUSTER_SIZE,
657
+ COHORT_READ_DEADLINE_MS,
603
658
  CONTROL_REPLICATION_BREADTH,
604
659
  CONTROL_CLUSTER_POLICY,
605
660
  DEFAULT_STRAND_CLUSTER_SIZE,
@@ -610,6 +665,14 @@ export {
610
665
  strandClusterPolicy
611
666
  } from '@serfab/quereus-plugin-sereus';
612
667
 
668
+ /**
669
+ * The declared link round trip every cadre-owned dial and reservation deadline is derived from,
670
+ * re-exported beside {@link NetworkConfig.linkRoundTripMs} so a host reading the setting finds
671
+ * the default it replaces. The counts, the measurement and the derivation live in
672
+ * `link-budget.ts`.
673
+ */
674
+ export { DECLARED_LINK_ROUND_TRIP_MS } from './link-budget.js';
675
+
613
676
  /**
614
677
  * Main configuration for a CadreNode
615
678
  */
@@ -743,10 +806,12 @@ export interface CadreNodeConfig {
743
806
  * to a table it has never fetched creates a private copy of it that never merges.
744
807
  * `timeoutMs` bounds how long {@link CadreNode.addStrand} waits before rejecting with
745
808
  * `StrandAwaitingFirstSyncError` (retryable — the launch stays up and keeps probing);
746
- * `pollIntervalMs` is the probe cadence. Omit for the defaults (`DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`, 30 s;
747
- * `DEFAULT_STRAND_FIRST_SYNC_POLL_MS`, 500 ms). There is deliberately no way to disable
748
- * the gate: a machine that already holds the Header is never gated, so nothing that works
749
- * today is blocked by it.
809
+ * `pollIntervalMs` is the probe cadence. Omit for the defaults (`DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`,
810
+ * 120 s — sized for a machine joining through a relay on a slow link; that constant's doc
811
+ * comment carries the measurement behind the number and what the budget costs, and is the
812
+ * only copy of it; `DEFAULT_STRAND_FIRST_SYNC_POLL_MS`, 500 ms). There is
813
+ * deliberately no way to disable the gate: a machine that already holds the Header is never
814
+ * gated, so nothing that works today is blocked by it.
750
815
  */
751
816
  strandFirstSync?: StrandFirstSyncConfig;
752
817
 
@@ -1322,6 +1387,28 @@ export interface CadreNodeEvents {
1322
1387
  * revoked.
1323
1388
  */
1324
1389
  'strand:revoked': { strandId: string };
1390
+ /**
1391
+ * Emitted when this node holds a staged membership invitation for a CLOSED strand
1392
+ * that its own writes cannot redeem — the shape a REMOVED party hits when a manager
1393
+ * hands it a fresh invitation. Redeeming means writing this party's `Strand.Member`
1394
+ * row into the strand, and the machines that would carry that write are the ones the
1395
+ * remaining members refuse, so the attempt is made and fails. Two triggers, reported
1396
+ * at most once per re-arm of the membership loop (a further invitation staged while the
1397
+ * loop is still running does not reset the report):
1398
+ *
1399
+ * - CONFIRMED: the revoked-peer gate already flags this node as removed (the
1400
+ * `strand:revoked` case) and an invitation is staged.
1401
+ * - PROBABLE: `UNFINISHED_PASSES_BEFORE_ESCALATION` consecutive attempts left the
1402
+ * invitation staged. This one is a SUSPICION, not a verdict — the invitation's row
1403
+ * may simply not have replicated here yet on a slow strand — and the accompanying
1404
+ * warning names both causes.
1405
+ *
1406
+ * Nothing is stopped or torn down. A fresh invitation cannot re-admit a removed
1407
+ * party by itself: the remedy is a remaining manager admitting this party's member
1408
+ * key directly (`addMemberByManager`), after which the membership loop, which keeps
1409
+ * retrying, finishes the join on its own.
1410
+ */
1411
+ 'strand:rejoin-blocked': { strandId: string };
1325
1412
  /**
1326
1413
  * Emitted when the control network advertises a strand this node has no
1327
1414
  * registered `sAppConfig` for — i.e. a strand created by another member, or