@serfab/cadre-core 1.17.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +3 -2
  2. package/dist/cadre-invite-protocol.d.ts +35 -13
  3. package/dist/cadre-invite-protocol.js +90 -25
  4. package/dist/cadre-invite-protocol.js.map +1 -1
  5. package/dist/cadre-node.d.ts +586 -255
  6. package/dist/cadre-node.js +1182 -566
  7. package/dist/cadre-node.js.map +1 -1
  8. package/dist/control-cohort.d.ts +66 -9
  9. package/dist/control-cohort.js +67 -9
  10. package/dist/control-cohort.js.map +1 -1
  11. package/dist/control-database.d.ts +451 -193
  12. package/dist/control-database.js +701 -359
  13. package/dist/control-database.js.map +1 -1
  14. package/dist/control-formation-recorder.d.ts +46 -43
  15. package/dist/control-formation-recorder.js +77 -78
  16. package/dist/control-formation-recorder.js.map +1 -1
  17. package/dist/control-protocol-guard.d.ts +6 -0
  18. package/dist/control-protocol-guard.js +14 -2
  19. package/dist/control-protocol-guard.js.map +1 -1
  20. package/dist/control-schema-version.d.ts +48 -31
  21. package/dist/control-schema-version.js +67 -45
  22. package/dist/control-schema-version.js.map +1 -1
  23. package/dist/control-schema.d.ts +8 -8
  24. package/dist/control-schema.js +16 -16
  25. package/dist/control-schema.js.map +1 -1
  26. package/dist/control-write-retry.d.ts +42 -7
  27. package/dist/control-write-retry.js +87 -19
  28. package/dist/control-write-retry.js.map +1 -1
  29. package/dist/enrolled-machine-store.d.ts +3 -3
  30. package/dist/enrolled-machine-store.js +3 -3
  31. package/dist/formation-token.d.ts +48 -0
  32. package/dist/formation-token.js +26 -0
  33. package/dist/formation-token.js.map +1 -0
  34. package/dist/index.d.ts +15 -14
  35. package/dist/index.js +17 -14
  36. package/dist/index.js.map +1 -1
  37. package/dist/joined-strand-store.d.ts +26 -9
  38. package/dist/joined-strand-store.js +63 -24
  39. package/dist/joined-strand-store.js.map +1 -1
  40. package/dist/link-budget.d.ts +9 -7
  41. package/dist/link-budget.js +9 -7
  42. package/dist/link-budget.js.map +1 -1
  43. package/dist/member-redemption.d.ts +15 -9
  44. package/dist/member-redemption.js +13 -8
  45. package/dist/member-redemption.js.map +1 -1
  46. package/dist/membership-view.d.ts +91 -0
  47. package/dist/membership-view.js +110 -0
  48. package/dist/membership-view.js.map +1 -0
  49. package/dist/owner-grant-protocol.d.ts +11 -7
  50. package/dist/owner-grant-protocol.js +31 -17
  51. package/dist/owner-grant-protocol.js.map +1 -1
  52. package/dist/peer-addr-book.d.ts +10 -1
  53. package/dist/peer-addr-book.js +4 -3
  54. package/dist/peer-addr-book.js.map +1 -1
  55. package/dist/peer-join-backfill.d.ts +96 -30
  56. package/dist/peer-join-backfill.js +272 -128
  57. package/dist/peer-join-backfill.js.map +1 -1
  58. package/dist/pending-join-runner.d.ts +48 -18
  59. package/dist/pending-join-runner.js +74 -56
  60. package/dist/pending-join-runner.js.map +1 -1
  61. package/dist/seed-bootstrap.d.ts +47 -40
  62. package/dist/seed-bootstrap.js +66 -61
  63. package/dist/seed-bootstrap.js.map +1 -1
  64. package/dist/self-address-update.d.ts +13 -0
  65. package/dist/self-address-update.js +19 -0
  66. package/dist/self-address-update.js.map +1 -0
  67. package/dist/strand-database.d.ts +23 -2
  68. package/dist/strand-database.js +26 -6
  69. package/dist/strand-database.js.map +1 -1
  70. package/dist/strand-first-sync-gate.d.ts +176 -89
  71. package/dist/strand-first-sync-gate.js +288 -122
  72. package/dist/strand-first-sync-gate.js.map +1 -1
  73. package/dist/strand-formation-deadlines.d.ts +9 -9
  74. package/dist/strand-formation-deadlines.js +9 -9
  75. package/dist/strand-formation-manager.d.ts +23 -17
  76. package/dist/strand-formation-manager.js +46 -44
  77. package/dist/strand-formation-manager.js.map +1 -1
  78. package/dist/strand-formation-protocol.d.ts +5 -8
  79. package/dist/strand-formation-protocol.js +101 -16
  80. package/dist/strand-formation-protocol.js.map +1 -1
  81. package/dist/strand-instance-manager.d.ts +26 -17
  82. package/dist/strand-instance-manager.js +119 -50
  83. package/dist/strand-instance-manager.js.map +1 -1
  84. package/dist/strand-membership-reconciler.d.ts +16 -13
  85. package/dist/strand-membership-reconciler.js +28 -22
  86. package/dist/strand-membership-reconciler.js.map +1 -1
  87. package/dist/strand-membership-writer.d.ts +40 -28
  88. package/dist/strand-membership-writer.js +212 -153
  89. package/dist/strand-membership-writer.js.map +1 -1
  90. package/dist/strand-move.js +5 -2
  91. package/dist/strand-move.js.map +1 -1
  92. package/dist/strand-revocation-enforcer.d.ts +134 -60
  93. package/dist/strand-revocation-enforcer.js +144 -71
  94. package/dist/strand-revocation-enforcer.js.map +1 -1
  95. package/dist/strand-solicitation.d.ts +50 -39
  96. package/dist/strand-solicitation.js +10 -3
  97. package/dist/strand-solicitation.js.map +1 -1
  98. package/dist/strand-watcher.d.ts +80 -13
  99. package/dist/strand-watcher.js +152 -48
  100. package/dist/strand-watcher.js.map +1 -1
  101. package/dist/table-change-watch.d.ts +63 -0
  102. package/dist/table-change-watch.js +209 -0
  103. package/dist/table-change-watch.js.map +1 -0
  104. package/dist/timeout-scheduler.d.ts +4 -3
  105. package/dist/timeout-scheduler.js +4 -3
  106. package/dist/timeout-scheduler.js.map +1 -1
  107. package/dist/types.d.ts +90 -49
  108. package/dist/types.js +4 -4
  109. package/dist/types.js.map +1 -1
  110. package/package.json +8 -8
  111. package/src/cadre-invite-protocol.ts +119 -31
  112. package/src/cadre-node.ts +1289 -588
  113. package/src/control-cohort.ts +86 -9
  114. package/src/control-database.ts +848 -403
  115. package/src/control-formation-recorder.ts +359 -353
  116. package/src/control-protocol-guard.ts +16 -2
  117. package/src/control-schema-version.ts +101 -51
  118. package/src/control-schema.ts +16 -16
  119. package/src/control-write-retry.ts +91 -19
  120. package/src/delegate-admission.ts +351 -351
  121. package/src/enrolled-machine-store.ts +3 -3
  122. package/src/formation-token.ts +63 -0
  123. package/src/index.ts +30 -18
  124. package/src/joined-strand-store.ts +69 -24
  125. package/src/link-budget.ts +9 -7
  126. package/src/member-redemption.ts +20 -13
  127. package/src/membership-view.ts +144 -0
  128. package/src/owner-grant-protocol.ts +33 -18
  129. package/src/peer-addr-book.ts +4 -3
  130. package/src/peer-join-backfill.ts +961 -761
  131. package/src/pending-join-runner.ts +76 -53
  132. package/src/seed-bootstrap.ts +72 -67
  133. package/src/self-address-update.ts +19 -0
  134. package/src/strand-database.ts +352 -322
  135. package/src/strand-first-sync-gate.ts +334 -134
  136. package/src/strand-formation-deadlines.ts +227 -227
  137. package/src/strand-formation-manager.ts +731 -725
  138. package/src/strand-formation-protocol.ts +122 -24
  139. package/src/strand-instance-manager.ts +2092 -2026
  140. package/src/strand-membership-reconciler.ts +31 -25
  141. package/src/strand-membership-writer.ts +2093 -2019
  142. package/src/strand-move.ts +5 -2
  143. package/src/strand-revocation-enforcer.ts +740 -654
  144. package/src/strand-solicitation.ts +61 -34
  145. package/src/strand-watcher.ts +175 -56
  146. package/src/table-change-watch.ts +285 -0
  147. package/src/timeout-scheduler.ts +29 -28
  148. package/src/types.ts +89 -51
