@serfab/cadre-core 1.7.0 → 1.9.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 (188) hide show
  1. package/README.md +8 -6
  2. package/dist/cadre-node.d.ts +527 -206
  3. package/dist/cadre-node.js +1084 -528
  4. package/dist/cadre-node.js.map +1 -1
  5. package/dist/control-authorization.d.ts +2 -2
  6. package/dist/control-authorization.js +1 -0
  7. package/dist/control-authorization.js.map +1 -1
  8. package/dist/control-cohort.d.ts +6 -0
  9. package/dist/control-cohort.js +6 -0
  10. package/dist/control-cohort.js.map +1 -1
  11. package/dist/control-database.d.ts +46 -8
  12. package/dist/control-database.js +97 -18
  13. package/dist/control-database.js.map +1 -1
  14. package/dist/control-formation-recorder.d.ts +37 -14
  15. package/dist/control-formation-recorder.js +60 -10
  16. package/dist/control-formation-recorder.js.map +1 -1
  17. package/dist/control-read-retry.d.ts +13 -12
  18. package/dist/control-read-retry.js +14 -12
  19. package/dist/control-read-retry.js.map +1 -1
  20. package/dist/control-schema.d.ts +1 -1
  21. package/dist/control-schema.js +67 -5
  22. package/dist/control-schema.js.map +1 -1
  23. package/dist/control-stream.d.ts +9 -1
  24. package/dist/control-stream.js +24 -1
  25. package/dist/control-stream.js.map +1 -1
  26. package/dist/control-write-retry.d.ts +12 -2
  27. package/dist/control-write-retry.js +13 -2
  28. package/dist/control-write-retry.js.map +1 -1
  29. package/dist/delegate-admission.d.ts +11 -8
  30. package/dist/delegate-admission.js +19 -11
  31. package/dist/delegate-admission.js.map +1 -1
  32. package/dist/diagnostics/connection-path.d.ts +0 -7
  33. package/dist/diagnostics/connection-path.js +0 -7
  34. package/dist/diagnostics/connection-path.js.map +1 -1
  35. package/dist/formation-approval.d.ts +7 -0
  36. package/dist/formation-approval.js +9 -3
  37. package/dist/formation-approval.js.map +1 -1
  38. package/dist/fs-atomic.d.ts +11 -2
  39. package/dist/fs-atomic.js +13 -3
  40. package/dist/fs-atomic.js.map +1 -1
  41. package/dist/hibernation-manager.d.ts +111 -8
  42. package/dist/hibernation-manager.js +234 -49
  43. package/dist/hibernation-manager.js.map +1 -1
  44. package/dist/index.d.ts +10 -10
  45. package/dist/index.js +25 -20
  46. package/dist/index.js.map +1 -1
  47. package/dist/joined-strand-store.d.ts +90 -25
  48. package/dist/joined-strand-store.js +223 -55
  49. package/dist/joined-strand-store.js.map +1 -1
  50. package/dist/key-store-file.js +14 -5
  51. package/dist/key-store-file.js.map +1 -1
  52. package/dist/link-budget.d.ts +367 -69
  53. package/dist/link-budget.js +375 -72
  54. package/dist/link-budget.js.map +1 -1
  55. package/dist/membership-connection-gater.d.ts +30 -43
  56. package/dist/membership-connection-gater.js +31 -42
  57. package/dist/membership-connection-gater.js.map +1 -1
  58. package/dist/node-local-snapshot.d.ts +6 -4
  59. package/dist/node-local-snapshot.js +7 -3
  60. package/dist/node-local-snapshot.js.map +1 -1
  61. package/dist/peer-dial.d.ts +24 -4
  62. package/dist/peer-dial.js +74 -5
  63. package/dist/peer-dial.js.map +1 -1
  64. package/dist/peer-join-backfill.d.ts +2 -10
  65. package/dist/peer-join-backfill.js +8 -8
  66. package/dist/peer-join-backfill.js.map +1 -1
  67. package/dist/push-fanout.d.ts +6 -0
  68. package/dist/push-fanout.js +6 -0
  69. package/dist/push-fanout.js.map +1 -1
  70. package/dist/relay-reservation.d.ts +3 -1
  71. package/dist/relay-reservation.js +3 -1
  72. package/dist/relay-reservation.js.map +1 -1
  73. package/dist/relay-server.d.ts +79 -0
  74. package/dist/relay-server.js +74 -0
  75. package/dist/relay-server.js.map +1 -0
  76. package/dist/seed-bootstrap.d.ts +57 -4
  77. package/dist/seed-bootstrap.js +107 -83
  78. package/dist/seed-bootstrap.js.map +1 -1
  79. package/dist/storage-scope.d.ts +33 -17
  80. package/dist/storage-scope.js +44 -23
  81. package/dist/storage-scope.js.map +1 -1
  82. package/dist/strand-addr-protocol.d.ts +58 -21
  83. package/dist/strand-addr-protocol.js +133 -85
  84. package/dist/strand-addr-protocol.js.map +1 -1
  85. package/dist/strand-database.d.ts +20 -2
  86. package/dist/strand-database.js +42 -5
  87. package/dist/strand-database.js.map +1 -1
  88. package/dist/strand-first-sync-gate.d.ts +59 -21
  89. package/dist/strand-first-sync-gate.js +58 -19
  90. package/dist/strand-first-sync-gate.js.map +1 -1
  91. package/dist/strand-formation-deadlines.d.ts +151 -0
  92. package/dist/strand-formation-deadlines.js +171 -0
  93. package/dist/strand-formation-deadlines.js.map +1 -0
  94. package/dist/strand-formation-manager.d.ts +63 -30
  95. package/dist/strand-formation-manager.js +67 -41
  96. package/dist/strand-formation-manager.js.map +1 -1
  97. package/dist/strand-formation-protocol.d.ts +45 -46
  98. package/dist/strand-formation-protocol.js +62 -126
  99. package/dist/strand-formation-protocol.js.map +1 -1
  100. package/dist/strand-fret-addrs.d.ts +54 -0
  101. package/dist/strand-fret-addrs.js +108 -0
  102. package/dist/strand-fret-addrs.js.map +1 -0
  103. package/dist/strand-instance-manager.d.ts +122 -44
  104. package/dist/strand-instance-manager.js +270 -114
  105. package/dist/strand-instance-manager.js.map +1 -1
  106. package/dist/strand-membership-writer.d.ts +6 -0
  107. package/dist/strand-membership-writer.js +11 -0
  108. package/dist/strand-membership-writer.js.map +1 -1
  109. package/dist/strand-network-state-file.d.ts +12 -0
  110. package/dist/strand-network-state-file.js +38 -0
  111. package/dist/strand-network-state-file.js.map +1 -0
  112. package/dist/strand-network-state.d.ts +111 -0
  113. package/dist/strand-network-state.js +191 -0
  114. package/dist/strand-network-state.js.map +1 -0
  115. package/dist/strand-revocation-enforcer.js +2 -1
  116. package/dist/strand-revocation-enforcer.js.map +1 -1
  117. package/dist/strand-solicitation.d.ts +36 -20
  118. package/dist/strand-solicitation.js +4 -4
  119. package/dist/strand-solicitation.js.map +1 -1
  120. package/dist/strand-wake-protocol.d.ts +80 -23
  121. package/dist/strand-wake-protocol.js +89 -64
  122. package/dist/strand-wake-protocol.js.map +1 -1
  123. package/dist/types.d.ts +148 -61
  124. package/dist/types.js +4 -3
  125. package/dist/types.js.map +1 -1
  126. package/package.json +11 -10
  127. package/src/cadre-node.ts +8400 -7776
  128. package/src/control-authorization.ts +2 -1
  129. package/src/control-cohort.ts +6 -0
  130. package/src/control-database.ts +122 -18
  131. package/src/control-formation-recorder.ts +354 -305
  132. package/src/control-read-retry.ts +14 -12
  133. package/src/control-schema.ts +67 -5
  134. package/src/control-stream.ts +25 -1
  135. package/src/control-write-retry.ts +13 -2
  136. package/src/delegate-admission.ts +23 -13
  137. package/src/diagnostics/connection-path.ts +0 -7
  138. package/src/formation-approval.ts +610 -604
  139. package/src/fs-atomic.ts +14 -4
  140. package/src/hibernation-manager.ts +638 -437
  141. package/src/index.ts +43 -65
  142. package/src/joined-strand-store.ts +247 -57
  143. package/src/key-store-file.ts +14 -5
  144. package/src/link-budget.ts +393 -74
  145. package/src/membership-connection-gater.ts +31 -45
  146. package/src/node-local-snapshot.ts +12 -7
  147. package/src/peer-dial.ts +81 -7
  148. package/src/peer-join-backfill.ts +10 -9
  149. package/src/push-fanout.ts +6 -0
  150. package/src/relay-reservation.ts +3 -1
  151. package/src/relay-server.ts +98 -0
  152. package/src/seed-bootstrap.ts +133 -91
  153. package/src/storage-scope.ts +44 -23
  154. package/src/strand-addr-protocol.ts +547 -458
  155. package/src/strand-database.ts +287 -242
  156. package/src/strand-first-sync-gate.ts +58 -19
  157. package/src/strand-formation-deadlines.ts +227 -0
  158. package/src/strand-formation-manager.ts +700 -647
  159. package/src/strand-formation-protocol.ts +106 -154
  160. package/src/strand-fret-addrs.ts +145 -0
  161. package/src/strand-instance-manager.ts +307 -140
  162. package/src/strand-membership-writer.ts +12 -0
  163. package/src/strand-network-state-file.ts +40 -0
  164. package/src/strand-network-state.ts +243 -0
  165. package/src/strand-revocation-enforcer.ts +2 -1
  166. package/src/strand-solicitation.ts +519 -500
  167. package/src/strand-wake-protocol.ts +401 -355
  168. package/src/types.ts +150 -61
  169. package/dist/strand-peer-book-file.d.ts +0 -12
  170. package/dist/strand-peer-book-file.js +0 -37
  171. package/dist/strand-peer-book-file.js.map +0 -1
  172. package/dist/strand-peer-book-protocol.d.ts +0 -159
  173. package/dist/strand-peer-book-protocol.js +0 -360
  174. package/dist/strand-peer-book-protocol.js.map +0 -1
  175. package/dist/strand-peer-book-swap.d.ts +0 -104
  176. package/dist/strand-peer-book-swap.js +0 -284
  177. package/dist/strand-peer-book-swap.js.map +0 -1
  178. package/dist/strand-peer-book.d.ts +0 -159
  179. package/dist/strand-peer-book.js +0 -354
  180. package/dist/strand-peer-book.js.map +0 -1
  181. package/dist/strand-peer-observer.d.ts +0 -88
  182. package/dist/strand-peer-observer.js +0 -180
  183. package/dist/strand-peer-observer.js.map +0 -1
  184. package/src/strand-peer-book-file.ts +0 -39
  185. package/src/strand-peer-book-protocol.ts +0 -463
  186. package/src/strand-peer-book-swap.ts +0 -338
  187. package/src/strand-peer-book.ts +0 -468
  188. package/src/strand-peer-observer.ts +0 -235
