@serfab/cadre-core 1.17.0 → 1.19.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 (148) hide show
  1. package/README.md +3 -2
  2. package/dist/cadre-invite-protocol.d.ts +35 -13
  3. package/dist/cadre-invite-protocol.js +90 -25
  4. package/dist/cadre-invite-protocol.js.map +1 -1
  5. package/dist/cadre-node.d.ts +586 -255
  6. package/dist/cadre-node.js +1182 -566
  7. package/dist/cadre-node.js.map +1 -1
  8. package/dist/control-cohort.d.ts +66 -9
  9. package/dist/control-cohort.js +67 -9
  10. package/dist/control-cohort.js.map +1 -1
  11. package/dist/control-database.d.ts +451 -193
  12. package/dist/control-database.js +701 -359
  13. package/dist/control-database.js.map +1 -1
  14. package/dist/control-formation-recorder.d.ts +46 -43
  15. package/dist/control-formation-recorder.js +77 -78
  16. package/dist/control-formation-recorder.js.map +1 -1
  17. package/dist/control-protocol-guard.d.ts +6 -0
  18. package/dist/control-protocol-guard.js +14 -2
  19. package/dist/control-protocol-guard.js.map +1 -1
  20. package/dist/control-schema-version.d.ts +48 -31
  21. package/dist/control-schema-version.js +67 -45
  22. package/dist/control-schema-version.js.map +1 -1
  23. package/dist/control-schema.d.ts +8 -8
  24. package/dist/control-schema.js +16 -16
  25. package/dist/control-schema.js.map +1 -1
  26. package/dist/control-write-retry.d.ts +42 -7
  27. package/dist/control-write-retry.js +87 -19
  28. package/dist/control-write-retry.js.map +1 -1
  29. package/dist/enrolled-machine-store.d.ts +3 -3
  30. package/dist/enrolled-machine-store.js +3 -3
  31. package/dist/formation-token.d.ts +48 -0
  32. package/dist/formation-token.js +26 -0
  33. package/dist/formation-token.js.map +1 -0
  34. package/dist/index.d.ts +15 -14
  35. package/dist/index.js +17 -14
  36. package/dist/index.js.map +1 -1
  37. package/dist/joined-strand-store.d.ts +26 -9
  38. package/dist/joined-strand-store.js +63 -24
  39. package/dist/joined-strand-store.js.map +1 -1
  40. package/dist/link-budget.d.ts +9 -7
  41. package/dist/link-budget.js +9 -7
  42. package/dist/link-budget.js.map +1 -1
  43. package/dist/member-redemption.d.ts +15 -9
  44. package/dist/member-redemption.js +13 -8
  45. package/dist/member-redemption.js.map +1 -1
  46. package/dist/membership-view.d.ts +91 -0
  47. package/dist/membership-view.js +110 -0
  48. package/dist/membership-view.js.map +1 -0
  49. package/dist/owner-grant-protocol.d.ts +11 -7
  50. package/dist/owner-grant-protocol.js +31 -17
  51. package/dist/owner-grant-protocol.js.map +1 -1
  52. package/dist/peer-addr-book.d.ts +10 -1
  53. package/dist/peer-addr-book.js +4 -3
  54. package/dist/peer-addr-book.js.map +1 -1
  55. package/dist/peer-join-backfill.d.ts +96 -30
  56. package/dist/peer-join-backfill.js +272 -128
  57. package/dist/peer-join-backfill.js.map +1 -1
  58. package/dist/pending-join-runner.d.ts +48 -18
  59. package/dist/pending-join-runner.js +74 -56
  60. package/dist/pending-join-runner.js.map +1 -1
  61. package/dist/seed-bootstrap.d.ts +47 -40
  62. package/dist/seed-bootstrap.js +66 -61
  63. package/dist/seed-bootstrap.js.map +1 -1
  64. package/dist/self-address-update.d.ts +13 -0
  65. package/dist/self-address-update.js +19 -0
  66. package/dist/self-address-update.js.map +1 -0
  67. package/dist/strand-database.d.ts +23 -2
  68. package/dist/strand-database.js +26 -6
  69. package/dist/strand-database.js.map +1 -1
  70. package/dist/strand-first-sync-gate.d.ts +176 -89
  71. package/dist/strand-first-sync-gate.js +288 -122
  72. package/dist/strand-first-sync-gate.js.map +1 -1
  73. package/dist/strand-formation-deadlines.d.ts +9 -9
  74. package/dist/strand-formation-deadlines.js +9 -9
  75. package/dist/strand-formation-manager.d.ts +23 -17
  76. package/dist/strand-formation-manager.js +46 -44
  77. package/dist/strand-formation-manager.js.map +1 -1
  78. package/dist/strand-formation-protocol.d.ts +5 -8
  79. package/dist/strand-formation-protocol.js +101 -16
  80. package/dist/strand-formation-protocol.js.map +1 -1
  81. package/dist/strand-instance-manager.d.ts +26 -17
  82. package/dist/strand-instance-manager.js +119 -50
  83. package/dist/strand-instance-manager.js.map +1 -1
  84. package/dist/strand-membership-reconciler.d.ts +16 -13
  85. package/dist/strand-membership-reconciler.js +28 -22
  86. package/dist/strand-membership-reconciler.js.map +1 -1
  87. package/dist/strand-membership-writer.d.ts +40 -28
  88. package/dist/strand-membership-writer.js +212 -153
  89. package/dist/strand-membership-writer.js.map +1 -1
  90. package/dist/strand-move.js +5 -2
  91. package/dist/strand-move.js.map +1 -1
  92. package/dist/strand-revocation-enforcer.d.ts +134 -60
  93. package/dist/strand-revocation-enforcer.js +144 -71
  94. package/dist/strand-revocation-enforcer.js.map +1 -1
  95. package/dist/strand-solicitation.d.ts +50 -39
  96. package/dist/strand-solicitation.js +10 -3
  97. package/dist/strand-solicitation.js.map +1 -1
  98. package/dist/strand-watcher.d.ts +80 -13
  99. package/dist/strand-watcher.js +152 -48
  100. package/dist/strand-watcher.js.map +1 -1
  101. package/dist/table-change-watch.d.ts +63 -0
  102. package/dist/table-change-watch.js +209 -0
  103. package/dist/table-change-watch.js.map +1 -0
  104. package/dist/timeout-scheduler.d.ts +4 -3
  105. package/dist/timeout-scheduler.js +4 -3
  106. package/dist/timeout-scheduler.js.map +1 -1
  107. package/dist/types.d.ts +90 -49
  108. package/dist/types.js +4 -4
  109. package/dist/types.js.map +1 -1
  110. package/package.json +8 -8
  111. package/src/cadre-invite-protocol.ts +119 -31
  112. package/src/cadre-node.ts +1289 -588
  113. package/src/control-cohort.ts +86 -9
  114. package/src/control-database.ts +848 -403
  115. package/src/control-formation-recorder.ts +359 -353
  116. package/src/control-protocol-guard.ts +16 -2
  117. package/src/control-schema-version.ts +101 -51
  118. package/src/control-schema.ts +16 -16
  119. package/src/control-write-retry.ts +91 -19
  120. package/src/delegate-admission.ts +351 -351
  121. package/src/enrolled-machine-store.ts +3 -3
  122. package/src/formation-token.ts +63 -0
  123. package/src/index.ts +30 -18
  124. package/src/joined-strand-store.ts +69 -24
  125. package/src/link-budget.ts +9 -7
  126. package/src/member-redemption.ts +20 -13
  127. package/src/membership-view.ts +144 -0
  128. package/src/owner-grant-protocol.ts +33 -18
  129. package/src/peer-addr-book.ts +4 -3
  130. package/src/peer-join-backfill.ts +961 -761
  131. package/src/pending-join-runner.ts +76 -53
  132. package/src/seed-bootstrap.ts +72 -67
  133. package/src/self-address-update.ts +19 -0
  134. package/src/strand-database.ts +352 -322
  135. package/src/strand-first-sync-gate.ts +334 -134
  136. package/src/strand-formation-deadlines.ts +227 -227
  137. package/src/strand-formation-manager.ts +731 -725
  138. package/src/strand-formation-protocol.ts +122 -24
  139. package/src/strand-instance-manager.ts +2092 -2026
  140. package/src/strand-membership-reconciler.ts +31 -25
  141. package/src/strand-membership-writer.ts +2093 -2019
  142. package/src/strand-move.ts +5 -2
  143. package/src/strand-revocation-enforcer.ts +740 -654
  144. package/src/strand-solicitation.ts +61 -34
  145. package/src/strand-watcher.ts +175 -56
  146. package/src/table-change-watch.ts +285 -0
  147. package/src/timeout-scheduler.ts +29 -28
  148. package/src/types.ts +89 -51