@@ -0,0 +1,209 @@
1
+ /**
2
+ * One way for cadre-core to be told that a control or strand table changed, so an idle loop
3
+ * can wait for a change instead of re-reading on a timer. Built on Quereus's
4
+ * `Database.watch`, which hears both a commit made on this `Database` and, through the
5
+ * Optimystic plugin's `notifyExternalChange` bridge, every change this node's storage takes
6
+ * in from elsewhere: a consensus commit it took part in as a cohort member, a block-transfer
7
+ * push such as join backfill, and a read-repair landing. Events are table-wide; there are no
8
+ * row keys.
9
+ *
10
+ * The consumer's `onChange` never runs inside a commit. The watch handler only sets flags
11
+ * and arms a timer, and runs are single-flight: changes inside the coalescing window share
12
+ * one run, a change during a run produces exactly one follow-up run, and a consumer's own
13
+ * `trigger` (start, retry backoff) and the backstop go through the same rule.
14
+ * `docs/architecture.md` → "Table change subscriptions" says what the seam does not hear,
15
+ * which is why consumers keep a backstop.
16
+ */
17
+ import debug from 'debug';
18
+ import { defaultTimeoutScheduler } from './timeout-scheduler.js';
19
+ const log = debug('sereus:cadre:table-watch');
20
+ /**
21
+ * Default coalescing window. One local commit fires the watch twice (Quereus's own
22
+ * post-commit dispatch, then the plugin's storage event); this folds both into one run.
23
+ */
24
+ export const TABLE_CHANGE_COALESCE_MS = 50;
25
+ /**
26
+ * Subscribe `onChange` to changes in `tables` (qualified `'Schema.Table'` names, e.g.
27
+ * `'CadreControl.Strand'`). Throws synchronously on a name that is not qualified, or a table
28
+ * that does not exist.
29
+ *
30
+ * NOTE: the plugin arms its storage bridge for a table on that table's first full
31
+ * initialization on this `Database`, so a table not yet touched hears local commits only,
32
+ * not replicated ones. Consumers' start runs touch their tables; the seam does not, because
33
+ * touching a never-written collection consults the network.
34
+ *
35
+ * NOTE: Quereus silently disposes a subscription whose table is dropped or altered. Control
36
+ * and strand schemas are fixed for a database's life (an app-schema attach adds `App` tables
37
+ * and alters no `Strand` one), so nothing re-subscribes; revisit if that stops holding.
38
+ */
39
+ export function watchTables(db, tables, onChange, options) {
40
+ return new TableChangeWatcher({ db, tables }, onChange, options);
41
+ }
42
+ /**
43
+ * {@link watchTables}' schedule — single-flight runs, `trigger`, the backstop — with no table
44
+ * subscription, for a consumer with no database to watch (tests, fakes).
45
+ */
46
+ export function scheduleRuns(onChange, options) {
47
+ return new TableChangeWatcher(undefined, onChange, options);
48
+ }
49
+ /**
50
+ * The subscriptions one database owner has handed out, so its `close()` can dispose those
51
+ * still live — synchronously, ahead of its own awaits, so nothing a closing commit does
52
+ * schedules a run. (`Database.close()` disposes them too, but only after its own awaits.)
53
+ */
54
+ export class TableWatchRegistry {
55
+ constructor() {
56
+ this.live = new Set();
57
+ }
58
+ watch(db, tables, onChange, options) {
59
+ const inner = watchTables(db, tables, onChange, options);
60
+ const tracked = {
61
+ trigger: delayMs => inner.trigger(delayMs),
62
+ dispose: () => {
63
+ this.live.delete(tracked);
64
+ inner.dispose();
65
+ },
66
+ };
67
+ this.live.add(tracked);
68
+ return tracked;
69
+ }
70
+ disposeAll() {
71
+ for (const watch of [...this.live]) {
72
+ watch.dispose();
73
+ }
74
+ }
75
+ }
76
+ class TableChangeWatcher {
77
+ constructor(watched, onChange, options) {
78
+ this.onChange = onChange;
79
+ this.options = options;
80
+ /** The consumer's name for each watched table, by Quereus's lowercased `schema.table` key. */
81
+ this.names = new Map();
82
+ /**
83
+ * Every armed timer: the coalescing window, `trigger` delays and the backstop. Whichever
84
+ * fires first starts the run, and starting a run clears the rest.
85
+ */
86
+ this.wakes = new Set();
87
+ this.running = false;
88
+ /** A timer fired while a run was in flight: one more run, a window after it settles. */
89
+ this.rerun = false;
90
+ this.disposed = false;
91
+ this.warnedThenable = false;
92
+ this.scheduler = options.scheduler ?? defaultTimeoutScheduler;
93
+ this.coalesceMs = options.coalesceMs ?? TABLE_CHANGE_COALESCE_MS;
94
+ this.subscription = watched && this.subscribe(watched);
95
+ this.armBackstop();
96
+ }
97
+ trigger(delayMs = 0) {
98
+ if (!this.disposed) {
99
+ this.arm(delayMs);
100
+ }
101
+ }
102
+ dispose() {
103
+ if (this.disposed) {
104
+ return;
105
+ }
106
+ this.disposed = true;
107
+ this.subscription?.unsubscribe();
108
+ this.clearWakes();
109
+ }
110
+ subscribe({ db, tables }) {
111
+ const watches = tables.map(fullTableWatch);
112
+ watches.forEach((watch, i) => this.names.set(tableKey(watch), tables[i]));
113
+ const scope = { watches, nonDeterministicSources: [], unboundParameters: [] };
114
+ // Returns undefined, so Quereus never awaits consumer work inside a commit.
115
+ return db.watch(scope, event => this.handleChange(event));
116
+ }
117
+ /** Runs inside the committing call, possibly under the control write lock: flags and a timer only. */
118
+ handleChange(event) {
119
+ if (this.disposed) {
120
+ return;
121
+ }
122
+ this.coalesceWake ?? (this.coalesceWake = this.arm(this.coalesceMs));
123
+ for (const { watch } of event.matched) {
124
+ this.invalidate(this.names.get(tableKey(watch)) ?? tableKey(watch));
125
+ }
126
+ }
127
+ invalidate(table) {
128
+ let result;
129
+ try {
130
+ result = this.options.onInvalidate?.(table);
131
+ }
132
+ catch (err) {
133
+ log('%s: onInvalidate threw for %s: %O', this.options.label, table, err);
134
+ return;
135
+ }
136
+ if (!this.warnedThenable && isThenable(result)) {
137
+ this.warnedThenable = true;
138
+ log('%s: misuse — onInvalidate returned a promise for %s; it is never awaited, and must only set a flag', this.options.label, table);
139
+ }
140
+ }
141
+ arm(delayMs) {
142
+ const wake = { handle: undefined };
143
+ wake.handle = this.scheduler.setTimeout(() => this.fire(wake), delayMs);
144
+ this.wakes.add(wake);
145
+ return wake;
146
+ }
147
+ armBackstop() {
148
+ if (this.options.backstopMs !== undefined) {
149
+ this.arm(this.options.backstopMs);
150
+ }
151
+ }
152
+ clearWakes() {
153
+ for (const wake of this.wakes) {
154
+ this.scheduler.clearTimeout(wake.handle);
155
+ }
156
+ this.wakes.clear();
157
+ this.coalesceWake = undefined;
158
+ }
159
+ fire(wake) {
160
+ if (!this.wakes.delete(wake)) {
161
+ return;
162
+ }
163
+ if (wake === this.coalesceWake) {
164
+ this.coalesceWake = undefined;
165
+ }
166
+ if (this.running) {
167
+ this.rerun = true;
168
+ return;
169
+ }
170
+ this.clearWakes();
171
+ this.rerun = false;
172
+ this.running = true;
173
+ void this.run();
174
+ }
175
+ async run() {
176
+ try {
177
+ await this.onChange();
178
+ }
179
+ catch (err) {
180
+ log('%s: change run failed: %O', this.options.label, err);
181
+ }
182
+ this.running = false;
183
+ this.settled();
184
+ }
185
+ settled() {
186
+ if (this.disposed) {
187
+ return;
188
+ }
189
+ if (this.rerun) {
190
+ this.coalesceWake ?? (this.coalesceWake = this.arm(this.coalesceMs));
191
+ }
192
+ this.armBackstop();
193
+ }
194
+ }
195
+ function fullTableWatch(qualifiedTable) {
196
+ const [schema, table, ...rest] = qualifiedTable.split('.');
197
+ if (!schema || !table || rest.length > 0) {
198
+ throw new Error(`watchTables: '${qualifiedTable}' is not a qualified 'Schema.Table' name`);
199
+ }
200
+ return { table: { schema, table }, columns: 'all', scope: { kind: 'full' } };
201
+ }
202
+ /** Quereus's own matching key for a watched table. */
203
+ function tableKey(watch) {
204
+ return `${watch.table.schema}.${watch.table.table}`.toLowerCase();
205
+ }
206
+ function isThenable(value) {
207
+ return typeof value === 'object' && value !== null && typeof value.then === 'function';
208
+ }
209
+ //# sourceMappingURL=table-change-watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-change-watch.js","sourceRoot":"","sources":["../src/table-change-watch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,MAAM,GAAG,GAAG,KAAK,CAAC,0BAA0B,CAAC,CAAC;AAE9C;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AA8B3C;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CACzB,EAAY,EACZ,MAAyB,EACzB,QAA6B,EAC7B,OAAgC;IAEhC,OAAO,IAAI,kBAAkB,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAA6B,EAAE,OAAgC;IAC1F,OAAO,IAAI,kBAAkB,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IAA/B;QACmB,SAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IAyBtD,CAAC;IAvBC,KAAK,CACH,EAAY,EACZ,MAAyB,EACzB,QAA6B,EAC7B,OAAgC;QAEhC,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACzD,MAAM,OAAO,GAAqB;YAChC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAC1C,OAAO,EAAE,GAAG,EAAE;gBACZ,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAC1B,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,CAAC;SACF,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,UAAU;QACR,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;CACF;AAaD,MAAM,kBAAkB;IAmBtB,YACE,OAAkC,EACjB,QAA6B,EAC7B,OAAgC;QADhC,aAAQ,GAAR,QAAQ,CAAqB;QAC7B,YAAO,GAAP,OAAO,CAAyB;QApBnD,8FAA8F;QAC7E,UAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;QAGnD;;;WAGG;QACc,UAAK,GAAG,IAAI,GAAG,EAAQ,CAAC;QAGjC,YAAO,GAAG,KAAK,CAAC;QACxB,wFAAwF;QAChF,UAAK,GAAG,KAAK,CAAC;QACd,aAAQ,GAAG,KAAK,CAAC;QACjB,mBAAc,GAAG,KAAK,CAAC;QAO7B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,uBAAuB,CAAC;QAC9D,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,wBAAwB,CAAC;QACjE,IAAI,CAAC,YAAY,GAAG,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED,OAAO,CAAC,OAAO,GAAG,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,CAAC;QACjC,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAEO,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,EAAiB;QAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAC3C,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,MAAM,KAAK,GAAgB,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAAC;QAC3F,4EAA4E;QAC5E,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,sGAAsG;IAC9F,YAAY,CAAC,KAAiB;QACpC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,YAAY,KAAjB,IAAI,CAAC,YAAY,GAAK,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,EAAC;QAChD,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACtC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAEO,UAAU,CAAC,KAAa;QAC9B,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,mCAAmC,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;YACzE,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC3B,GAAG,CAAC,oGAAoG,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACvI,CAAC;IACH,CAAC;IAEO,GAAG,CAAC,OAAe;QACzB,MAAM,IAAI,GAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QACzC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QACxE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,WAAW;QACjB,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YAC1C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAEO,UAAU;QAChB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;IAChC,CAAC;IAEO,IAAI,CAAC,IAAU;QACrB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,OAAO;QACT,CAAC;QACD,IAAI,IAAI,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC;YAC/B,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;QAChC,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;IAClB,CAAC;IAEO,KAAK,CAAC,GAAG;QACf,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,2BAA2B,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAEO,OAAO;QACb,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,YAAY,KAAjB,IAAI,CAAC,YAAY,GAAK,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,EAAC;QAClD,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;CACF;AAED,SAAS,cAAc,CAAC,cAAsB;IAC5C,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,iBAAiB,cAAc,0CAA0C,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;AAC/E,CAAC;AAED,sDAAsD;AACtD,SAAS,QAAQ,CAAC,KAAiB;IACjC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC;AACpE,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAQ,KAA4B,CAAC,IAAI,KAAK,UAAU,CAAC;AACjH,CAAC"}
@@ -1,7 +1,8 @@
1
1
  /**
2
- * The timer seam shared by the strand bring-up loops that re-arm themselves one step at a
3
- * time — the first-sync write gate's probe loop (`strand-first-sync-gate.ts`) and the
4
- * membership reconciler's retry ladder (`strand-membership-reconciler.ts`). `setTimeout`
2
+ * The timer seam shared by the loops that re-arm themselves one step at a time — the
3
+ * membership reconciler's retry ladder (`strand-membership-reconciler.ts`) — and by the table
4
+ * change subscription's coalescing, trigger and backstop timers (`table-change-watch.ts`), on
5
+ * which the first-sync write gate's probes run (`strand-first-sync-gate.ts`). `setTimeout`
5
6
  * rather than a repeating interval because the next step is armed only once the previous
6
7
  * one settles, so a slow network read never stacks steps behind it, and because the delay
7
8
  * can differ per step.
@@ -1,7 +1,8 @@
1
1
  /**
2
- * The timer seam shared by the strand bring-up loops that re-arm themselves one step at a
3
- * time — the first-sync write gate's probe loop (`strand-first-sync-gate.ts`) and the
4
- * membership reconciler's retry ladder (`strand-membership-reconciler.ts`). `setTimeout`
2
+ * The timer seam shared by the loops that re-arm themselves one step at a time — the
3
+ * membership reconciler's retry ladder (`strand-membership-reconciler.ts`) — and by the table
4
+ * change subscription's coalescing, trigger and backstop timers (`table-change-watch.ts`), on
5
+ * which the first-sync write gate's probes run (`strand-first-sync-gate.ts`). `setTimeout`
5
6
  * rather than a repeating interval because the next step is armed only once the previous
6
7
  * one settles, so a slow network read never stacks steps behind it, and because the delay
7
8
  * can differ per step.
@@ -1 +1 @@
1
- {"version":3,"file":"timeout-scheduler.js","sourceRoot":"","sources":["../src/timeout-scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,uBAAuB,GAAqB;IACvD,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACrB,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACjC,MAAiC,CAAC,KAAK,EAAE,EAAE,CAAC;QAC7C,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAA4C,CAAC;CACrF,CAAC"}
1
+ {"version":3,"file":"timeout-scheduler.js","sourceRoot":"","sources":["../src/timeout-scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,uBAAuB,GAAqB;IACvD,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACrB,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACjC,MAAiC,CAAC,KAAK,EAAE,EAAE,CAAC;QAC7C,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAA4C,CAAC;CACrF,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { ConnectionGater, Libp2p, PeerId, PrivateKey } from '@libp2p/interface';
2
2
  import type { CircuitRelayServerInit } from '@libp2p/circuit-relay-v2';
3
3
  import type { IRawStorage, Libp2pConnectionMonitorInit, Libp2pTransports, NoiseCryptoInterface } from '@optimystic/db-p2p';
4
- import type { IPeerNetwork, IRepo } from '@optimystic/db-core';
4
+ import type { CollectionChangeListener, IPeerNetwork, IRepo } from '@optimystic/db-core';
5
5
  import type { PeerJoinBackfillConfig } from './peer-join-backfill.js';
6
6
  import type { StrandReactivityConfig } from './strand-reactivity.js';
7
7
  import type { StrandRevocationEnforcementConfig } from './strand-revocation-enforcer.js';
@@ -18,6 +18,7 @@ import type { EnrolledMachineStore } from './enrolled-machine-store.js';
18
18
  import type { JoinedStrandStore } from './joined-strand-store.js';
19
19
  import type { PushNotifier } from './push-notifier.js';
20
20
  import type { CadreInviteSignedFields, OwnerGrantSignedFields, RevocableTable } from './control-authorization.js';
21
+ import type { FoundingInvite } from './control-database.js';
21
22
  import type { CadreInvitation } from './cadre-invite-protocol.js';
22
23
  import type { OwnerGrantBundle } from './owner-grant-protocol.js';
23
24
  import type { RedemptionAddressOutcome } from './member-redemption.js';
@@ -38,6 +39,23 @@ export interface Libp2pNodeWithRepo extends Libp2p {
38
39
  * backfill (`peer-join-backfill.ts`) logs once and stays inert when it is absent.
39
40
  */
