@serfab/cadre-core 0.9.0 → 0.10.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 +4 -2
  2. package/dist/bootstrap-peer-store-file.d.ts +13 -0
  3. package/dist/bootstrap-peer-store-file.js +42 -0
  4. package/dist/bootstrap-peer-store-file.js.map +1 -0
  5. package/dist/bootstrap-peer-store.d.ts +73 -0
  6. package/dist/bootstrap-peer-store.js +128 -0
  7. package/dist/bootstrap-peer-store.js.map +1 -0
  8. package/dist/cadre-node.d.ts +918 -53
  9. package/dist/cadre-node.js +1843 -190
  10. package/dist/cadre-node.js.map +1 -1
  11. package/dist/control-authorization.d.ts +93 -0
  12. package/dist/control-authorization.js +57 -0
  13. package/dist/control-authorization.js.map +1 -0
  14. package/dist/control-database.d.ts +990 -50
  15. package/dist/control-database.js +1459 -108
  16. package/dist/control-database.js.map +1 -1
  17. package/dist/control-formation-recorder.d.ts +89 -10
  18. package/dist/control-formation-recorder.js +129 -14
  19. package/dist/control-formation-recorder.js.map +1 -1
  20. package/dist/control-schema.d.ts +1 -1
  21. package/dist/control-schema.js +774 -210
  22. package/dist/control-schema.js.map +1 -1
  23. package/dist/control-stream.d.ts +38 -4
  24. package/dist/control-stream.js +70 -4
  25. package/dist/control-stream.js.map +1 -1
  26. package/dist/control-write-retry.d.ts +151 -0
  27. package/dist/control-write-retry.js +411 -0
  28. package/dist/control-write-retry.js.map +1 -0
  29. package/dist/delegate-admission.d.ts +134 -0
  30. package/dist/delegate-admission.js +244 -0
  31. package/dist/delegate-admission.js.map +1 -0
  32. package/dist/device-token.d.ts +11 -11
  33. package/dist/device-token.js +14 -13
  34. package/dist/device-token.js.map +1 -1
  35. package/dist/ed25519-key.d.ts +26 -0
  36. package/dist/ed25519-key.js +56 -1
  37. package/dist/ed25519-key.js.map +1 -1
  38. package/dist/file-durable-slot.d.ts +20 -0
  39. package/dist/file-durable-slot.js +53 -0
  40. package/dist/file-durable-slot.js.map +1 -0
  41. package/dist/formation-approval.d.ts +157 -0
  42. package/dist/formation-approval.js +375 -0
  43. package/dist/formation-approval.js.map +1 -0
  44. package/dist/fs-atomic.d.ts +38 -0
  45. package/dist/fs-atomic.js +97 -0
  46. package/dist/fs-atomic.js.map +1 -0
  47. package/dist/identity-key.d.ts +49 -0
  48. package/dist/identity-key.js +81 -0
  49. package/dist/identity-key.js.map +1 -0
  50. package/dist/index.d.ts +16 -6
  51. package/dist/index.js +39 -5
  52. package/dist/index.js.map +1 -1
  53. package/dist/key-store-file.d.ts +2 -24
  54. package/dist/key-store-file.js +10 -77
  55. package/dist/key-store-file.js.map +1 -1
  56. package/dist/membership-connection-gater.d.ts +146 -0
  57. package/dist/membership-connection-gater.js +180 -0
  58. package/dist/membership-connection-gater.js.map +1 -0
  59. package/dist/node-local-snapshot.d.ts +109 -0
  60. package/dist/node-local-snapshot.js +182 -0
  61. package/dist/node-local-snapshot.js.map +1 -0
  62. package/dist/peer-authorization.d.ts +135 -18
  63. package/dist/peer-authorization.js +152 -21
  64. package/dist/peer-authorization.js.map +1 -1
  65. package/dist/peer-record.d.ts +21 -8
  66. package/dist/peer-record.js +26 -10
  67. package/dist/peer-record.js.map +1 -1
  68. package/dist/relay-addrs.d.ts +45 -0
  69. package/dist/relay-addrs.js +103 -0
  70. package/dist/relay-addrs.js.map +1 -0
  71. package/dist/relay-reservation.d.ts +266 -0
  72. package/dist/relay-reservation.js +613 -0
  73. package/dist/relay-reservation.js.map +1 -0
  74. package/dist/seed-bootstrap.d.ts +221 -56
  75. package/dist/seed-bootstrap.js +328 -188
  76. package/dist/seed-bootstrap.js.map +1 -1
  77. package/dist/seed-trust-policy.d.ts +47 -21
  78. package/dist/seed-trust-policy.js +24 -15
  79. package/dist/seed-trust-policy.js.map +1 -1
  80. package/dist/strand-addr-protocol.d.ts +42 -13
  81. package/dist/strand-addr-protocol.js +58 -52
  82. package/dist/strand-addr-protocol.js.map +1 -1
  83. package/dist/strand-backfill.d.ts +109 -0
  84. package/dist/strand-backfill.js +279 -0
  85. package/dist/strand-backfill.js.map +1 -0
  86. package/dist/strand-formation-manager.d.ts +38 -9
  87. package/dist/strand-formation-manager.js +120 -17
  88. package/dist/strand-formation-manager.js.map +1 -1
  89. package/dist/strand-formation-protocol.d.ts +103 -4
  90. package/dist/strand-formation-protocol.js +232 -4
  91. package/dist/strand-formation-protocol.js.map +1 -1
  92. package/dist/strand-instance-manager.d.ts +39 -1
  93. package/dist/strand-instance-manager.js +92 -14
  94. package/dist/strand-instance-manager.js.map +1 -1
  95. package/dist/strand-member-registry.d.ts +5 -5
  96. package/dist/strand-member-registry.js +18 -10
  97. package/dist/strand-member-registry.js.map +1 -1
  98. package/dist/strand-membership-writer.d.ts +482 -88
  99. package/dist/strand-membership-writer.js +757 -136
  100. package/dist/strand-membership-writer.js.map +1 -1
  101. package/dist/strand-solicitation.d.ts +99 -33
  102. package/dist/strand-solicitation.js +100 -52
  103. package/dist/strand-solicitation.js.map +1 -1
  104. package/dist/strand-transport-key.d.ts +40 -0
  105. package/dist/strand-transport-key.js +56 -0
  106. package/dist/strand-transport-key.js.map +1 -0
  107. package/dist/strand-wake-protocol.d.ts +9 -7
  108. package/dist/strand-wake-protocol.js +22 -46
  109. package/dist/strand-wake-protocol.js.map +1 -1
  110. package/dist/strand-watcher.d.ts +17 -2
  111. package/dist/strand-watcher.js +47 -3
  112. package/dist/strand-watcher.js.map +1 -1
  113. package/dist/trusted-owner-store-file.d.ts +12 -0
  114. package/dist/trusted-owner-store-file.js +39 -0
  115. package/dist/trusted-owner-store-file.js.map +1 -0
  116. package/dist/trusted-owner-store.d.ts +64 -0
  117. package/dist/trusted-owner-store.js +126 -0
  118. package/dist/trusted-owner-store.js.map +1 -0
  119. package/dist/types.d.ts +275 -20
  120. package/dist/types.js +13 -0
  121. package/dist/types.js.map +1 -1
  122. package/package.json +19 -8
  123. package/src/bootstrap-peer-store-file.ts +44 -0
  124. package/src/bootstrap-peer-store.ts +172 -0
  125. package/src/cadre-node.ts +1992 -202
  126. package/src/control-authorization.ts +114 -0
  127. package/src/control-database.ts +1704 -146
  128. package/src/control-formation-recorder.ts +172 -19
  129. package/src/control-schema.ts +786 -222
  130. package/src/control-stream.ts +81 -4
  131. package/src/control-write-retry.ts +473 -0
  132. package/src/delegate-admission.ts +302 -0
  133. package/src/device-token.ts +18 -13
  134. package/src/ed25519-key.ts +134 -69
  135. package/src/file-durable-slot.ts +61 -0
  136. package/src/formation-approval.ts +604 -0
  137. package/src/fs-atomic.ts +107 -0
  138. package/src/identity-key.ts +109 -0
  139. package/src/index.ts +136 -5
  140. package/src/key-store-file.ts +10 -81
  141. package/src/membership-connection-gater.ts +204 -0
  142. package/src/node-local-snapshot.ts +260 -0
  143. package/src/peer-authorization.ts +199 -21
  144. package/src/peer-record.ts +28 -10
  145. package/src/relay-addrs.ts +111 -0
  146. package/src/relay-reservation.ts +823 -0
  147. package/src/seed-bootstrap.ts +397 -197
  148. package/src/seed-trust-policy.ts +58 -25
  149. package/src/strand-addr-protocol.ts +84 -53
  150. package/src/strand-backfill.ts +366 -0
  151. package/src/strand-formation-manager.ts +145 -19
  152. package/src/strand-formation-protocol.ts +309 -8
  153. package/src/strand-instance-manager.ts +111 -14
  154. package/src/strand-member-registry.ts +18 -13
  155. package/src/strand-membership-writer.ts +1430 -640
  156. package/src/strand-solicitation.ts +155 -90
  157. package/src/strand-transport-key.ts +58 -0
  158. package/src/strand-wake-protocol.ts +29 -46
  159. package/src/strand-watcher.ts +66 -3
  160. package/src/trusted-owner-store-file.ts +41 -0
  161. package/src/trusted-owner-store.ts +171 -0
  162. package/src/types.ts +292 -21