@@ -1,6 +1,6 @@
1
1
  import debug from 'debug';
2
2
  import { Database } from '@quereus/quereus';
3
- import { connectToStrand } from '@serfab/quereus-plugin-sereus';
3
+ import { applyAppSchema, connectToStrand } from '@serfab/quereus-plugin-sereus';
4
4
  import { bootstrapFounderMembership } from './strand-membership-writer.js';
5
5
  import { strandMemberKeyPair } from './strand-member-key.js';
6
6
  const log = debug('sereus:cadre:strand-db');
@@ -38,7 +38,13 @@ export class StrandDatabase {
38
38
  return;
39
39
  }
40
40
  const sid = this.config.strandId;
41
- log('Initializing StrandDatabase for strand: %s (sApp: %s v%s)', sid, this.config.sAppConfig.id, this.config.sAppConfig.version);
41
+ const { sAppConfig } = this.config;
42
+ if (this.config.founder === true) {
43
+ // Before composing anything: a founder that cannot write its Header must not
44
+ // leave a half-built connection behind.
45
+ this.founderSApp();
46
+ }
47
+ log('Initializing StrandDatabase for strand: %s (%s)', sid, sAppConfig ? `sApp: ${sAppConfig.id} v${sAppConfig.version}` : 'storage replica');
42
48
  this.db = new Database();