40
41
  keyNetwork?: IPeerNetwork;
42
+ /**
43
+ * The node's block store, attached by the same factory (Optimystic's
44
+ * `OptimysticNodeAttachments.storageRepo`), narrowed to the storage-wide change feed: it
45
+ * fires for every block that lands in this node's store, a push or a commit. Optional for
46
+ * the same reason as `keyNetwork`; the first-sync gate (`strand-first-sync-gate.ts`) wakes
47
+ * on it, and logs once and probes on its backstop alone when it is absent.
48
+ */
49
+ storageRepo?: {
50
+ onAnyCollectionChange(listener: CollectionChangeListener): () => void;
51
+ };
52
+ /**
53
+ * Tells the node a peer holds a block at a revision or later (Optimystic's
54
+ * `OptimysticNodeAttachments.reportBlockHeld`), so its under-replication drain and rebalance
55
+ * monitor do not push that peer the same block again. Optional for the same reason as
56
+ * `keyNetwork`; the join catch-up (`peer-join-backfill.ts`) reports each block a peer accepted.
57
+ */
58
+ reportBlockHeld?: (blockId: string, rev: number, peerId: string) => Promise<void>;
41
59
  }
42
60
  /**
43
61
  * Node profile determines storage participation
@@ -546,8 +564,13 @@ export interface NetworkConfig {
546
564
  */