@@ -20,16 +20,86 @@
20
20
  import debug from 'debug';
21
21
  import { multiaddr } from '@multiformats/multiaddr';
22
22
  import { writeFrame, withTimeout } from './control-stream.js';
23
+ import { canonicalJson } from './canonical-json.js';
24
+ import { requireEd25519PublicKeyB64 } from './ed25519-key.js';
25
+ import { verifyFormationConsent } from './peer-authorization.js';
26
+ // seed-bootstrap imports neither this protocol nor the manager/solicitation layers,
27
+ // so this import introduces no cycle.
28
+ import { ed25519PublicKeyB64FromPeerId } from './seed-bootstrap.js';
23
29
  const log = debug('sereus:cadre:formation-proto');
24
30
  /** Protocol id for the native formation transport (parallel to `/sereus/seed/1.0.0`). */
25
31
  export const FORMATION_PROTOCOL = '/sereus/formation/1.0.0';
32
+ /**
33
+ * Rejection reason for an invitation that cannot be redeemed at all — unknown, expired, or
34
+ * fully spent. Shared with `StrandFormationManager`, which reports an
35
+ * `InvitationExhaustedError` (an invite spent out from under a redemption already in flight)
36
+ * with this SAME wording on purpose: a joiner that lost the race must be indistinguishable
37
+ * from a non-racing latecomer, so the two sites must never drift apart.
38
+ */
39
+ export const INVALID_TOKEN_REASON = 'Invalid token';
26
40
  /** Maximum formation message size (1MB). */