@@ -21,8 +21,7 @@
21
21
  * - **Retries fast while the join is unfinished, slowly while it is only waiting.** A pass
22
22
  * that leaves the join unfinished re-arms on a ladder starting at
23
23
  * {@link INITIAL_JOIN_RETRY_INTERVAL_MS} and doubling, capped at the configured poll
24
- * interval (default {@link DEFAULT_REVOCATION_POLL_INTERVAL_MS}; an embedder's
25
- * `revocationEnforcement.pollIntervalMs` is mirrored here by the instance manager). A pass
24
+ * interval (default {@link DEFAULT_MEMBERSHIP_RECONCILE_INTERVAL_MS}). A pass
26
25
  * that finds no member row AND no staged invitation is not an unfinished join but a wait
27
26
  * for someone to admit this party — nothing this machine can do faster — so it re-arms on
28
27
  * the flat poll interval and resets the ladder. The loop stops on the done state.
@@ -152,11 +151,17 @@ import {
152
151
  registerMemberPeer,
153
152
  type StrandWriteOptions,
154
153
  } from './strand-membership-writer.js';
155
- import { DEFAULT_REVOCATION_POLL_INTERVAL_MS } from './strand-revocation-enforcer.js';
156
154
  import { defaultTimeoutScheduler, type TimeoutScheduler } from './timeout-scheduler.js';