547
565
  targetDegree?: number;
548
566
  /**
549
- * Recurring reconcile cadence in ms. Defaults to
550
- * {@link DEFAULT_CONTROL_COHORT_RECONCILE_MS}.
567
+ * The reconcile pass's backstop in ms: a pass runs this long after the last one
568
+ * settled when nothing has started one sooner (a membership table change, a sibling
569
+ * connecting or disconnecting, an address change, a redial or strand-address wake).
570
+ * It also caps the redial ladder. Defaults to
571
+ * {@link DEFAULT_CONTROL_COHORT_RECONCILE_MS} (2 minutes). The membership rows view's
572
+ * maximum age (`MEMBERSHIP_VIEW_MAX_AGE_MS`, 15 s) does not follow this field: a backstop
573
+ * longer than it reads storage on every backstop pass, a shorter one every 15 s at most.
551
574
  */
552
575
  reconcileMs?: number;
553
576
  /**
@@ -615,10 +638,10 @@ export interface NetworkConfig {
615
638
  * pinging node's monitor sees the reset and aborts the connection exactly as it would on its
616
639
  * own timeout. So this deadline is only real up to the handler timeout of the node being
617
640
  * pinged, which has to stay above it. The deployed relay sets its handler to 60 s for that
618
- * reason (`ops/docker/libp2p-infra/src/main.ts`). Every cadre node's handler is Optimystic's
619
- * bare `ping()`, `@libp2p/ping`'s 10 s, and nothing here can move it
620
- * (`tickets/blocked/optimystic-ping-handler-timeout-below-the-monitor-deadline.md`): between
621
- * two cadre nodes, a ping whose echo takes longer than 10 s still drops the connection.
641
+ * reason (`ops/docker/libp2p-infra/src/main.ts`). Every cadre node's handler is Optimystic's,
642
+ * which derives it from this object: twice `pingTimeout.maxTimeout`, so 60 s
643
+ * (`NodeOptions.pingHandlerTimeoutMs`, unset here, since Optimystic 1.16.0). An app that
644
+ * replaces this object without a `pingTimeout` gets `@libp2p/ping`'s 10 s handler back.
622
645
  *
623
646
  * WHAT THE LINK NEEDS OF IT. One ping opens a fresh stream and echoes over it: a protocol
624
647
  * negotiation plus the echo, two link round trips by `link-budget.ts`'s counts (a `newStream`
@@ -783,10 +806,11 @@ export interface CadreNodeConfig {
783
806
  strandClusterSize?: number;
784
807
  /**
785
808
  * Tuning for the strand peer-join block catch-up (see `peer-join-backfill.ts`):
786
- * when a strand's libp2p node connects to a peer this runtime has not yet
787
- * caught up, every block in the strand's own raw store is pushed to that peer,
788
- * so a machine that joined the strand after blocks were committed still ends
789
- * up physically holding them. Applies to every strand this node starts.
809
+ * when a strand's libp2p node connects (or reconnects) to a peer, every
810
+ * block in the strand's own raw store that the peer is not
811
+ * known to hold is pushed to it, each block once per peer, so a machine that
812
+ * joined the strand after blocks were committed still ends up physically
813
+ * holding them. Applies to every strand this node starts.
790
814
  * Omit for the defaults (`DEFAULT_PEER_JOIN_BACKFILL`); `{ enabled: false }`
791
815
  * restores the no-backfill behaviour. The control network runs its own,
792
816
  * separately tuned catch-up — see {@link controlBackfill}.
@@ -813,13 +837,14 @@ export interface CadreNodeConfig {
813
837
  /**
814
838
  * Tuning for the CONTROL network's peer-join block catch-up (same module,
815
839
  * `peer-join-backfill.ts`, wired in `CadreNode.start`): when the control
816
- * libp2p node connects to an AUTHORIZED party member this runtime has not yet
817
- * caught up, every block in the control database's own raw store is pushed to
818
- * it. This is what makes a control block committed while the writer was alone
819
- * (collection headers written at genesis above all) physically reach members
820
- * that joined later — without it, such a member that restarts offline reads
821
- * the affected tables as empty. Pushes are gated on
822
- * `CadreNode.isAuthorizedMember`, checked at push time, because the control
840
+ * libp2p node connects (or reconnects) to an AUTHORIZED party member, every
841
+ * block in the control database's own raw store that the
842
+ * member is not known to hold is pushed to it. This is what makes a control
843
+ * block committed while the writer was alone (collection headers written at
844
+ * genesis above all) physically reach members that joined later — without it,
845
+ * such a member that restarts offline reads the affected tables as empty.
846
+ * Pushes are gated on the node's authorized-member snapshot (the rule
847
+ * `CadreNode.isAuthorizedMember` reads), checked at push time, because the control
823
848
  * network's inbound gate deliberately admits non-members in several states
824
849
  * (seed delivery, enrollment, bootstrap/relay peers). Omit for the defaults
825
850
  * (`DEFAULT_PEER_JOIN_BACKFILL`); `{ enabled: false }` disables it.
@@ -831,9 +856,10 @@ export interface CadreNodeConfig {
831
856
  * starts: each strand node materializes the peer ids bound to REMOVED members
832
857
  * (orphaned `Strand.MemberPeer` rows) and refuses them at the stream and
833
858
  * connection layers, so removing a party cuts the network to its machines
834
- * rather than only deleting its row. Open strands never arm it. Omit for the
835
- * defaults (`DEFAULT_REVOCATION_POLL_INTERVAL_MS` refresh cadence);
836
- * `{ enabled: false }` restores the pre-existing behaviour.
859
+ * rather than only deleting its row. Open strands never arm it. Each strand
860
+ * re-reads its membership when `Strand.Member` or `Strand.MemberPeer` changes,
861
+ * and at the latest every `DEFAULT_REVOCATION_BACKSTOP_MS`. Omit for the
862
+ * defaults; `{ enabled: false }` restores the pre-existing behaviour.
837
863
  */