27
41
  const MAX_FORMATION_MSG_SIZE = 1024 * 1024;
28
42
  /** Default whole-session timeout (ms). */
29
43
  const DEFAULT_SESSION_TIMEOUT_MS = 30000;
30
44
  /** Default per-step (single read/write) timeout (ms). */
31
45
  const DEFAULT_STEP_TIMEOUT_MS = 5000;
32
- /** Default cap on concurrent inbound formation sessions. */
46
+ /**
47
+ * Default provisioning budget (ms) — distinct from {@link DEFAULT_STEP_TIMEOUT_MS} because
48
+ * `provisionStrand` is real work (DB writes, and an outbound approval-hook HTTP call when
49
+ * the invite carries a `ValidationUrl`), not a bare wire read/write.
50
+ *
51
+ * Ordering is deliberate — each layer must be able to fail and report before the layer above
52
+ * it gives up:
53
+ *
54
+ * approval hook (10 s, `formation-approval.ts`)
55
+ * < responder provisioning (12 s)
56
+ * < initiator await-response (15 s)
57
+ * < session (30 s)
58
+ *
59
+ * The 3 s margin between responder provisioning and initiator await-response is the wire
60
+ * latency budget for the result frame to travel back.
61
+ *
62
+ * The last {@link PROVISION_SETTLE_GRACE_MS} of this budget is a settle grace, so the default
63
+ * WORK budget (12 s − 2 s = 10 s) EQUALS the hook's own 10 s: a dead hook races 'Formation
64
+ * approval unavailable, retry' against 'Formation provisioning timed out'. Both are retryable
65
+ * and both leave the invite unspent, so the race is benign.
66
+ */
67
+ const DEFAULT_PROVISION_TIMEOUT_MS = 12000;
68
+ /**
69
+ * Settle grace (ms), carved OUT of `provisionTimeoutMs` — never added on top, so the budget
70
+ * ladder above is untouched. The listener aborts the provisioning hook when the work budget
71
+ * ({@link splitProvisionBudget}) expires, then waits up to this grace for the work to settle
72
+ * anyway: once the `FormationUsage` insert has been issued nothing can un-spend it (the table
73
+ * is append-only), so a provisioning that lands late is ADOPTED and reported as a real
74
+ * approval rather than lying "timed out" over a spent invite. A hook that observes the
75
+ * abort before writing leaves the invite unspent.
76
+ */
77
+ const PROVISION_SETTLE_GRACE_MS = 2000;
78
+ /**
79
+ * Wire-latency margin (ms) added to the responder's provisioning budget to get the
80
+ * initiator's await-response budget — the travel time for the result frame to come back
81
+ * after the responder finishes (see {@link DEFAULT_PROVISION_TIMEOUT_MS}).
82
+ * `StrandFormationManager` reuses this constant so a CONFIGURED budget preserves the same
83
+ * margin the defaults do, instead of applying the same number to both sides and racing the
84
+ * initiator's own timeout against the responder's clean rejection reply. The responder's
85
+ * clamp ({@link provisionCeilingMs}) holds the same margin back, so the ordering survives a
86
+ * budget large enough that BOTH roles get clamped.
87
+ *
88
+ * NOTE: 3 s is a fixed assumption about how long the result frame takes to travel back. If
89
+ * formation ever runs over paths where that frame can take longer (a slow circuit relay, a
90
+ * congested mobile link), the margin is too thin and the initiator's timeout races the
91
+ * responder's reply again — raise it, or derive it from an observed round-trip.
92
+ */
93
+ export const PROVISION_RESPONSE_TRAVEL_MARGIN_MS = 3000;
94
+ /** Default initiator await-response budget (ms); see {@link DEFAULT_PROVISION_TIMEOUT_MS}. */
95
+ const DEFAULT_INITIATOR_PROVISION_TIMEOUT_MS = DEFAULT_PROVISION_TIMEOUT_MS + PROVISION_RESPONSE_TRAVEL_MARGIN_MS;
96
+ /**
97
+ * Default cap on concurrent inbound formation sessions.
98
+ * NOTE: a longer provisioning budget holds a session's slot longer, so a slow hook can
99
+ * exhaust this cap and make the listener reject fresh joiners with 'Too many concurrent
100
+ * formation sessions'. Fine at the current default; revisit if `provisionTimeoutMs` is
101
+ * raised well above its default or this cap is lowered.
102
+ */
33
103
  const DEFAULT_MAX_CONCURRENT_SESSIONS = 100;
34
104
  // ── Stream framing helpers ───────────────────────────────────────────────────