157
155
 
158
156
  const log = debug('sereus:cadre:strand-membership-reconciler');
159
157
 
158
+ /**
159
+ * Default poll interval, ms: the loop's idle cadence while nobody has admitted this party, and
160
+ * the cap of its unfinished-join retry ladder. The loop stops once joined, so this matters only
161
+ * while a machine is joining.
162
+ */
163
+ export const DEFAULT_MEMBERSHIP_RECONCILE_INTERVAL_MS = 30_000;
164
+
160
165
  /**
161
166
  * Idle passes (no member row, no staged invitation) before ONE `console.warn`
162
167
  * escalation — at the default 30 s cadence, about five minutes of waiting. The loop
@@ -182,9 +187,8 @@ export const UNFINISHED_PASSES_BEFORE_ESCALATION = 10;
182
187
  * unwritable, a database not yet published. Doubles per such pass up to the configured
183
188
  * poll interval. Sized for what it retries: invite-row replication resolves in about a
184
189
  * second over a direct connection and a few over a relay. The poll interval is NOT sized
185
- * for that — it is the revocation enforcer's deny-set refresh cadence, mirrored here for
186
- * the idle case (waiting to be admitted at all), which is why this ladder exists instead
187
- * of a shorter interval.
190
+ * for that — it is sized for the idle case (waiting to be admitted at all), which is why
191
+ * this ladder exists instead of a shorter interval.
188
192
  */
189
193
  export const INITIAL_JOIN_RETRY_INTERVAL_MS = 1_000;
190
194
 
@@ -366,19 +370,18 @@ export interface StrandMembershipReconciliationConfig {
366
370
  enabled?: boolean;
367
371
  /**
368
372
  * Idle cadence, ms — how often the loop re-checks while nobody has admitted this party
369
- * yet — and the CAP of the unfinished-join retry ladder. When omitted the instance
370
- * manager mirrors the revocation enforcer's configured cadence; default
371
- * {@link DEFAULT_REVOCATION_POLL_INTERVAL_MS}.
373
+ * yet — and the CAP of the unfinished-join retry ladder. Default
374
+ * {@link DEFAULT_MEMBERSHIP_RECONCILE_INTERVAL_MS}.
372
375
  */
373
376
  pollIntervalMs?: number;
374
377
  }