43
49
  // Delegate to the shared composition. The node is injected, so:
44
50
  // - `connectToStrand` never creates a node (its `createdNode` stays null);
@@ -50,9 +56,14 @@ export class StrandDatabase {
50
56
  // and cadre-core always injects the node and runs the network transactor.
51
57
  // (The plugin keeps its `storage` option for the browser entry point.)
52
58
  const t0 = performance.now();
59
+ //
60
+ // A storage replica passes no schema, so no `App` tables are declared. A warm restart's
61
+ // hydrate may still load `App` table definitions persisted in the replicated store into
62
+ // the catalog — declarative metadata, nothing the app supplied runs — and the first-sync
63
+ // probe then reads those tables once from blocks this node holds.
53
64
  const result = await connectToStrand(this.db, {
54
65
  strandId: sid,
55
- schema: this.config.sAppConfig.schema,
66
+ schema: sAppConfig?.schema,
56
67
  libp2pNode: this.config.libp2pNode,
57
68
  coordinatedRepo: this.config.coordinatedRepo,
58
69
  enableCache: true,
@@ -83,18 +94,31 @@ export class StrandDatabase {
83
94
  * no keypair (Header only). Idempotent — see {@link bootstrapFounderMembership}.
84
95
  */
85
96
  async bootstrapFounder() {
86
- const { strandId, strandType, memberPrivateKey, partyMemberPrivateKey, sAppConfig } = this.config;
97
+ const { strandId, strandType, memberPrivateKey, partyMemberPrivateKey } = this.config;
87
98
  const closed = strandType === 'c';
88
99
  await bootstrapFounderMembership(this.db, {
89
100
  strandId,
90
101
  type: strandType,
91
- sApp: sAppConfig,
102
+ sApp: this.founderSApp(),
92
103
  founderKeyPair: closed ? this.deriveFounderKeyPair(strandId, partyMemberPrivateKey) : undefined,
93
104
  sharedMemberPublicKey: closed && memberPrivateKey
94
105
  ? strandMemberKeyPair(memberPrivateKey).publicKeyB64
95
106
  : undefined,
96
107
  });
97
108
  }
109
+ /**
110
+ * The sApp a founder bootstrap records in the `Strand.Header`. Throws for a storage
111
+ * replica (no sApp config): its Header's sApp columns would have nothing to say.
112
+ * `CadreNode` always launches a replica as a joiner, so this guards a future caller.
113
+ */
114
+ founderSApp() {
115
+ const { sAppConfig, strandId } = this.config;
116
+ if (!sAppConfig) {
117
+ throw new Error(`Cannot found strand ${strandId} as a storage replica: the founder bootstrap writes ` +
118
+ 'the sApp id and version into Strand.Header, and a replica has no sApp config.');
119
+ }
120
+ return sAppConfig;
121
+ }
98
122
  /**
99
123
  * Derive the founding keypair for a closed strand from the party's own
100
124
  * `partyMemberPrivateKey`, failing loudly when the key is absent (a closed strand
@@ -133,6 +157,19 @@ export class StrandDatabase {
133
157
  await this.bootstrapFounder();
134
158
  this.config.founder = true;
135
159
  }
160
+ /**
161
+ * Give a live storage replica the app's schema: apply `App` to the already-composed
162
+ * database — same libp2p node, same store, so the new tables read the blocks this node
163
+ * already holds — and record the sApp, so a later {@link ensureFounderBootstrap} has it
164
+ * for the `Header`. Idempotent: the apply is a declarative diff, so re-applying the same
165
+ * schema emits nothing. A failed apply records nothing.
166
+ */
167
+ async attachAppSchema(sAppConfig) {
168
+ this.ensureInitialized();
169
+ await applyAppSchema(this.db, sAppConfig.schema);
170
+ this.config.sAppConfig = sAppConfig;
171
+ log('StrandDatabase for strand %s attached sApp %s v%s', this.config.strandId, sAppConfig.id, sAppConfig.version);
172
+ }
136
173
  /**
137
174
  * Get the underlying database for queries
138
175
  */
@@ -1 +1 @@
1
- {"version":3,"file":"strand-database.js","sourceRoot":"","sources":["../src/strand-database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strand-database.js","sourceRoot":"","sources":["../src/strand-database.ts"],"names":[],"mappings":"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"}
@@ -78,24 +78,57 @@ export declare const DEFAULT_STRAND_FIRST_SYNC_POLL_MS = 500;
78
78
  *
79
79
  * Every harness figure below was taken on one shape: one Windows developer machine, two
80
80
  * relay-only `CadreNode`s (`listenAddrs: []`) on a shared loopback dedicated relay, with a
81
- * 900 ms one-way per-frame outbound delay applied to every websocket by
81
+ * one-way per-frame outbound delay applied to every websocket by
82
82
  * `integration-tests/src/harness/ws-latency.ts` in `pipelined` mode — frames stay overlapped
83
83
  * in flight, so the figure is latency and bandwidth stays unlimited; the harness's other mode
84
84
  * (`serial`) is a per-socket frame-rate cap and its delays are NOT comparable
85
- * (`docs/testing.md` → "Link latency"). A round trip therefore costs about 1.8 s. Times run
86
- * from the `addStrand` call to writable.
87
- *
88
- * FRESH JOIN (2026-09-26), a machine holding nothing of the strand yet: 23, 27, 31 and 41 s
89
- * over four runs at optimystic's 1000 ms cohort read deadline, and 35, 42 and 46 s over three
90
- * runs at a 5000 ms one; the re-attach scenario below later took 38.7, 38.7 and 56.9 s for
91
- * the same fresh join. **The 5000 ms band, 35-57 s, is the one in force**: sereus declares
92
- * 5000 ms (`COHORT_READ_DEADLINE_MS`, `quereus-plugin-sereus/src/cluster-size.ts`), because
93
- * 1000 ms is shorter than one round trip on that link and leaves every cohort read with no
94
- * answer to corroborate against. Widening that deadline makes a consult against a peer that
95
- * cannot answer cost longer, and this phase runs several of those — which is why both bands
96
- * are recorded here, and why a change to either number has to be weighed against the other.
97
- *
98
- * RE-ATTACH (2026-09-26, the opt-in
85
+ * (`docs/testing.md` → "Link latency"). Two delays were used: 900 ms one-way, a round trip
86
+ * near 1.8 s, the band sereus supported first; and 1 500 ms one-way, the supported 3-second
87
+ * round trip. Times run from the `addStrand` call to writable.
88
+ *
89
+ * FRESH JOIN at 900 ms (2026-09-26), a machine holding nothing of the strand yet: 23, 27, 31
90
+ * and 41 s over four runs at optimystic's 1000 ms cohort read deadline, and 35, 42 and 46 s
91
+ * over three runs at a 5000 ms one; the re-attach scenario below later took 38.7, 38.7 and
92
+ * 56.9 s for the same fresh join. At that delay the 5000 ms band, 35-57 s, was the one in
93
+ * force: sereus then declared 5000 ms, because 1000 ms is shorter than one round trip on
94
+ * that link and leaves every cohort read with no answer to corroborate against. Widening that
95
+ * deadline makes a consult against a peer that cannot answer cost longer, and this phase runs
96
+ * several of those — which is why both bands are recorded here, and why a change to either
97
+ * number has to be weighed against the other.
98
+ *
99
+ * AT THE SUPPORTED LINK ON `@optimystic/*` 1.8.0 (2026-09-29), 1 500 ms one-way, the read
100
+ * deadline derived from the declared link at 7 000 ms (`cohortReadDeadlineMs` in
101
+ * `link-budget.ts`; the plugin's `COHORT_READ_DEADLINE_MS` is the same number), and cadre
102
+ * stating its declared link to Optimystic, the same scenario, all under the coordinator debug
103
+ * channel:
104
+ * - Fresh join: writable at 63.9 and 63.8 s over two runs, the row A wrote before the join
105
+ * readable at 88.0 s in both. B's strand node connected to A's at 18.3-18.4 s. No read was
106
+ * declined: every consult on B completed, 6.03-6.05 s after the one before it (two link
107
+ * round trips each). The time did not fall, because B's consults ran one after another and
108
+ * the gate opened only after the seventh.
109
+ * - Re-attach over the store B kept: no longer bimodal. All 3 runs held everything and came
110
+ * up writable at launch (9.2 s), and the row A wrote while B was away was readable at
111
+ * 35.6 s in each, read with one consult for the collection and its data block together. No
112
+ * read was declined during the attach. The empty-store arm was not re-run.
113
+ *
114
+ * AT THE SUPPORTED LINK ON `@optimystic/*` 1.7.0 (2026-09-29), same delay, deadline and
115
+ * scenario, before cadre stated its link to Optimystic:
116
+ * - Fresh join: writable at 52.1 and 82.1 s over two runs, and 63.6 s in a third run under
117
+ * the coordinator debug channel; a fourth run with the deadline set back to 5 000 ms took
118
+ * 70.2 s. B's strand node connected to A's at 18.2-18.3 s in every run at this delay. The
119
+ * row A wrote before the join was readable at 67-124 s.
120
+ * - Re-attach over the store B kept: bimodal again. 1 of 3 runs held everything and came up
121
+ * writable at launch (9.1 s; the row at 33.4 s). The other 2 were gated and writable at
122
+ * 75.7 and 78.7 s (the row at 120.8 and 175.0 s). The empty-store arm was not re-run; at
123
+ * 900 ms it matched a fresh join.
124
+ * - What that band was NOT: a measure of the 7 000 ms deadline. Every cohort consult in those
125
+ * runs ended 3.00-3.02 s after the one before it, at 5 000 and 7 000 alike, because
126
+ * Optimystic's then-fixed 3 000 ms request dial deadline also bounded the protocol
127
+ * negotiation on an already-open connection, and one negotiation is one link round trip —
128
+ * 3 s here. So it was the band of a join whose every consult failed and whose blocks
129
+ * arrived by peer-join backfill.
130
+ *
131
+ * RE-ATTACH at 900 ms (2026-09-26, the opt-in
99
132
  * `integration-tests/src/scenarios/strand-reattach-first-sync-measure.integration.ts` — re-run
100
133
  * it before changing this number): B attached once on an undelayed link, left with
101
134
  * `stopStrand`, A wrote while it was away, the delay was raised, and B called `addStrand`
@@ -112,18 +145,23 @@ export declare const DEFAULT_STRAND_FIRST_SYNC_POLL_MS = 500;
112
145
  * connected to A's at 11 s in every arm, fresh join included, and a run with 20 missed
113
146
  * writes fell inside the run-to-run spread of those with 1.
114
147
  * This budget covers WRITABLE, not caught up: in the gated kept-store runs the row A wrote
115
- * while B was away became readable only at 140-162 s.
148
+ * while B was away became readable only at 140-162 s (121-175 s at the supported link).
116
149
  *
117
150
  * Two samples from outside this harness, neither reproduced here and neither discarded: a
118
151
  * reporter's re-attach of the same shape (optimystic #22; their harness, storage and delay
119
152
  * injector are unknown to us) opened the gate at about 150 s, after the previous 120 s budget
120
153
  * had already rejected; and a real Galaxy S7 joining fresh through relay.sereus.org took 178 s.
121
154
  *
122
- * 300 s clears the worst harness sample (79 s) by about 3.8x, the reporter's re-attach by 2x
123
- * and the device join by about 1.7x. 240 s was rejected: it clears the device join by only
124
- * 1.35x, and that sample was not a re-attach. History: the original 30 s sat INSIDE the 1000 ms
125
- * fresh band and refused about half of those joins while their sync was progressing normally;
126
- * 120 s was sized from fresh joins only and refused the reporter's re-attach.
155
+ * 300 s clears the worst harness sample (82 s, a fresh join at the supported link on 1.7.0; the
156
+ * worst on 1.8.0 is 64 s) by about 3.6x, the reporter's re-attach by 2x and the device join by
157
+ * about 1.7x. Kept at 300 s on 2026-09-29, and again after the 1.8.0 re-measure the same day,
158
+ * by the rule that re-measure applied and the next one should: keep it while the
159
+ * worst harness sample is at most 100 s, which holds a margin of at least 3x; otherwise raise
160
+ * it to 3x the worst sample, rounded up to the next whole minute. 240 s was rejected earlier:
161
+ * it clears the device join by only 1.35x, and that sample was not a re-attach. History: the
162
+ * original 30 s sat INSIDE the 1000 ms fresh band and refused about half of those joins while
163
+ * their sync was progressing normally; 120 s was sized from fresh joins only and refused the
164
+ * reporter's re-attach.
127
165
  *
128
166
  * NOTE: accepted tradeoff — what 300 s costs. This wait is what an app's `addStrand` sits in
129
167
  * before it is told "not yet", so a strand none of whose members is reachable at all takes five
@@ -78,24 +78,57 @@ export const DEFAULT_STRAND_FIRST_SYNC_POLL_MS = 500;
78
78
  *
79
79
  * Every harness figure below was taken on one shape: one Windows developer machine, two
80
80
  * relay-only `CadreNode`s (`listenAddrs: []`) on a shared loopback dedicated relay, with a
81
- * 900 ms one-way per-frame outbound delay applied to every websocket by
81
+ * one-way per-frame outbound delay applied to every websocket by
82
82
  * `integration-tests/src/harness/ws-latency.ts` in `pipelined` mode — frames stay overlapped
83
83
  * in flight, so the figure is latency and bandwidth stays unlimited; the harness's other mode
84
84
  * (`serial`) is a per-socket frame-rate cap and its delays are NOT comparable
85
- * (`docs/testing.md` → "Link latency"). A round trip therefore costs about 1.8 s. Times run
86
- * from the `addStrand` call to writable.
85
+ * (`docs/testing.md` → "Link latency"). Two delays were used: 900 ms one-way, a round trip
86
+ * near 1.8 s, the band sereus supported first; and 1 500 ms one-way, the supported 3-second
87
+ * round trip. Times run from the `addStrand` call to writable.
87
88
  *
88
- * FRESH JOIN (2026-09-26), a machine holding nothing of the strand yet: 23, 27, 31 and 41 s
89
- * over four runs at optimystic's 1000 ms cohort read deadline, and 35, 42 and 46 s over three
90
- * runs at a 5000 ms one; the re-attach scenario below later took 38.7, 38.7 and 56.9 s for
91
- * the same fresh join. **The 5000 ms band, 35-57 s, is the one in force**: sereus declares
92
- * 5000 ms (`COHORT_READ_DEADLINE_MS`, `quereus-plugin-sereus/src/cluster-size.ts`), because
93
- * 1000 ms is shorter than one round trip on that link and leaves every cohort read with no
94
- * answer to corroborate against. Widening that deadline makes a consult against a peer that
95
- * cannot answer cost longer, and this phase runs several of those — which is why both bands
96
- * are recorded here, and why a change to either number has to be weighed against the other.
89
+ * FRESH JOIN at 900 ms (2026-09-26), a machine holding nothing of the strand yet: 23, 27, 31
90
+ * and 41 s over four runs at optimystic's 1000 ms cohort read deadline, and 35, 42 and 46 s
91
+ * over three runs at a 5000 ms one; the re-attach scenario below later took 38.7, 38.7 and
92
+ * 56.9 s for the same fresh join. At that delay the 5000 ms band, 35-57 s, was the one in
93
+ * force: sereus then declared 5000 ms, because 1000 ms is shorter than one round trip on
94
+ * that link and leaves every cohort read with no answer to corroborate against. Widening that
95
+ * deadline makes a consult against a peer that cannot answer cost longer, and this phase runs
96
+ * several of those — which is why both bands are recorded here, and why a change to either
97
+ * number has to be weighed against the other.
97
98
  *
98
- * RE-ATTACH (2026-09-26, the opt-in
99
+ * AT THE SUPPORTED LINK ON `@optimystic/*` 1.8.0 (2026-09-29), 1 500 ms one-way, the read
100
+ * deadline derived from the declared link at 7 000 ms (`cohortReadDeadlineMs` in
101
+ * `link-budget.ts`; the plugin's `COHORT_READ_DEADLINE_MS` is the same number), and cadre
102
+ * stating its declared link to Optimystic, the same scenario, all under the coordinator debug
103
+ * channel:
104
+ * - Fresh join: writable at 63.9 and 63.8 s over two runs, the row A wrote before the join
105
+ * readable at 88.0 s in both. B's strand node connected to A's at 18.3-18.4 s. No read was
106
+ * declined: every consult on B completed, 6.03-6.05 s after the one before it (two link
107
+ * round trips each). The time did not fall, because B's consults ran one after another and
108
+ * the gate opened only after the seventh.
109
+ * - Re-attach over the store B kept: no longer bimodal. All 3 runs held everything and came
110
+ * up writable at launch (9.2 s), and the row A wrote while B was away was readable at
111
+ * 35.6 s in each, read with one consult for the collection and its data block together. No
112
+ * read was declined during the attach. The empty-store arm was not re-run.
113
+ *
114
+ * AT THE SUPPORTED LINK ON `@optimystic/*` 1.7.0 (2026-09-29), same delay, deadline and
115
+ * scenario, before cadre stated its link to Optimystic:
116
+ * - Fresh join: writable at 52.1 and 82.1 s over two runs, and 63.6 s in a third run under
117
+ * the coordinator debug channel; a fourth run with the deadline set back to 5 000 ms took
118
+ * 70.2 s. B's strand node connected to A's at 18.2-18.3 s in every run at this delay. The
119
+ * row A wrote before the join was readable at 67-124 s.
120
+ * - Re-attach over the store B kept: bimodal again. 1 of 3 runs held everything and came up
121
+ * writable at launch (9.1 s; the row at 33.4 s). The other 2 were gated and writable at
122
+ * 75.7 and 78.7 s (the row at 120.8 and 175.0 s). The empty-store arm was not re-run; at
123
+ * 900 ms it matched a fresh join.
124
+ * - What that band was NOT: a measure of the 7 000 ms deadline. Every cohort consult in those
125
+ * runs ended 3.00-3.02 s after the one before it, at 5 000 and 7 000 alike, because
126
+ * Optimystic's then-fixed 3 000 ms request dial deadline also bounded the protocol
127
+ * negotiation on an already-open connection, and one negotiation is one link round trip —
128
+ * 3 s here. So it was the band of a join whose every consult failed and whose blocks
129
+ * arrived by peer-join backfill.
130
+ *
131
+ * RE-ATTACH at 900 ms (2026-09-26, the opt-in
99
132
  * `integration-tests/src/scenarios/strand-reattach-first-sync-measure.integration.ts` — re-run
100
133
  * it before changing this number): B attached once on an undelayed link, left with
101
134
  * `stopStrand`, A wrote while it was away, the delay was raised, and B called `addStrand`
@@ -112,18 +145,23 @@ export const DEFAULT_STRAND_FIRST_SYNC_POLL_MS = 500;
112
145
  * connected to A's at 11 s in every arm, fresh join included, and a run with 20 missed
113
146
  * writes fell inside the run-to-run spread of those with 1.
114
147
  * This budget covers WRITABLE, not caught up: in the gated kept-store runs the row A wrote
115
- * while B was away became readable only at 140-162 s.
148
+ * while B was away became readable only at 140-162 s (121-175 s at the supported link).
116
149
  *
117
150
  * Two samples from outside this harness, neither reproduced here and neither discarded: a
118
151
  * reporter's re-attach of the same shape (optimystic #22; their harness, storage and delay
119
152
  * injector are unknown to us) opened the gate at about 150 s, after the previous 120 s budget
120
153
  * had already rejected; and a real Galaxy S7 joining fresh through relay.sereus.org took 178 s.
121
154
  *
122
- * 300 s clears the worst harness sample (79 s) by about 3.8x, the reporter's re-attach by 2x
123
- * and the device join by about 1.7x. 240 s was rejected: it clears the device join by only
124
- * 1.35x, and that sample was not a re-attach. History: the original 30 s sat INSIDE the 1000 ms
125
- * fresh band and refused about half of those joins while their sync was progressing normally;
126
- * 120 s was sized from fresh joins only and refused the reporter's re-attach.
155
+ * 300 s clears the worst harness sample (82 s, a fresh join at the supported link on 1.7.0; the
156
+ * worst on 1.8.0 is 64 s) by about 3.6x, the reporter's re-attach by 2x and the device join by
157
+ * about 1.7x. Kept at 300 s on 2026-09-29, and again after the 1.8.0 re-measure the same day,
158
+ * by the rule that re-measure applied and the next one should: keep it while the
159
+ * worst harness sample is at most 100 s, which holds a margin of at least 3x; otherwise raise
160
+ * it to 3x the worst sample, rounded up to the next whole minute. 240 s was rejected earlier:
161
+ * it clears the device join by only 1.35x, and that sample was not a re-attach. History: the
162
+ * original 30 s sat INSIDE the 1000 ms fresh band and refused about half of those joins while
163
+ * their sync was progressing normally; 120 s was sized from fresh joins only and refused the
164
+ * reporter's re-attach.
127
165
  *
128
166
  * NOTE: accepted tradeoff — what 300 s costs. This wait is what an app's `addStrand` sits in
129
167
  * before it is told "not yet", so a strand none of whose members is reachable at all takes five
@@ -135,6 +173,7 @@ export const DEFAULT_STRAND_FIRST_SYNC_POLL_MS = 500;
135
173
  * "no peer at all" could then be reported at once and this budget would only ever be spent on a
136
174
  * sync that is actually in progress — worth more the longer this budget gets.
137
175
  */
176
+ // eslint-disable-next-line no-restricted-syntax -- measured, not derived: the bands above, from integration-tests' strand-reattach-first-sync-measure.integration.ts; a whole first sync has no round-trip count
138
177
  export const DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS = 300000;
139
178
  /**
140
179
  * Thrown by `CadreNode.addStrand` / `whenStrandWritable` when a machine has not received the
@@ -1 +1 @@
1
- {"version":3,"file":"strand-first-sync-gate.js","sourceRoot":"","sources":["../src/strand-first-sync-gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strand-first-sync-gate.js","sourceRoot":"","sources":["../src/strand-first-sync-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Strand-formation deadlines, derived from the declared link round trip.
3
+ *
4
+ * The wire protocol lives in `strand-formation-protocol.ts`; this module is only the
5
+ * arithmetic it runs on: the ladder of deadlines one declaration yields
6
+ * ({@link formationDeadlines}) and the clamping of a caller's configured provisioning budget
7
+ * under its session ({@link resolveProvisionTimeoutMs}, {@link splitProvisionBudget}). It
8
+ * reaches `link-budget.ts` for the counts and `formation-approval.ts` for the hook's flat
9
+ * timeout, and nothing in the protocol, so both roles and the manager can import it freely.
10
+ */
11
+ /**
12
+ * Every deadline of one formation session, derived from one declared link round trip
13
+ * ({@link formationDeadlines}). Each waits on work that crosses the link between the two
14
+ * parties' machines, so each is a round-trip count times the declaration rather than a
15
+ * number chosen on a fast network; the initiator's and the responder's fields are derived
16
+ * together so the ordering between the roles holds by construction.
17
+ */
18
+ export interface FormationDeadlines {
19
+ /** Initiator: open a possibly relayed connection and negotiate the formation protocol. */
20
+ dialMs: number;
21
+ /** Responder: from the handler starting to the contact frame arriving. */
22
+ awaitContactMs: number;
23
+ /**
24
+ * Responder: `validateToken` plus `validateDisclosure`, budgeted as two consulting control
25
+ * reads. Counted in the travel margin and the clamp room, not wrapped in a deadline of their
26
+ * own: the session bounds them.
27
+ */
28
+ validationMs: number;
29
+ /** Responder: the provisioning hook's work budget, after which its signal aborts. */
30
+ provisionWorkMs: number;
31
+ /** Responder: the settle grace after the abort, which must contain one control commit. */
32
+ provisionGraceMs: number;
33
+ /**
34
+ * What the initiator's await-response holds over the responder's provisioning: the
35
+ * contact frame's trip out, the result frame's trip back, and the validation reads that
36
+ * run before the responder's provisioning clock starts.
37
+ */
38
+ responseTravelMarginMs: number;
39
+ /** Initiator: the result frame read. */
40
+ initiatorAwaitResponseMs: number;
41
+ /** Both roles: the whole session. */
42
+ sessionMs: number;
43
+ }
44
+ /**
45
+ * Derive every formation deadline from the declared link round trip
46
+ * (`NetworkConfig.linkRoundTripMs`; the default when none is declared is
47
+ * `DECLARED_LINK_ROUND_TRIP_MS` in `link-budget.ts`). With `L` the declaration, `R` one
48
+ * cohort read at that link (`cohortReadDeadlineMs`, 2L: one request and its answer over an
49
+ * open circuit) and `K` one commit (`commitBudgetMs`, 20L, measured):
50
+ *
51
+ * dialMs = a relayed dial plus one protocol negotiation (5L + 2 000)
52
+ * awaitContactMs = one request over the open stream (2L)
53
+ * validationMs = 2R (4L)
54
+ * provisionWorkMs = approval hook (10 000, flat) + 2R + K (10 000 + 24L)
55
+ * provisionGraceMs = R + K (22L)
56
+ * responseTravelMarginMs = L + validationMs (5L)
57
+ * initiatorAwaitResponseMs = work + grace + margin (10 000 + 51L)
58
+ * sessionMs = dialMs + initiatorAwaitResponseMs (12 000 + 56L)
59
+ *
60
+ * Why each term:
61
+ *
62
+ * - **awaitContact**: the listener's handler starts when it answers the protocol
63
+ * negotiation; the answer reaches the joiner and the contact frame comes back, one round
64
+ * trip, budgeted as a request over an open stream (2) for the same headroom as every other
65
+ * one-exchange read in `link-budget.ts`.
66
+ * - **validation** and the reads inside **provisionWork** are budgeted at two consulting
67
+ * reads each, not one per read the code issues. What actually runs: validation is three
68
+ * control reads (`isTokenValid` reads the invite; `isTokenUsed` reads it again and counts
69
+ * its usages) plus whatever the disclosure validator does; the bound provisioning path
70
+ * (`StrandFormationManager.provisionAsResponder`) reads the invite and the strand row to
71
+ * resolve the host strand, the invite again, the approver's `ValidationKey` and the seat
72
+ * count around the outside approval hook (an HTTP call, so its own
73
+ * `DEFAULT_APPROVAL_TIMEOUT_MS` is link-independent and added flat), the strand row and its
74
+ * `StrandPartyKey` to issue the joiner's membership invite, and the host strand's stamp
75
+ * before the `FormationUsage` write; then one strand-database commit issues the invite.
76
+ * Every one of those is a read of this party's own control database, which in steady state
77
+ * touches held blocks and consults nobody (`cadre-consistency.md` → "Deadlines Over
78
+ * Optimystic's Reads and Commits"), so `R` is charged as headroom for the ones that do
79
+ * consult, not per read. Waking a hibernating host strand inside the issue is NOT counted
80
+ * either: `wakeHostStrandForFormation` cuts it off by design with a retryable rejection
81
+ * and leaves the wake running, so the joiner's retry finds the strand live.
82
+ * NOTE: if formation is seen timing out while the responder's control reads are consulting
83
+ * the cohort (a cold responder, a control database not yet converged), charge one `R` per
84
+ * read here rather than raising the flat headroom; the joiner is told 'Formation
85
+ * provisioning timed out' with its invite unspent, so the cost is a retry, not a spent
86
+ * invite.
87
+ * - **provisionGrace**: the window it exists for opens when the `FormationUsage` insert
88
+ * attempt passes its abort check. `ControlDatabase.redeemInvitation` and
89
+ * `recordFormationUsage` check the signal once per attempt inside the write lock, then run
90
+ * the seat read (`assertSeatRemains`) and the transaction, so the grace contains one read
91
+ * plus one commit. A retry after a failed attempt re-checks the abort, so only one attempt
92
+ * needs containing.
93
+ * - **responseTravelMargin**: half a round trip out, half back, plus the validation reads,
94
+ * which run on the initiator's await-response clock but before the responder's
95
+ * provisioning clock starts.
96
+ * - **session**: the initiator's dial plus its await-response. The responder's whole path
97
+ * (awaitContact + validation + work + grace = 10 000 + 52L) is inside it at every L.
98
+ *
99
+ * Ordering, strict at every L > 0, and pinned by `strand-formation-deadlines.spec.ts`:
100
+ * approval hook < provisionWork < work + grace < initiatorAwaitResponse < session, and the
101
+ * responder's whole path < session. Each layer can therefore fail and report before the
102
+ * layer above it gives up, and the work budget always outlasts the hook's own timeout, so a
103
+ * dead hook reports 'Formation approval unavailable, retry' rather than racing a
104
+ * provisioning timeout.
105
+ *
106
+ * **What this costs.** At the default declaration (3 500 ms) a joiner whose responder
107
+ * accepts the contact and then hangs waits up to 188.5 s, about three minutes, before it is
108
+ * told, where the fixed ladder this replaced told it after 15 s; the whole session is
109
+ * bounded at 208 s. A responder that is unreachable still fails at the dial, 19.5 s. A host
110
+ * that knows its party is on a fast link lowers `linkRoundTripMs` and gets roughly the old
111
+ * numbers back (at L = 100: dial 2.5 s, provisioning 14.6 s, await-response 15.1 s, session
112
+ * 17.6 s). Before this derivation, the fixed 5 s dial could not open a relayed connection
113
+ * above a 1.25-second round trip, and the fixed 2 s grace could not contain one commit at the
114
+ * supported link, so a joiner could be told 'timed out' over an invite that was in fact
115
+ * spent.
116
+ */
117
+ export declare function formationDeadlines(linkRoundTripMs?: number): FormationDeadlines;
118
+ /**
119
+ * The extra room the responder's clamp holds back ({@link provisionCeilingMs}'s `reserveMs`).
120
+ *
121
+ * The initiator's clamped await-response is `session - dial`, and the responder's reply
122
+ * reaches it `responseTravelMarginMs` after provisioning ends, so provisioning must end by
123
+ * `session - dial - margin`. The responder's own room is `session - awaitContact -
124
+ * validation`; the difference between the two is what it must hold back on top. Computed
125
+ * from the responder's own derivation, since it cannot see the initiator's dial budget: the
126
+ * module doc of `link-budget.ts` says why both parties must declare the same link.
127
+ */
128
+ export declare function responderClampReserveMs(deadlines: FormationDeadlines, awaitContactMs: number): number;
129
+ /**
130
+ * Resolve a caller-supplied provisioning budget: `0`/negative means "unset" (use
131
+ * `defaultMs`). If the result would let provisioning outlive the session — no result
132
+ * frame is ever sent, exactly the failure this budget exists to prevent — clamp it to
133
+ * {@link provisionCeilingMs} and log a warning. A budget that exactly meets the ceiling is
134
+ * not clamped: the derived defaults sit there by construction.
135
+ */
136
+ export declare function resolveProvisionTimeoutMs(configured: number | undefined, defaultMs: number, sessionTimeoutMs: number, precedingMs: number, role: string, reserveMs?: number): number;
137
+ /**
138
+ * Split a resolved provisioning budget into the WORK budget and the trailing settle grace.
139
+ *
140
+ * The grace is carved OUT of the budget, never added on top, so the ladder in
141
+ * {@link formationDeadlines} is untouched: the listener aborts the provisioning hook when the
142
+ * work budget expires, then waits up to the grace for the work to settle anyway
143
+ * (`FormationListener.settleWithinGrace` in `strand-formation-protocol.ts`). `graceCeilingMs`
144
+ * is the derived grace, which contains one seat read plus one commit at the declared link; it
145
+ * is capped at half the budget so a small configured budget still spends at least half its
146
+ * time doing work.
147
+ */
148
+ export declare function splitProvisionBudget(provisionTimeoutMs: number, graceCeilingMs: number): {
149
+ workMs: number;
150
+ graceMs: number;
151
+ };