838
864
  strandRevocationEnforcement?: StrandRevocationEnforcementConfig;
839
865
  /**
@@ -845,9 +871,8 @@ export interface CadreNodeConfig {
845
871
  * background without blocking bring-up. `pollIntervalMs` is the IDLE cadence
846
872
  * (how often a machine nobody has admitted re-checks) and the cap of the
847
873
  * unfinished-join retry ladder, NOT the retry cadence itself — an unfinished
848
- * join retries from 1 s, doubling. Omit for the defaults (it mirrors
849
- * {@link strandRevocationEnforcement}'s `pollIntervalMs`);
850
- * `{ enabled: false }` disarms the loop — for test fixtures that hand-drive
874
+ * join retries from 1 s, doubling. Omit for the defaults
875
+ * (`DEFAULT_MEMBERSHIP_RECONCILE_INTERVAL_MS`); `{ enabled: false }` disarms the loop — for test fixtures that hand-drive
851
876
  * the membership writers and assert exact row sets.
852
877
  */
853
878
  strandMembershipReconciliation?: StrandMembershipReconciliationConfig;
@@ -856,21 +881,27 @@ export interface CadreNodeConfig {
856
881
  * (`strand-first-sync-gate.ts`), applied to every strand this node launches as a
857
882
  * non-founder: the strand's database is withheld from the app (`StrandInstance.database`
858
883
  * unset, status `'syncing'`) until the strand's `Strand.Header` has been received from