35
105
  /**
@@ -114,6 +184,49 @@ export function isValidResponderCreatesResult(response) {
114
184
  return false;
115
185
  return true;
116
186
  }
187
+ /**
188
+ * Ceiling for a resolved provisioning budget.
189
+ *
190
+ * The session budget also has to cover the wire step that PRECEDES provisioning (the
191
+ * responder's contact read, the initiator's dial-connect), so a whole step is held back
192
+ * rather than triggering only on a literal overrun.
193
+ *
194
+ * `reserveMs` is the EXTRA room only the responder holds back, so its own budget still
195
+ * lands strictly before the initiator's larger await-response budget even when both are
196
+ * clamped — without it, any configured budget at or above `sessionTimeoutMs -
197
+ * stepTimeoutMs` clamps both roles onto the same number and the responder's clean
198
+ * rejection races the initiator's timeout again. Capped at half the remaining room (like
199
+ * {@link splitProvisionBudget}) so a small session config still spends most of it working.
200
+ */
201
+ function provisionCeilingMs(sessionTimeoutMs, stepTimeoutMs, reserveMs) {
202
+ const roomMs = sessionTimeoutMs - stepTimeoutMs;
203
+ return Math.max(1, roomMs - Math.min(reserveMs, Math.floor(roomMs / 2)));
204
+ }
205
+ /**
206
+ * Resolve a caller-supplied provisioning budget: `0`/negative means "unset" (use
207
+ * `defaultMs`). If the result would let provisioning outlive the session — no result
208
+ * frame is ever sent, exactly the failure this budget exists to prevent — clamp it to
209
+ * {@link provisionCeilingMs} and log a warning.
210
+ */
211
+ function resolveProvisionTimeoutMs(configured, defaultMs, sessionTimeoutMs, stepTimeoutMs, role, reserveMs = 0) {
212
+ const requested = configured && configured > 0 ? configured : defaultMs;
213
+ const ceilingMs = provisionCeilingMs(sessionTimeoutMs, stepTimeoutMs, reserveMs);
214
+ if (requested >= ceilingMs) {
215
+ log('%s provisionTimeoutMs %dms leaves no room under sessionTimeoutMs %dms (step %dms, reserve %dms); clamping to %dms', role, requested, sessionTimeoutMs, stepTimeoutMs, reserveMs, ceilingMs);
216
+ return ceilingMs;
217
+ }
218
+ return requested;
219
+ }
220
+ /**
221
+ * Split a resolved provisioning budget into the WORK budget and the trailing settle grace.
222
+ *
223
+ * The grace is carved OUT of the budget (see {@link PROVISION_SETTLE_GRACE_MS}), and capped at
224
+ * half of it so a small configured budget still spends at least half its time doing work.
225
+ */
226
+ function splitProvisionBudget(provisionTimeoutMs) {
227
+ const graceMs = Math.min(PROVISION_SETTLE_GRACE_MS, Math.floor(provisionTimeoutMs / 2));
228
+ return { workMs: provisionTimeoutMs - graceMs, graceMs };
229
+ }
117
230
  /**
118
231
  * Responder side of the native formation protocol. Registers a libp2p handler and
119
232
  * drives each inbound stream through token + disclosure validation, provisioning,
@@ -128,6 +241,9 @@ export class FormationListener {
128
241
  this.options = options;
129
242
  this.sessionTimeoutMs = options.sessionTimeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;
130
243
  this.stepTimeoutMs = options.stepTimeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
244
+ const split = splitProvisionBudget(resolveProvisionTimeoutMs(options.provisionTimeoutMs, DEFAULT_PROVISION_TIMEOUT_MS, this.sessionTimeoutMs, this.stepTimeoutMs, 'FormationListener', PROVISION_RESPONSE_TRAVEL_MARGIN_MS));
245
+ this.provisionWorkMs = split.workMs;
246
+ this.provisionGraceMs = split.graceMs;
131
247
  this.maxConcurrentSessions = options.maxConcurrentSessions ?? DEFAULT_MAX_CONCURRENT_SESSIONS;
132
248
  }
133
249
  /** Number of in-flight inbound sessions. */
@@ -181,6 +297,107 @@ export class FormationListener {
181
297
  catch { /* ignore */ }
182
298
  }
183
299
  }