375
378
 
376
379
  /**
377
- * The per-strand membership reconciliation loop. Lifecycle mirrors
378
- * `StrandRevocationEnforcer`: created in `StrandInstanceManager.buildStrandRuntime`
379
- * (closed strands with a party key only), started right after the strand database
380
- * initializes, stopped and dropped in `releaseRuntime` — so quiesce → resume rebuilds
381
- * it and re-runs the (idempotent) ladder from scratch.
380
+ * The per-strand membership reconciliation loop. Created in
381
+ * `StrandInstanceManager.buildStrandRuntime` (closed strands with a party key only),
382
+ * started right after the strand database initializes, stopped and dropped in
383
+ * `releaseRuntime` — so quiesce → resume rebuilds it and re-runs the (idempotent) ladder
384
+ * from scratch.
382
385
  */
383
386
  export class StrandMembershipReconciler {
384
387
  private readonly scheduler: TimeoutScheduler;
@@ -423,7 +426,7 @@ export class StrandMembershipReconciler {
423
426
  config?: StrandMembershipReconciliationConfig
424
427
  ) {
425
428
  this.scheduler = deps.scheduler ?? defaultTimeoutScheduler;
426
- this.pollIntervalMs = config?.pollIntervalMs ?? DEFAULT_REVOCATION_POLL_INTERVAL_MS;
429
+ this.pollIntervalMs = config?.pollIntervalMs ?? DEFAULT_MEMBERSHIP_RECONCILE_INTERVAL_MS;
427
430
  }
428
431
 
429
432
  /**
@@ -573,16 +576,19 @@ export class StrandMembershipReconciler {
573
576
  private async attempt(db: Database, keyPair: Ed25519KeyPair): Promise<void> {
574
577
  try {
575
578
  if (!(await this.ensureMembership(db, keyPair))) return;
576
- // NOTE: the redemption (`Member` + `ConsumedInvite`) and the binding below are two
577
- // SEPARATE commits — measured at 27 and 18 `/cluster` streams on 2026-09-17.
578
- // One write batch could carry both (`MemberPeer.MemberExists`
579
- // reads the LIVE `Member` table, and `MemberPeer.Authorized`'s add branch only verifies a
580
- // self-signature over the new row), worth perhaps a third of that plus one commit
581
- // round-trip. Left as two deliberately: the saving is unmeasured, and merging them merges
582
- // their failure modes — today a redemption that lands but reports torn simply heals on the
583
- // next pass, which sees the member row and proceeds to the binding. If a joiner's write
584
- // cost ever shows up in a measurement, try the single transaction and measure against the
585
- // 27 + 18 baseline.
579
+ // NOTE: accepted tradeoff — the redemption (`Member` + `ConsumedInvite`) and the binding
580
+ // below stay two SEPARATE commits on purpose. Merging them would save one commit: its
581
+ // serial latency and, since Optimystic 1.19.0 pends and commits a transaction's collections
582
+ // together, its `/cluster` requests (the two commits cost 6 on a two-member strand in
583
+ // `message-cost-accounting` on 2026-10-25, 20 before). It would also let a torn commit
584
+ // (Optimystic can report a multi-collection commit partly saved) or the cohort applying
585
+ // the collections out of order show this machine's `MemberPeer` without its `Member`,
586
+ // which the revocation enforcer reads as a removed member and hangs up on — fail closed,
587
+ // undone only by a manager clearing the binding (module doc of
588
+ // `strand-revocation-enforcer.ts`). And it would add a half-committed shape (`MemberPeer`
589
+ // saved, the invitation still live) that {@link reportHalfCommittedJoin}'s dropping of the
590
+ // invitation gets wrong. Revisit when Optimystic offers an all-or-nothing multi-collection
591
+ // commit, or when the enforcer can tell a not-yet-seated binding from a removed member's.
586
592
  await this.ensureBinding(db, keyPair);
587
593
  } finally {
588
594
  this.noteAttemptOutcome();