@serfab/cadre-core 1.17.0 → 1.18.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 (144) 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 +1176 -564
  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 +14 -13
  35. package/dist/index.js +16 -13
  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 +89 -20
  56. package/dist/peer-join-backfill.js +265 -108
  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/strand-database.d.ts +23 -2
  65. package/dist/strand-database.js +26 -6
  66. package/dist/strand-database.js.map +1 -1
  67. package/dist/strand-first-sync-gate.d.ts +176 -89
  68. package/dist/strand-first-sync-gate.js +288 -122
  69. package/dist/strand-first-sync-gate.js.map +1 -1
  70. package/dist/strand-formation-deadlines.d.ts +9 -9
  71. package/dist/strand-formation-deadlines.js +9 -9
  72. package/dist/strand-formation-manager.d.ts +23 -17
  73. package/dist/strand-formation-manager.js +46 -44
  74. package/dist/strand-formation-manager.js.map +1 -1
  75. package/dist/strand-formation-protocol.d.ts +5 -8
  76. package/dist/strand-formation-protocol.js +101 -16
  77. package/dist/strand-formation-protocol.js.map +1 -1
  78. package/dist/strand-instance-manager.d.ts +26 -17
  79. package/dist/strand-instance-manager.js +119 -50
  80. package/dist/strand-instance-manager.js.map +1 -1
  81. package/dist/strand-membership-reconciler.d.ts +16 -13
  82. package/dist/strand-membership-reconciler.js +30 -22
  83. package/dist/strand-membership-reconciler.js.map +1 -1
  84. package/dist/strand-membership-writer.d.ts +40 -28
  85. package/dist/strand-membership-writer.js +212 -153
  86. package/dist/strand-membership-writer.js.map +1 -1
  87. package/dist/strand-move.js +5 -2
  88. package/dist/strand-move.js.map +1 -1
  89. package/dist/strand-revocation-enforcer.d.ts +134 -60
  90. package/dist/strand-revocation-enforcer.js +144 -71
  91. package/dist/strand-revocation-enforcer.js.map +1 -1
  92. package/dist/strand-solicitation.d.ts +50 -39
  93. package/dist/strand-solicitation.js +10 -3
  94. package/dist/strand-solicitation.js.map +1 -1
  95. package/dist/strand-watcher.d.ts +80 -13
  96. package/dist/strand-watcher.js +152 -48
  97. package/dist/strand-watcher.js.map +1 -1
  98. package/dist/table-change-watch.d.ts +63 -0
  99. package/dist/table-change-watch.js +209 -0
  100. package/dist/table-change-watch.js.map +1 -0
  101. package/dist/timeout-scheduler.d.ts +4 -3
  102. package/dist/timeout-scheduler.js +4 -3
  103. package/dist/timeout-scheduler.js.map +1 -1
  104. package/dist/types.d.ts +90 -49
  105. package/dist/types.js +4 -4
  106. package/dist/types.js.map +1 -1
  107. package/package.json +8 -8
  108. package/src/cadre-invite-protocol.ts +119 -31
  109. package/src/cadre-node.ts +1282 -584
  110. package/src/control-cohort.ts +86 -9
  111. package/src/control-database.ts +848 -403
  112. package/src/control-formation-recorder.ts +359 -353
  113. package/src/control-protocol-guard.ts +16 -2
  114. package/src/control-schema-version.ts +101 -51
  115. package/src/control-schema.ts +16 -16
  116. package/src/control-write-retry.ts +91 -19
  117. package/src/delegate-admission.ts +351 -351
  118. package/src/enrolled-machine-store.ts +3 -3
  119. package/src/formation-token.ts +63 -0
  120. package/src/index.ts +30 -17
  121. package/src/joined-strand-store.ts +69 -24
  122. package/src/link-budget.ts +9 -7
  123. package/src/member-redemption.ts +20 -13
  124. package/src/membership-view.ts +144 -0
  125. package/src/owner-grant-protocol.ts +33 -18
  126. package/src/peer-addr-book.ts +4 -3
  127. package/src/peer-join-backfill.ts +971 -761
  128. package/src/pending-join-runner.ts +76 -53
  129. package/src/seed-bootstrap.ts +72 -67
  130. package/src/strand-database.ts +352 -322
  131. package/src/strand-first-sync-gate.ts +334 -134
  132. package/src/strand-formation-deadlines.ts +227 -227
  133. package/src/strand-formation-manager.ts +731 -725
  134. package/src/strand-formation-protocol.ts +122 -24
  135. package/src/strand-instance-manager.ts +2092 -2026
  136. package/src/strand-membership-reconciler.ts +33 -25
  137. package/src/strand-membership-writer.ts +2093 -2019
  138. package/src/strand-move.ts +5 -2
  139. package/src/strand-revocation-enforcer.ts +740 -654
  140. package/src/strand-solicitation.ts +61 -34
  141. package/src/strand-watcher.ts +175 -56
  142. package/src/table-change-watch.ts +285 -0
  143. package/src/timeout-scheduler.ts +29 -28
  144. 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,21 @@ 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. One transaction would save no `/cluster`
581
+ // requests: Optimystic pends and commits each collection on its own, about 4 requests per
582
+ // collection (`blocked/optimystic-multi-collection-commit-costs-four-requests-per-collection`),
583
+ // and both commits already span several collections (each table plus its UNIQUE trees),
584
+ // so merging saves only one commit's serial latency. It would also let a torn commit
585
+ // (Optimystic can report a multi-collection commit partly saved) or the cohort applying
586
+ // the collections out of order show this machine's `MemberPeer` without its `Member`,
587
+ // which the revocation enforcer reads as a removed member and hangs up on — fail closed,
588
+ // undone only by a manager clearing the binding (module doc of
589
+ // `strand-revocation-enforcer.ts`). And it would add a half-committed shape (`MemberPeer`
590
+ // saved, the invitation still live) that {@link reportHalfCommittedJoin}'s dropping of the
591
+ // invitation gets wrong. Revisit when Optimystic offers an all-or-nothing multi-collection
592
+ // commit, or when its per-coordinator batching lands and the enforcer can tell a
593
+ // not-yet-seated binding from a removed member's.
586
594
  await this.ensureBinding(db, keyPair);
587
595
  } finally {
588
596
  this.noteAttemptOutcome();