300
+ /**
301
+ * Run the provisioning hook under its WORK budget — the configured provisioning budget minus
302
+ * the trailing settle grace ({@link splitProvisionBudget}, {@link PROVISION_SETTLE_GRACE_MS}).
303
+ *
304
+ * When the work budget expires the hook's signal is aborted, then
305
+ * {@link settleWithinGrace} waits out the grace for the aborted work to settle anyway.
306
+ * Returns `undefined` for "timed out" — which the caller turns into a retryable rejection
307
+ * frame — and rethrows every other failure so it reaches the internal-error path.
308
+ *
309
+ * Deliberately not `withDeadline`: that exposes neither the timed-out flag nor the signal
310
+ * outside its `op`, and both are needed here.
311
+ */
312
+ async provision(id, contact) {
313
+ const controller = new AbortController();
314
+ let timedOut = false;
315
+ const pending = this.options.provisionStrand(contact, controller.signal);
316
+ try {
317
+ return await withTimeout(this.provisionWorkMs, `Formation provisioning#${id}`, () => pending, () => { timedOut = true; controller.abort(); });
318
+ }
319
+ catch (err) {
320
+ if (!timedOut)
321
+ throw err;
322
+ log('formation session #%d provisioning work budget expired after %dms; aborted, settling up to %dms', id, this.provisionWorkMs, this.provisionGraceMs);
323
+ return await this.settleWithinGrace(id, pending);
324
+ }
325
+ }
326
+ /**
327
+ * Wait out the settle grace for an ABORTED provisioning, adopting a late success.
328
+ *
329
+ * A hook that observed the abort before issuing its `FormationUsage` insert rejects (with
330
+ * `FormationAbortedError`) and leaves the invite unspent → reported as a timeout. A hook
331
+ * that had already written cannot be un-written (the table is append-only), so if it lands
332
+ * inside the grace its outcome is ADOPTED — the joiner is told the truth (approved, or the
333
+ * hook's own rejection reason) rather than "timed out" over a spent invite.
334
+ *
335
+ * NOTE: one window stays open — a work budget that expires after the `FormationUsage`
336
+ * insert was issued AND a commit that outlasts the grace. The joiner is told 'timed out'
337
+ * while its one-time invite is in fact spent, and since every retry mints a fresh keypair
338
+ * no recovery path can match it. Nothing can un-spend an append-only row, so the late-settle
339
+ * logging below is the observability for it; widen the grace only if it is seen in the wild.
340
+ */
341
+ async settleWithinGrace(id, pending) {
342
+ let stillPending = false;
343
+ try {
344
+ const outcome = await withTimeout(this.provisionGraceMs, `Formation settle#${id}`, () => pending, () => { stillPending = true; });
345
+ log('formation session #%d provisioning settled within its grace: approved=%s — adopting', id, outcome.approved);
346
+ return outcome;
347
+ }
348
+ catch (err) {
349
+ if (!stillPending) {
350
+ log('formation session #%d provisioning failed after its abort: %o', id, err);
351
+ return undefined;
352
+ }
353
+ log('formation session #%d provisioning still pending after its %dms grace', id, this.provisionGraceMs);
354
+ // Log how it eventually settled so a late failure is not swallowed by the two
355
+ // abandoned `withTimeout` calls.
356
+ void pending.then((late) => log('formation session #%d provisioning settled after its grace: approved=%s', id, late.approved), (err2) => log('formation session #%d provisioning failed after its grace: %o', id, err2));
357
+ return undefined;
358
+ }
359
+ }
360
+ /**
361
+ * Joiner-consent pre-check — the legibility twin of the schema's
362
+ * `FormationUsage.PeerConsented` CHECK, exactly as `verifyFormationApproval` is for the
363
+ * approver's vouch. The database constraint stays the authority; this turns a bad consent
364
+ * into a clean 'Invalid joiner consent' rejection instead of an opaque CHECK failure at
365
+ * commit. Runs BEFORE token/disclosure validation: it is the cheapest check (pure local
366
+ * crypto, no DB read) and a rejection discloses nothing.
367
+ *
368
+ * Also pins `partyId` to `peerKey`: an ed25519 peer id is an identity multihash of the
369
+ * very key, and this is the ONLY layer that can check the match — SQL cannot unwrap a
370
+ * multihash, and `partyId` itself is never stored.
371
+ */
372
+ isJoinerConsentValid(id, contact) {
373
+ try {
374
+ requireEd25519PublicKeyB64(contact.peerKey, 'joiner peer key');
375
+ }
376
+ catch {
377
+ // Deliberately not rethrown or echoed: the helper's error message embeds the whole
378
+ // rejected value, and this one arrived from a remote peer (the cap-the-echo case its
379
+ // own NOTE warns about). Log without the raw value instead.
380
+ log('formation session #%d: joiner peer key is not a base64url ed25519 public key', id);
381
+ return false;
382
+ }
383
+ const embedded = ed25519PublicKeyB64FromPeerId(contact.partyId);
384
+ if (!embedded || embedded !== contact.peerKey) {
385
+ log('formation session #%d: partyId does not embed the presented peer key', id);
386
+ return false;
387
+ }
388
+ const consentOk = verifyFormationConsent({
389
+ token: contact.token,
390
+ usageStampId: contact.usageStampId,
391
+ peerKey: contact.peerKey,
392
+ disclosure: canonicalJson(contact.disclosure),
393
+ peerSig: contact.peerSignature
394
+ });
395
+ if (!consentOk) {
396
+ log('formation session #%d: joiner consent signature failed verification', id);
397
+ return false;
398
+ }
399
+ return true;
400
+ }
184
401
  async runSession(id, stream) {
185
402
  // Track whether ANY frame has been written so the catch below can convert an
186
403
  // unexpected internal error into a non-disclosing rejection ONLY when nothing has
@@ -194,9 +411,13 @@ export class FormationListener {
194
411
  const reader = new FrameReader(stream);
195
412
  const contact = await withTimeout(this.stepTimeoutMs, `Formation await-contact#${id}`, () => reader.read());
196
413
  log('formation session #%d contact: token=%s party=%s', id, contact.token, contact.partyId);
414
+ if (!this.isJoinerConsentValid(id, contact)) {
415
+ send({ approved: false, reason: 'Invalid joiner consent' });
416
+ return;
417
+ }
197
418
  const tokenResult = await this.options.validateToken(contact.token);
198
419
  if (!tokenResult.valid) {
199
- send({ approved: false, reason: 'Invalid token' });
420
+ send({ approved: false, reason: INVALID_TOKEN_REASON });
200
421
  return;
201
422
  }
202
423
  const disclosureOk = await this.options.validateDisclosure(contact.token, contact.disclosure);
@@ -204,7 +425,13 @@ export class FormationListener {
204
425
  send({ approved: false, reason: 'Invalid disclosure' });
205
426
  return;
206
427
  }
207
- const outcome = await this.options.provisionStrand(contact.token, contact.partyId, contact.disclosure);
428
+ const outcome = await this.provision(id, contact);
429
+ if (!outcome) {
430
+ // Reported as its own retryable reason rather than falling through to the generic
431
+ // catch below, which would report the misleading 'Internal formation error'.
432
+ send({ approved: false, reason: 'Formation provisioning timed out' });
433
+ return;
434
+ }
208
435
  if (!outcome.approved) {
209
436
  // A post-validation rejection still discloses NEITHER identity NOR cadre,
210
437
  // exactly like the token/disclosure rejections above.
@@ -250,6 +477,7 @@ export async function dialFormation(node, options) {
250
477
  const protocolId = options.protocolId ?? FORMATION_PROTOCOL;
251
478
  const sessionTimeoutMs = options.sessionTimeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;
252
479
  const stepTimeoutMs = options.stepTimeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
480
+ const provisionTimeoutMs = resolveProvisionTimeoutMs(options.provisionTimeoutMs, DEFAULT_INITIATOR_PROVISION_TIMEOUT_MS, sessionTimeoutMs, stepTimeoutMs, 'dialFormation');
253
481
  const addr = multiaddr(options.responderAddrs[0]);
254
482
  return withTimeout(sessionTimeoutMs, 'Formation dial', async () => {
255
483
  const rawStream = await withTimeout(stepTimeoutMs, 'Formation dial-connect', async () => node.dialProtocol(addr, protocolId));
@@ -257,7 +485,7 @@ export async function dialFormation(node, options) {
257
485
  try {
258
486
  const reader = new FrameReader(stream);
259
487
  writeFrame(stream, options.contact);
260
- const response = await withTimeout(stepTimeoutMs, 'Formation await-response', () => reader.read());
488
+ const response = await withTimeout(provisionTimeoutMs, 'Formation await-response', () => reader.read());
261
489
  if (!response.approved) {
262
490
  throw new Error(`Formation rejected: ${response.reason ?? 'no reason provided'}`);
263
491
  }
@@ -1 +1 @@
1
- {"version":3,"file":"strand-formation-protocol.js","sourceRoot":"","sources":["../src/strand-formation-protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strand-formation-protocol.js","sourceRoot":"","sources":["../src/strand-formation-protocol.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { PrivateKey } from '@libp2p/interface';
2
+ import { type StrandBackfillConfig } from './strand-backfill.js';
2
3
  import type { StrandInstance, StrandRow, StorageConfig, NetworkConfig, LatencyHint, NodeProfile, SAppConfig, StrandMode } from './types.js';
3
4
  /**
4
5
  * Configuration for starting a strand instance
@@ -33,6 +34,25 @@ export interface StartStrandConfig {
33
34
  * {@link StrandConfig.founder}.
34
35
  */
35
36
  founder?: boolean;
37
+ /**
38
+ * Number of nodes Optimystic is told this strand's replication cluster should
39
+ * have. Same rule as {@link CadreNodeConfig.strandClusterSize}, which CadreNode
40
+ * forwards here: every node on the strand should use the same value, and it is
41
+ * frozen when the strand's libp2p node is created. Defaults to
42
+ * `DEFAULT_STRAND_CLUSTER_SIZE` (4); values below `MIN_CLUSTER_SIZE` (2) are
43
+ * rejected by `resolveStrandClusterSize`. Strand-only — the
44
+ * control network's breadth is the fixed `CONTROL_REPLICATION_BREADTH`.
45
+ */
46
+ clusterSize?: number;
47
+ /**
48
+ * Tuning for the strand peer-join block catch-up ({@link StrandBackfill}),
49
+ * forwarded from {@link CadreNodeConfig.strandBackfill}. When the strand's
50
+ * libp2p node connects to a peer this runtime has not yet caught up, every
51
+ * block in the strand's own raw store is pushed to it. Runs only on
52
+ * `networked` strands with per-strand storage; `{ enabled: false }` restores
53
+ * the pre-existing no-backfill behaviour.
54
+ */
55
+ backfill?: StrandBackfillConfig;
36
56
  }
37
57
  /**
38
58
  * Volatile inputs re-resolved when resuming a quiesced strand. These may have
@@ -72,6 +92,15 @@ export declare class StrandInstanceManager {
72
92
  * without the caller re-threading storage/network/profile/key/sApp config.
73
93
  */
74
94
  private launchConfigs;
95
+ /**
96
+ * The per-strand peer-join block catch-up, keyed by strand id. Private (not on
97
+ * the public {@link StrandInstance}) because it is runtime plumbing with the
98
+ * same lifetime as the strand's libp2p node: created in `buildStrandRuntime`,
99
+ * stopped and dropped in `releaseRuntime` — so quiesce → resume rebuilds it
100
+ * with a fresh caught-up-peer memo, which is intended (a resumed node may have
101
+ * missed writes).
102
+ */
103
+ private backfills;
75
104
  private stopping;
76
105
  constructor();
77
106
  /**
@@ -87,7 +116,16 @@ export declare class StrandInstanceManager {
87
116
  */
88
117
  hasStrand(strandId: string): boolean;
89
118
  /**
90
- * Start a new strand instance
119
+ * Start a new strand instance.
120
+ *
121
+ * A failed launch leaves NOTHING tracked: the instance and its retained launch
122
+ * config are both dropped before the error is rethrown, so the strand id is
123
+ * free for a genuine retry. This matches the pre-registration failure path
124
+ * (a rejected schema signature, which throws before anything is recorded) —
125
+ * both failure modes of this call leave the same residue: none. Callers learn
126
+ * of the failure from the rejected promise (and, on the control-discovered
127
+ * path, from CadreNode's `strand:error` event), not from an error record left
128
+ * behind in `instances`.
91
129
  */
92
130
  startStrand(config: StartStrandConfig): Promise<StrandInstance>;
93
131
  /**
@@ -1,7 +1,10 @@
1
1
  import debug from 'debug';
2
2
  import { createLibp2pNode } from '@optimystic/db-p2p';
3
3
  import { StrandDatabase } from './strand-database.js';
4
+ import { StrandBackfill } from './strand-backfill.js';
4
5
  import { assertSchemaSignature } from './schema-verification.js';
6
+ import { resolveStrandClusterSize, STRAND_CLUSTER_POLICY } from './types.js';
7
+ import { resolveListenAddrs } from './relay-addrs.js';
5
8
  const log = debug('sereus:cadre:strand-manager');
6
9
  const timing = debug('sereus:cadre:timing');
7
10
  /**
@@ -61,6 +64,15 @@ export class StrandInstanceManager {
61
64
  * without the caller re-threading storage/network/profile/key/sApp config.
62
65
  */
63
66
  this.launchConfigs = new Map();
67
+ /**
68
+ * The per-strand peer-join block catch-up, keyed by strand id. Private (not on
69
+ * the public {@link StrandInstance}) because it is runtime plumbing with the
70
+ * same lifetime as the strand's libp2p node: created in `buildStrandRuntime`,
71
+ * stopped and dropped in `releaseRuntime` — so quiesce → resume rebuilds it
72
+ * with a fresh caught-up-peer memo, which is intended (a resumed node may have
73
+ * missed writes).
74
+ */
75
+ this.backfills = new Map();
64
76
  this.stopping = false;
65
77
  log('StrandInstanceManager created');
66
78
  }
@@ -83,7 +95,16 @@ export class StrandInstanceManager {
83
95
  return this.instances.has(strandId);
84
96
  }
85
97
  /**
86
- * Start a new strand instance
98
+ * Start a new strand instance.
99
+ *
100
+ * A failed launch leaves NOTHING tracked: the instance and its retained launch
101
+ * config are both dropped before the error is rethrown, so the strand id is
102
+ * free for a genuine retry. This matches the pre-registration failure path
103
+ * (a rejected schema signature, which throws before anything is recorded) —
104
+ * both failure modes of this call leave the same residue: none. Callers learn
105
+ * of the failure from the rejected promise (and, on the control-discovered
106
+ * path, from CadreNode's `strand:error` event), not from an error record left
107
+ * behind in `instances`.
87
108
  */
88
109
  async startStrand(config) {
89
110
  const { strandRow, sAppConfig } = config;
@@ -117,7 +138,10 @@ export class StrandInstanceManager {
117
138
  memberPrivateKey: strandRow.MemberPrivateKey ?? undefined,
118
139
  connectedPeers: 0,
119
140
  lastActivity: new Date(),
120
- latencyHint
141
+ latencyHint,
142
+ // Seed only — buildStrandRuntime overwrites this with the same resolution,
143
+ // so an instance that errors mid-build still reads sensibly.
144
+ mode: config.mode ?? 'networked'
121
145
  };
122
146
  this.instances.set(strandId, instance);
123
147
  this.launchConfigs.set(strandId, config);
@@ -128,9 +152,15 @@ export class StrandInstanceManager {
128
152
  return instance;
129
153
  }
130
154
  catch (error) {
155
+ // Status/error first — the (now discarded) record is still what `log` reports on.
131
156
  instance.status = 'error';
132
157
  instance.error = error instanceof Error ? error.message : String(error);
133
158
  log('Failed to start strand %s: %s', strandId, instance.error);
159
+ // Drop the dead record so this strand id can be launched again. Keep the
160
+ // `launchConfigs` has an entry iff `instances` does invariant — resumeStrand
161
+ // reads both, and a config without an instance would strand the config.
162
+ this.instances.delete(strandId);
163
+ this.launchConfigs.delete(strandId);
134
164
  throw error;
135
165
  }
136
166
  }
@@ -145,6 +175,7 @@ export class StrandInstanceManager {
145
175
  async buildStrandRuntime(instance, config) {
146
176
  const strandId = instance.strandId;
147
177
  const { sAppConfig } = config;
178
+ const mode = config.mode ?? 'networked';
148
179
  // Resolve storage for this strand. If a factory function is provided, it is
149
180
  // called with the strandId to create strand-specific storage (e.g.,
150
181
  // strand-isolated directories).
@@ -152,39 +183,56 @@ export class StrandInstanceManager {
152
183
  if (strandStorage) {
153
184
  log('Strand %s using provided storage provider', strandId);
154
185
  }
155
- // Note: protocolPrefix would be used by libp2p services, but createLibp2pNode
156
- // may not support it yet - tracked for future implementation.
157
- const _protocolPrefix = `/sereus/strand/${strandId}`;
186
+ // db-p2p namespaces every one of the node's protocol ids by network name
187
+ // (`/optimystic/<networkName>/...`), so anything dialing this node's own
188
+ // services must derive its prefix from the SAME string the node was built
189
+ // with — hence one binding for both, not two literals that can drift.
190
+ const networkName = `strand-${strandId}`;
191
+ const protocolPrefix = `/optimystic/${networkName}`;
158
192
  // Determine relay mode: if explicitly set in config, use that;
159
193
  // otherwise default to true for storage profile nodes.
160
194
  const enableRelay = config.network?.enableRelay ?? (config.profile === 'storage');
195
+ const listenAddrs = resolveListenAddrs(config.network);
161
196
  try {
162
197
  let t0 = performance.now();
163
198
  const node = await createLibp2pNode({
164
199
  port: 0, // Random port
165
200
  bootstrapNodes: config.bootstrapNodes ?? [],
166
- networkName: `strand-${strandId}`,
201
+ networkName,
167
202
  storage: strandStorage,
168
203
  fretProfile: config.profile === 'storage' ? 'core' : 'edge',
169
204
  relay: enableRelay,
170
- clusterSize: 3,
171
- clusterPolicy: {
172
- allowDownsize: true,
173
- sizeTolerance: 0.5
174
- },
205
+ clusterSize: resolveStrandClusterSize(config.clusterSize),
206
+ // Deliberately NOT CONTROL_CLUSTER_POLICY: a strand is application data with its own
207
+ // breadth reasoning, and the shape match with the control policy is a coincidence.
208
+ clusterPolicy: STRAND_CLUSTER_POLICY,
175
209
  arachnode: {
176
210
  enableRingZulu: config.profile === 'storage'
177
211
  },
178
212
  ...(config.privateKey && { privateKey: config.privateKey }),
179
213
  ...(config.network?.transports && { transports: config.network.transports }),
180
- ...(config.network?.listenAddrs && { listenAddrs: config.network.listenAddrs }),
214
+ // NOTE: strand nodes receive the same RESOLVED listen addrs as the
215
+ // control node. A fixed-port listen addr (e.g. cadre-cli's example
216
+ // `/ip4/0.0.0.0/tcp/4001`) would have control + strand nodes racing to
217
+ // bind one port — EADDRINUSE. Unverified; if a deployment configures a
218
+ // fixed port and strands fail to start, rewrite the port per node here.
219
+ // An inherited `/p2p-circuit` addr, by contrast, is deliberate — it is
220
+ // what gives a NAT'd strand node a reachable relay slot — and works
221
+ // because the launch path announces this strand's derived peerId to
222
+ // the relay first (delegate admission; see cadre-node.ts). Those circuit
223
+ // entries come either from a hand-written `network.listenAddrs` or from
224
+ // `network.relayAddrs`, which `resolveListenAddrs` folds into the same list.
225
+ ...(listenAddrs && { listenAddrs }),
181
226
  ...(config.network?.connectionGater && { connectionGater: config.network.connectionGater })
182
227
  });
183
228
  timing('[buildStrandRuntime:%s] createLibp2pNode: %dms', strandId, Math.round(performance.now() - t0));
184
229
  instance.libp2pNode = node;
230
+ // Assigned on every runtime (re)build — resume re-enters here with
231
+ // resumeConfig, so a bootstrap → networked shift refreshes the field.
232
+ instance.mode = mode;
185
233
  // Create and initialize the StrandDatabase. In bootstrap mode the same
186
234
  // strandStorage instance also backs the optimystic local transactor so
187
- // DML lands on the host's persistent storage (e.g. MMKV on RN). Sharing
235
+ // DML lands on the host's persistent storage (e.g. LevelDB on RN). Sharing
188
236
  // the instance — not creating a second one over the same id+prefix —
189
237
  // avoids cache divergence between the libp2p and database paths.
190
238
  //
@@ -196,7 +244,7 @@ export class StrandInstanceManager {
196
244
  sAppConfig,
197
245
  libp2pNode: node,
198
246
  coordinatedRepo: node.coordinatedRepo,
199
- mode: config.mode ?? 'networked',
247
+ mode,
200
248
  rawStorage: strandStorage,
201
249
  // Founder bootstrap inputs: the strand's type drives which membership rows
202
250
  // are written, and the closed-strand MemberPrivateKey derives the founding
@@ -208,6 +256,28 @@ export class StrandInstanceManager {
208
256
  instance.database = strandDb;
209
257
  await strandDb.initialize();
210
258
  timing('[buildStrandRuntime:%s] strandDatabase.initialize: %dms', strandId, Math.round(performance.now() - t0));
259
+ // Peer-join block catch-up: push this strand's own blocks to each newly
260
+ // connected peer, so a machine that joined after blocks were committed
261
+ // still ends up physically holding them (a bootstrap strand has no mesh
262
+ // to catch up, and without per-strand storage there is nothing to copy).
263
+ if (mode === 'networked' && strandStorage && config.backfill?.enabled !== false) {
264
+ if (node.keyNetwork) {
265
+ const backfill = new StrandBackfill({
266
+ strandId,
267
+ libp2p: node,
268
+ peerNetwork: node.keyNetwork,
269
+ storage: strandStorage,
270
+ // The same prefix the receiver registered its block-transfer handler
271
+ // under — derived from networkName above, never re-spelled here.
272
+ protocolPrefix
273
+ }, config.backfill);
274
+ backfill.start();
275
+ this.backfills.set(strandId, backfill);
276
+ }
277
+ else {
278
+ log('Strand %s: libp2p node exposes no keyNetwork; peer-join block catch-up is inert', strandId);
279
+ }
280
+ }
211
281
  instance.status = 'active';
212
282
  instance.lastActivity = new Date();
213
283
  }
@@ -230,6 +300,14 @@ export class StrandInstanceManager {
230
300
  * Shared by `quiesceStrand`, `stopStrand`, and that rollback path.
231
301
  */
232
302
  async releaseRuntime(instance) {
303
+ // Backfill first — before the database closes and the libp2p node stops — so
304
+ // no NEW catch-up push is issued against a torn-down transport. A push already
305
+ // in flight is not awaited; it fails into the module's own per-chunk catch.
306
+ const backfill = this.backfills.get(instance.strandId);
307
+ if (backfill) {
308
+ backfill.stop();
309
+ this.backfills.delete(instance.strandId);
310
+ }
233
311
  if (instance.database) {
234
312
  await instance.database.close();
235
313
  instance.database = undefined;