859
- * another member and every `App` table has been read once, because a machine that writes
860
- * to a table it has never fetched creates a private copy of it that never merges.
884
+ * another member and every `Strand` and `App` table has been read once, because a
885
+ * machine that writes to a table it has never fetched creates a private copy of it that
886
+ * never merges.
861
887
  * `timeoutMs` bounds how long {@link CadreNode.addStrand} waits before rejecting with
862
- * `StrandAwaitingFirstSyncError` (retryable — the launch stays up and keeps probing);
863
- * `pollIntervalMs` is the probe cadence. Omit for the defaults (`DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`,
888
+ * `StrandAwaitingFirstSyncError` (retryable — the launch stays up and keeps probing).
889
+ * While gated the machine probes whenever data lands in the strand's store, and
890
+ * `backstopMs` after its last probe when none has. Omit for the defaults (`DEFAULT_STRAND_FIRST_SYNC_TIMEOUT_MS`,
864
891
  * sized for a machine re-attaching through a relay on a slow link, the slowest attach
865
892
  * measured; that constant's doc comment carries the number, the measurement behind it and
866
- * what the budget costs, and is the only copy of them; `DEFAULT_STRAND_FIRST_SYNC_POLL_MS`,
867
- * 500 ms). There is deliberately no way to disable the gate: a machine whose store already
893
+ * what the budget costs, and is the only copy of them; `DEFAULT_STRAND_FIRST_SYNC_BACKSTOP_MS`,
894
+ * 2 s). There is deliberately no way to disable the gate: a machine whose store already
868
895
  * holds the Header is never gated, so nothing that works today is blocked by it.
869
896
  */
870
897
  strandFirstSync?: StrandFirstSyncConfig;
871
898
  /** Hibernation configuration */
872
899
  hibernation?: HibernationConfig;
873
- /** Polling interval for strand watcher in ms (default: 5000) */
900
+ /**
901
+ * The strand watcher's backstop in ms (default 300000): the longest it goes without
902
+ * re-reading `Strand` and `JoinedStrand` when no change to them is heard. It otherwise runs
903
+ * on a change. Also caps the first step of its retry ladders, which is 5 s otherwise.
904
+ */
874
905
  strandWatchInterval?: number;
875
906
  /**
876
907
  * Require a valid author signature on every sApp schema before a strand is
@@ -1363,6 +1394,14 @@ export interface FoundStrandConfig {
1363
1394
  memberPrivateKey?: string;
1364
1395
  /** sApp configuration the hosting application provides, as for {@link StrandConfig}. */
1365
1396
  sAppConfig: SAppConfig;
1397
+ /**
1398
+ * A formation invitation to publish bound to this strand, for the app `sAppConfig.id`, in the
1399
+ * same control transaction and owner approval as the strand row. Mint it first with
1400
+ * `createOpenInvitation` (its `expiration` is `expiresAtMs`). When the strand is already
1401
+ * published the invitation is published on its own, under its own approval, as
1402
+ * `publishFormationInvite` would; one already stored under `token` for this strand is kept.
1403
+ */
1404
+ formationInvite?: Omit<FoundingInvite, 'sAppId'>;
1366
1405
  /**
1367
1406
  * Forwarded to the attach ({@link StrandConfig.awaitFirstSync}). Matters only when the
1368
1407
  * call ATTACHES rather than founds — the stored row was published by a sibling machine
@@ -1682,10 +1721,10 @@ export interface CadreNodeEvents {
1682
1721
  * row is the product of the formation's consent, so claiming it needs no second one.
1683
1722
  *
1684
1723
  * **Fired once per strand per session, and it can fire before your listener is
1685
- * attached.** The strand watcher's first poll runs inside `CadreNode.start()`
1724
+ * attached.** The strand watcher's first run is inside `CadreNode.start()`
1686
1725
  * (100 ms after the watcher starts), so every strand already stored for this
1687
1726
  * party is normally offered while the embedding app is still inside its own
1688
- * `start()` await. The watcher then records the strand as seen and no later poll
1727
+ * `start()` await. The watcher then records the strand as seen and no later run
1689
1728
  * re-offers it, so a listener attached a moment late misses those strands for the
1690
1729
  * life of the process.
1691
1730
  *
@@ -1746,10 +1785,17 @@ export interface CadreNodeEvents {
1746
1785
  partyId: string;
1747
1786
  peerId: string;
1748
1787
  };
1749
- /** Emitted when a seed is successfully applied */
1788
+ /**
1789
+ * Emitted when a seed delivered over the seed protocol has been verified, merged and
1790
+ * acknowledged to its sender. The dials of its preferred peers follow this event, so it
1791
+ * waits on no connection. `dialTargets` are those peers (`seedDialTargets`): an embedder
1792
+ * that restarts into another party's stores carries them there, since this node retained
1793
+ * them in its own.
1794
+ */
1750
1795
  'seed:applied': {
1751
1796
  partyId: string;
1752
1797
  peersAdded: number;
1798
+ dialTargets: SeedDialTarget[];
1753
1799
  };
1754
1800
  /** Emitted when seed application fails */
1755
1801
  'seed:error': {
@@ -1761,10 +1807,10 @@ export interface CadreNodeEvents {
1761
1807
  * valid claim proof has been recorded (`claim.record`) and has durably anchored its
1762
1808
  * signer as this node's owner. Carries the claim's party beside the owner key: the
1763
1809
  * party this node should serve from now on, which its own config did not know. Fires
1764
- * BEFORE the seed is acknowledged to the claimant, its peers merged and its owners
1765
- * dialed, so it precedes that seed's `seed:applied`; an embedder that restarts into
1766
- * the claimed party on this event should wait for that `seed:applied` (or `seed:error`)
1767
- * so the acknowledgement is not lost with the node. `CadreNode.isAwaitingClaim` is the
1810
+ * BEFORE the seed is acknowledged to the claimant and its peers merged, so it precedes
1811
+ * that seed's `seed:applied`; an embedder that restarts into the claimed party on this
1812
+ * event should wait for that `seed:applied` (or `seed:error`) so the acknowledgement is
1813
+ * not lost with the node. `CadreNode.isAwaitingClaim` is the
1768
1814
  * matching predicate for a reader that subscribed too late to see the event.
1769
1815
  */
1770
1816
  'claim:accepted': NodeClaimRecord;
@@ -2007,7 +2053,7 @@ export interface CadreOwnerKey {
2007
2053
  }
2008
2054
  /** Why `CadreNode.addOwner` refuses by name (`OwnerKeyAddRefusedError.code`). */
2009
2055
  export type OwnerKeyAddRefusal =
2010
- /** No control connection while the party has other members: the row would stay on this node, and nothing re-issues an `OwnerKey` add. */
2056
+ /** No sibling reachable (`CadreNode.hasReachableControlSibling`) while the party has other members: the row would stay on this node, and nothing re-issues an `OwnerKey` add. */
2011
2057
  'alone';
2012
2058
  /** Why `CadreNode.removeMachine` refuses by name (`MachineRemovalRefusedError.code`). */
2013
2059
  export type MachineRemovalRefusal =
@@ -2044,7 +2090,7 @@ export type OwnerKeyRemovalRefusal =
2044
2090
  | 'last-owner'
2045
2091
  /** The key is this node's own owner key: an owner cannot sign its own row's removal (`A.Key <> old.Key`), so ask another owner key. */
2046
2092
  | 'self'
2047
- /** A re-vouch of an `OwnerKey` row was asked for while this node has no control connection and the party has other members: the re-added row has no re-issue path. Decided at call time, so never planned. */
2093
+ /** A re-vouch of an `OwnerKey` row was asked for while this node reaches no sibling and the party has other members: the re-added row has no re-issue path. Decided at call time, so never planned. */
2048
2094
  | 'alone';
2049
2095
  /** The rows sharing the owner keys' voucher column that a removed owner key strands unless re-vouched first. */
2050
2096
  export interface OwnerKeyDependents {
@@ -2148,8 +2194,8 @@ export interface OwnerGrantRedemptionResult {
2148
2194
  * `ControlDatabase.queryRevocations`: the identity triple plus the `ReissuedAt`
2149
2195
  * counter an owner bumps (`ControlDatabase.reissueRevocations`) to re-write — and
2150
2196
  * therefore re-broadcast — a tombstone that committed while the node was alone.
2151
- * Never the ledger marker: that row names no guarded table, and `queryRevocations`
2152
- * skips it, which is what keeps `tableName` a {@link RevocableTable}.
2197
+ * Never the ledger marker an older party may hold: that row names no guarded table, and
2198
+ * `queryRevocations` skips it, which is what keeps `tableName` a {@link RevocableTable}.
2153
2199
  */
2154
2200
  export interface RevocationRow {
2155
2201
  /** Which guarded table's stamp was retired. */
@@ -2165,13 +2211,6 @@ export interface RevocationRow {
2165
2211
  /** That owner's signature over the tombstone's `'remove'` digest (`Revocation.SignerSig`). */
2166
2212
  signerSig: string;
2167
2213
  }
2168
- /**
2169
- * Outcome of `ControlDatabase.openRevocationLedger`: `'opened'` when this call filed the
2170
- * singleton `Revocation` ledger marker, `'already-open'` when it was already filed (seen
2171
- * by the in-lock guard, or filed first by another owner and refused on the primary key).
2172
- * Either way the marker exists afterwards.
2173
- */
2174
- export type RevocationLedgerOpenResult = 'opened' | 'already-open';
2175
2214
  /**
2176
2215
  * Outcome of {@link CadreNode.registerSelf}, surfaced so callers (e.g. the CLI
2177
2216
  * `--owner` branch) can log what happened:
@@ -2347,6 +2386,8 @@ export interface SeedPeer {
2347
2386
  */
2348
2387
  preferred: boolean;
2349
2388
  }
2389
+ /** A peer a seed nominates for dialing, and the addresses to dial it at (`seedDialTargets`). */
2390
+ export type SeedDialTarget = Pick<SeedPeer, 'peerId' | 'multiaddrs'>;
2350
2391
  /**
2351
2392
  * Control network seed for bootstrapping new nodes.
2352
2393
  * Pre-populates a new node's cache to solve the cold-start problem:
package/dist/types.js CHANGED
@@ -71,10 +71,10 @@ export const HIBERNATION_TIMEOUTS = {
71
71
  * pinging node's monitor sees the reset and aborts the connection exactly as it would on its
72
72
  * own timeout. So this deadline is only real up to the handler timeout of the node being
73
73
  * pinged, which has to stay above it. The deployed relay sets its handler to 60 s for that
74
- * reason (`ops/docker/libp2p-infra/src/main.ts`). Every cadre node's handler is Optimystic's
75
- * bare `ping()`, `@libp2p/ping`'s 10 s, and nothing here can move it
76
- * (`tickets/blocked/optimystic-ping-handler-timeout-below-the-monitor-deadline.md`): between
77
- * two cadre nodes, a ping whose echo takes longer than 10 s still drops the connection.
74
+ * reason (`ops/docker/libp2p-infra/src/main.ts`). Every cadre node's handler is Optimystic's,
75
+ * which derives it from this object: twice `pingTimeout.maxTimeout`, so 60 s
76
+ * (`NodeOptions.pingHandlerTimeoutMs`, unset here, since Optimystic 1.16.0). An app that
77
+ * replaces this object without a `pingTimeout` gets `@libp2p/ping`'s 10 s handler back.
78
78
  *
79
79
  * WHAT THE LINK NEEDS OF IT. One ping opens a fresh stream and echoes over it: a protocol
80
80
  * negotiation plus the echo, two link round trips by `link-budget.ts`'s counts (a `newStream`
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA0FA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA6C;IAC5E,QAAQ,EAAE;QACR,WAAW,EAAE,QAAQ,EAAS,aAAa;QAC3C,gBAAgB,EAAE,QAAQ,EAAI,kBAAkB;QAChD,eAAe,EAAE,QAAQ,EAAK,MAAM;QACpC,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,QAAQ,CAAE,MAAM;KACrC;IACD,WAAW,EAAE;QACX,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,YAAY;QAC1C,gBAAgB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,wBAAwB;QAC1D,eAAe,EAAE,EAAE,GAAG,IAAI,EAAI,mBAAmB;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KACnD;IACD,UAAU,EAAE;QACV,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,WAAW;QACzC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAG,uBAAuB;QACzD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,kBAAkB;QACpD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,gBAAgB;KACxD;IACD,OAAO,EAAE;QACP,WAAW,EAAE,EAAE,GAAG,IAAI,EAAQ,aAAa;QAC3C,gBAAgB,EAAE,EAAE,GAAG,IAAI,EAAG,wBAAwB;QACtD,eAAe,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAG,eAAe;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KAC5D;CACF,CAAC;AAweF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAC,MAAM,CAAC;IACtD,uFAAuF;IACvF,mFAAmF;IACnF,YAAY,EAAE,KAAM;IACpB,kFAAkF;IAClF,kFAAkF;IAClF,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,KAAM,EAAE,UAAU,EAAE,KAAM,EAAE,CAAC;CACjC,CAAC,CAAC;AAmBzC;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,CAAC;AAYnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AAEvC;;;;;GAKG;AACH,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA0GA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA6C;IAC5E,QAAQ,EAAE;QACR,WAAW,EAAE,QAAQ,EAAS,aAAa;QAC3C,gBAAgB,EAAE,QAAQ,EAAI,kBAAkB;QAChD,eAAe,EAAE,QAAQ,EAAK,MAAM;QACpC,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,QAAQ,CAAE,MAAM;KACrC;IACD,WAAW,EAAE;QACX,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,YAAY;QAC1C,gBAAgB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,wBAAwB;QAC1D,eAAe,EAAE,EAAE,GAAG,IAAI,EAAI,mBAAmB;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KACnD;IACD,UAAU,EAAE;QACV,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,WAAW;QACzC,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAG,uBAAuB;QACzD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAI,kBAAkB;QACpD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,gBAAgB;KACxD;IACD,OAAO,EAAE;QACP,WAAW,EAAE,EAAE,GAAG,IAAI,EAAQ,aAAa;QAC3C,gBAAgB,EAAE,EAAE,GAAG,IAAI,EAAG,wBAAwB;QACtD,eAAe,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAG,eAAe;QACjD,oBAAoB,EAAE,CAAC;QACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe;KAC5D;CACF,CAAC;AA6eF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAC,MAAM,CAAC;IACtD,uFAAuF;IACvF,mFAAmF;IACnF,YAAY,EAAE,KAAM;IACpB,kFAAkF;IAClF,kFAAkF;IAClF,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,KAAM,EAAE,UAAU,EAAE,KAAM,EAAE,CAAC;CACjC,CAAC,CAAC;AAmBzC;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,CAAC;AAYnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AAEvC;;;;;GAKG;AACH,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serfab/cadre-core",
3
- "version": "1.17.0",
3
+ "version": "1.19.0",
4
4
  "type": "module",
5
5
  "description": "Core library for Sereus cadre nodes - manages control networks and strand participation",
6
6
  "main": "dist/index.js",
@@ -90,7 +90,7 @@
90
90
  "@libp2p/logger": "^6.2.13",
91
91
  "@libp2p/peer-store": "^12.0.28",
92
92
  "@libp2p/webrtc": "6.0.33",
93
- "@optimystic/db-p2p-storage-fs": "^1.15.0",
93
+ "@optimystic/db-p2p-storage-fs": "^1.19.0",
94
94
  "@types/debug": "^4.1.12",
95
95
  "@types/node": "^25.0.10",
96
96
  "@vitest/coverage-v8": "^4.0.17",
@@ -111,12 +111,12 @@
111
111
  "@multiformats/multiaddr": "^13.0.3",
112
112
  "@noble/ciphers": "^2.0.1",
113
113
  "@noble/hashes": "^2.0.0",
114
- "@optimystic/db-core": "^1.15.0",
115
- "@optimystic/db-p2p": "^1.15.0",
116
- "@optimystic/quereus-plugin-crypto": "^1.15.0",
117
- "@optimystic/quereus-plugin-optimystic": "^1.15.0",
118
- "@quereus/quereus": "^4.20.1",
119
- "@serfab/quereus-plugin-sereus": "^1.17.0",
114
+ "@optimystic/db-core": "^1.19.0",
115
+ "@optimystic/db-p2p": "^1.19.0",
116
+ "@optimystic/quereus-plugin-crypto": "^1.19.0",
117
+ "@optimystic/quereus-plugin-optimystic": "^1.19.0",
118
+ "@quereus/quereus": "^4.20.3",
119
+ "@serfab/quereus-plugin-sereus": "^1.19.0",
120
120
  "debug": "^4.4.3",
121
121
  "libp2p": "^3.3.11",
122
122
  "uint8arrays": "^6.1.1"