@serfab/cadre-core 1.16.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/bootstrap-peer-store-file.js +1 -1
- package/dist/bootstrap-peer-store.d.ts +1 -1
- package/dist/bootstrap-peer-store.js +4 -4
- package/dist/cadre-invite-protocol.d.ts +81 -142
- package/dist/cadre-invite-protocol.js +168 -458
- package/dist/cadre-invite-protocol.js.map +1 -1
- package/dist/cadre-join-request.d.ts +15 -2
- package/dist/cadre-join-request.js +22 -3
- package/dist/cadre-join-request.js.map +1 -1
- package/dist/cadre-node.d.ts +1089 -475
- package/dist/cadre-node.js +2170 -1037
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +36 -14
- package/dist/control-authorization.js +20 -3
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.d.ts +88 -32
- package/dist/control-cohort.js +91 -33
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +856 -318
- package/dist/control-database.js +1422 -701
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +46 -43
- package/dist/control-formation-recorder.js +77 -78
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-protocol-guard.d.ts +11 -4
- package/dist/control-protocol-guard.js +20 -5
- package/dist/control-protocol-guard.js.map +1 -1
- package/dist/control-schema-version.d.ts +97 -0
- package/dist/control-schema-version.js +225 -0
- package/dist/control-schema-version.js.map +1 -0
- package/dist/control-schema.d.ts +45 -5
- package/dist/control-schema.js +262 -78
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +45 -10
- package/dist/control-write-retry.js +92 -22
- package/dist/control-write-retry.js.map +1 -1
- package/dist/delegate-admission.d.ts +13 -0
- package/dist/delegate-admission.js +27 -0
- package/dist/delegate-admission.js.map +1 -1
- package/dist/ed25519-key.d.ts +10 -7
- package/dist/ed25519-key.js +23 -20
- package/dist/ed25519-key.js.map +1 -1
- package/dist/edge-node/config.d.ts +122 -0
- package/dist/edge-node/config.js +199 -0
- package/dist/edge-node/config.js.map +1 -0
- package/dist/edge-node/dissolve.d.ts +95 -0
- package/dist/edge-node/dissolve.js +184 -0
- package/dist/edge-node/dissolve.js.map +1 -0
- package/dist/edge-node/edge-join.d.ts +44 -0
- package/dist/edge-node/edge-join.js +227 -0
- package/dist/edge-node/edge-join.js.map +1 -0
- package/dist/edge-node/edge-node.d.ts +295 -0
- package/dist/edge-node/edge-node.js +704 -0
- package/dist/edge-node/edge-node.js.map +1 -0
- package/dist/edge-node/index.d.ts +18 -0
- package/dist/edge-node/index.js +18 -0
- package/dist/edge-node/index.js.map +1 -0
- package/dist/edge-node/join-credential.d.ts +11 -0
- package/dist/edge-node/join-credential.js +11 -0
- package/dist/edge-node/join-credential.js.map +1 -0
- package/dist/edge-node/lazy-raw-storage.d.ts +40 -0
- package/dist/edge-node/lazy-raw-storage.js +134 -0
- package/dist/edge-node/lazy-raw-storage.js.map +1 -0
- package/dist/edge-node/node-local.d.ts +60 -0
- package/dist/edge-node/node-local.js +58 -0
- package/dist/edge-node/node-local.js.map +1 -0
- package/dist/edge-node/options.d.ts +83 -0
- package/dist/edge-node/options.js +110 -0
- package/dist/edge-node/options.js.map +1 -0
- package/dist/edge-node/owner-grant.d.ts +41 -0
- package/dist/edge-node/owner-grant.js +38 -0
- package/dist/edge-node/owner-grant.js.map +1 -0
- package/dist/edge-node/platform.d.ts +140 -0
- package/dist/edge-node/platform.js +12 -0
- package/dist/edge-node/platform.js.map +1 -0
- package/dist/edge-node/strand-move.d.ts +115 -0
- package/dist/edge-node/strand-move.js +223 -0
- package/dist/edge-node/strand-move.js.map +1 -0
- package/dist/edge-node/strands.d.ts +28 -0
- package/dist/edge-node/strands.js +60 -0
- package/dist/edge-node/strands.js.map +1 -0
- package/dist/enrolled-machine-store.d.ts +3 -3
- package/dist/enrolled-machine-store.js +3 -3
- package/dist/formation-token.d.ts +48 -0
- package/dist/formation-token.js +26 -0
- package/dist/formation-token.js.map +1 -0
- package/dist/index.d.ts +27 -17
- package/dist/index.js +39 -17
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +29 -12
- package/dist/joined-strand-store.js +66 -27
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store.d.ts +9 -4
- package/dist/key-store.js +9 -4
- package/dist/key-store.js.map +1 -1
- package/dist/link-budget.d.ts +9 -7
- package/dist/link-budget.js +9 -7
- package/dist/link-budget.js.map +1 -1
- package/dist/machine-removal.d.ts +19 -39
- package/dist/machine-removal.js +24 -98
- package/dist/machine-removal.js.map +1 -1
- package/dist/member-redemption.d.ts +256 -0
- package/dist/member-redemption.js +490 -0
- package/dist/member-redemption.js.map +1 -0
- package/dist/membership-view.d.ts +91 -0
- package/dist/membership-view.js +110 -0
- package/dist/membership-view.js.map +1 -0
- package/dist/owner-anchor-sync.d.ts +4 -4
- package/dist/owner-anchor-sync.js +16 -15
- package/dist/owner-anchor-sync.js.map +1 -1
- package/dist/owner-grant-protocol.d.ts +251 -0
- package/dist/owner-grant-protocol.js +344 -0
- package/dist/owner-grant-protocol.js.map +1 -0
- package/dist/owner-key-export.d.ts +70 -0
- package/dist/owner-key-export.js +279 -0
- package/dist/owner-key-export.js.map +1 -0
- package/dist/owner-key-flows.d.ts +55 -0
- package/dist/owner-key-flows.js +60 -0
- package/dist/owner-key-flows.js.map +1 -0
- package/dist/owner-key-store.d.ts +31 -0
- package/dist/owner-key-store.js +81 -0
- package/dist/owner-key-store.js.map +1 -0
- package/dist/owner-keys.d.ts +78 -0
- package/dist/owner-keys.js +133 -0
- package/dist/owner-keys.js.map +1 -0
- package/dist/owner-signer.d.ts +54 -0
- package/dist/owner-signer.js +51 -0
- package/dist/owner-signer.js.map +1 -0
- package/dist/peer-addr-book.d.ts +10 -1
- package/dist/peer-addr-book.js +4 -3
- package/dist/peer-addr-book.js.map +1 -1
- package/dist/peer-authorization.d.ts +83 -18
- package/dist/peer-authorization.js +123 -53
- package/dist/peer-authorization.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +89 -20
- package/dist/peer-join-backfill.js +265 -108
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/pending-join-runner.d.ts +48 -18
- package/dist/pending-join-runner.js +74 -56
- package/dist/pending-join-runner.js.map +1 -1
- package/dist/provider/rent-node.js +1 -1
- package/dist/provider/rent-node.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +162 -133
- package/dist/seed-bootstrap.js +300 -283
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.js +4 -4
- package/dist/strand-addr-protocol.d.ts +4 -1
- package/dist/strand-addr-protocol.js +8 -2
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-cohort.d.ts +3 -0
- package/dist/strand-cohort.js.map +1 -1
- package/dist/strand-database.d.ts +24 -2
- package/dist/strand-database.js +29 -6
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +176 -88
- package/dist/strand-first-sync-gate.js +288 -121
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-deadlines.d.ts +9 -9
- package/dist/strand-formation-deadlines.js +9 -9
- package/dist/strand-formation-manager.d.ts +23 -17
- package/dist/strand-formation-manager.js +46 -44
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +5 -8
- package/dist/strand-formation-protocol.js +101 -16
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +27 -18
- package/dist/strand-instance-manager.js +119 -50
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-reconciler.d.ts +16 -13
- package/dist/strand-membership-reconciler.js +30 -22
- package/dist/strand-membership-reconciler.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +45 -22
- package/dist/strand-membership-writer.js +226 -131
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-move.d.ts +6 -5
- package/dist/strand-move.js +36 -32
- package/dist/strand-move.js.map +1 -1
- package/dist/strand-peer-redial.js +2 -3
- package/dist/strand-peer-redial.js.map +1 -1
- package/dist/strand-revocation-enforcer.d.ts +134 -60
- package/dist/strand-revocation-enforcer.js +144 -71
- package/dist/strand-revocation-enforcer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +50 -39
- package/dist/strand-solicitation.js +10 -3
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-watcher.d.ts +80 -13
- package/dist/strand-watcher.js +152 -48
- package/dist/strand-watcher.js.map +1 -1
- package/dist/table-change-watch.d.ts +63 -0
- package/dist/table-change-watch.js +209 -0
- package/dist/table-change-watch.js.map +1 -0
- package/dist/timeout-scheduler.d.ts +4 -3
- package/dist/timeout-scheduler.js +4 -3
- package/dist/timeout-scheduler.js.map +1 -1
- package/dist/types.d.ts +374 -150
- package/dist/types.js +4 -4
- package/dist/types.js.map +1 -1
- package/package.json +17 -8
- package/src/bootstrap-peer-store-file.ts +44 -44
- package/src/bootstrap-peer-store.ts +214 -214
- package/src/cadre-invite-protocol.ts +740 -1052
- package/src/cadre-join-request.ts +24 -3
- package/src/cadre-node.ts +2402 -1079
- package/src/control-authorization.ts +47 -14
- package/src/control-cohort.ts +204 -128
- package/src/control-database.ts +1749 -820
- package/src/control-formation-recorder.ts +359 -353
- package/src/control-protocol-guard.ts +23 -6
- package/src/control-schema-version.ts +301 -0
- package/src/control-schema.ts +273 -78
- package/src/control-write-retry.ts +641 -567
- package/src/delegate-admission.ts +351 -314
- package/src/ed25519-key.ts +174 -171
- package/src/edge-node/config.ts +277 -0
- package/src/edge-node/dissolve.ts +251 -0
- package/src/edge-node/edge-join.ts +319 -0
- package/src/edge-node/edge-node.ts +1045 -0
- package/src/edge-node/index.ts +59 -0
- package/src/edge-node/join-credential.ts +16 -0
- package/src/edge-node/lazy-raw-storage.ts +178 -0
- package/src/edge-node/node-local.ts +84 -0
- package/src/edge-node/options.ts +179 -0
- package/src/edge-node/owner-grant.ts +63 -0
- package/src/edge-node/platform.ts +151 -0
- package/src/edge-node/strand-move.ts +270 -0
- package/src/edge-node/strands.ts +72 -0
- package/src/enrolled-machine-store.ts +3 -3
- package/src/formation-token.ts +63 -0
- package/src/index.ts +899 -789
- package/src/joined-strand-store.ts +73 -28
- package/src/key-store.ts +10 -4
- package/src/link-budget.ts +9 -7
- package/src/machine-removal.ts +26 -111
- package/src/member-redemption.ts +649 -0
- package/src/membership-view.ts +144 -0
- package/src/owner-anchor-sync.ts +19 -18
- package/src/owner-grant-protocol.ts +547 -0
- package/src/owner-key-export.ts +348 -0
- package/src/owner-key-flows.ts +109 -0
- package/src/owner-key-store.ts +85 -0
- package/src/owner-keys.ts +178 -0
- package/src/owner-signer.ts +94 -0
- package/src/peer-addr-book.ts +4 -3
- package/src/peer-authorization.ts +558 -488
- package/src/peer-join-backfill.ts +971 -761
- package/src/pending-join-runner.ts +76 -53
- package/src/provider/rent-node.ts +1 -1
- package/src/seed-bootstrap.ts +343 -349
- package/src/seed-trust-policy.ts +4 -4
- package/src/strand-addr-protocol.ts +547 -538
- package/src/strand-cohort.ts +4 -0
- package/src/strand-database.ts +352 -319
- package/src/strand-first-sync-gate.ts +334 -133
- package/src/strand-formation-deadlines.ts +227 -227
- package/src/strand-formation-manager.ts +731 -725
- package/src/strand-formation-protocol.ts +122 -24
- package/src/strand-instance-manager.ts +2092 -2026
- package/src/strand-membership-reconciler.ts +33 -25
- package/src/strand-membership-writer.ts +2093 -1974
- package/src/strand-move.ts +50 -38
- package/src/strand-peer-redial.ts +2 -3
- package/src/strand-revocation-enforcer.ts +740 -654
- package/src/strand-solicitation.ts +61 -34
- package/src/strand-watcher.ts +175 -56
- package/src/table-change-watch.ts +285 -0
- package/src/timeout-scheduler.ts +29 -28
- package/src/types.ts +391 -152
package/src/strand-database.ts
CHANGED
|
@@ -1,319 +1,352 @@
|
|
|
1
|
-
import debug from 'debug';
|
|
2
|
-
import { Database } from '@quereus/quereus';
|
|
3
|
-
import { applyAppSchema, connectToStrand } from '@serfab/quereus-plugin-sereus';
|
|
4
|
-
import type { SereusPluginResult } from '@serfab/quereus-plugin-sereus';
|
|
5
|
-
import type { Libp2p } from '@libp2p/interface';
|
|
6
|
-
import type { IRepo } from '@optimystic/db-core';
|
|
7
|
-
import type { SAppConfig } from './types.js';
|
|
8
|
-
import { bootstrapFounderMembership } from './strand-membership-writer.js';
|
|
9
|
-
import { strandMemberKeyPair } from './strand-member-key.js';
|
|
10
|
-
import { StrandCommitRecord } from './strand-commit-durability.js';
|
|
11
|
-
import type { StrandCommitDurability, StrandCommitListeners } from './strand-commit-durability.js';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*
|
|
47
|
-
* `
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
*
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
*
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
'
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
return
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
*
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
1
|
+
import debug from 'debug';
|
|
2
|
+
import { Database } from '@quereus/quereus';
|
|
3
|
+
import { applyAppSchema, connectToStrand } from '@serfab/quereus-plugin-sereus';
|
|
4
|
+
import type { SereusPluginResult } from '@serfab/quereus-plugin-sereus';
|
|
5
|
+
import type { Libp2p } from '@libp2p/interface';
|
|
6
|
+
import type { IRepo } from '@optimystic/db-core';
|
|
7
|
+
import type { SAppConfig } from './types.js';
|
|
8
|
+
import { bootstrapFounderMembership } from './strand-membership-writer.js';
|
|
9
|
+
import { strandMemberKeyPair } from './strand-member-key.js';
|
|
10
|
+
import { StrandCommitRecord } from './strand-commit-durability.js';
|
|
11
|
+
import type { StrandCommitDurability, StrandCommitListeners } from './strand-commit-durability.js';
|
|
12
|
+
import { TableWatchRegistry } from './table-change-watch.js';
|
|
13
|
+
import type { TableChangeWatch, TableChangeWatchOptions } from './table-change-watch.js';
|
|
14
|
+
|
|
15
|
+
const log = debug('sereus:cadre:strand-db');
|
|
16
|
+
const timing = debug('sereus:cadre:timing');
|
|
17
|
+
|
|
18
|
+
export interface StrandDatabaseConfig extends StrandCommitListeners {
|
|
19
|
+
/** The strand ID */
|
|
20
|
+
strandId: string;
|
|
21
|
+
/**
|
|
22
|
+
* sApp configuration containing the schema. Absent for a storage replica — a strand
|
|
23
|
+
* this node stores and serves without the app installed: only the `Strand` membership
|
|
24
|
+
* schema is applied, and such a database can never found (see {@link founderSApp}).
|
|
25
|
+
*/
|
|
26
|
+
sAppConfig?: SAppConfig;
|
|
27
|
+
/** Libp2p node for the strand network */
|
|
28
|
+
libp2pNode: Libp2p;
|
|
29
|
+
/** Coordinated repo from the libp2p node */
|
|
30
|
+
coordinatedRepo: IRepo;
|
|
31
|
+
/**
|
|
32
|
+
* Strand type (`'o'` open / `'c'` closed) from the control-network strand row.
|
|
33
|
+
* Drives the founder bootstrap: open strands get a `Header` only; closed strands
|
|
34
|
+
* also get the founding `Member`+`Manager`.
|
|
35
|
+
*/
|
|
36
|
+
strandType: 'o' | 'c';
|
|
37
|
+
/**
|
|
38
|
+
* The closed-strand `MemberPrivateKey` (base64 protobuf) from the strand row —
|
|
39
|
+
* the strand-wide read secret every joining party receives. Carried for the
|
|
40
|
+
* strand's read-gating story only; it derives NOBODY's identity (that used to be
|
|
41
|
+
* this key's second job, which let any joiner sign as the founding manager —
|
|
42
|
+
* gotchoices/sereus#4). Absent for open strands.
|
|
43
|
+
*/
|
|
44
|
+
memberPrivateKey?: string;
|
|
45
|
+
/**
|
|
46
|
+
* THIS party's own strand membership private key (base64 protobuf), from the
|
|
47
|
+
* control-layer `StrandPartyKey` row (or supplied explicitly at attach). Required
|
|
48
|
+
* when `founder` is true and `strandType` is `'c'` — it derives the founding
|
|
49
|
+
* `Member.Key`/`Manager.MemberKey`. Never shared outside the party; absent for
|
|
50
|
+
* open strands and for joiners that have not yet persisted one.
|
|
51
|
+
*/
|
|
52
|
+
partyMemberPrivateKey?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Whether this node founds the strand. When true, {@link initialize} runs the
|
|
55
|
+
* one-time founder membership bootstrap after the schema is applied. Joiners
|
|
56
|
+
* leave this false and write nothing (rows arrive via sync). Defaults to false.
|
|
57
|
+
*/
|
|
58
|
+
founder?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Whether a closed strand's founder bootstrap also writes this machine's `Strand.MemberPeer`
|
|
61
|
+
* binding (for `libp2pNode`'s peer id) in the founding transaction. That binding is the
|
|
62
|
+
* membership reconciler's job; the founding run that seats the founding `Member` writes it
|
|
63
|
+
* too, which saves the reconciler's commit. False whenever the reconciler is disarmed, so a
|
|
64
|
+
* fixture that hand-drives the membership writers sees only the rows it wrote. Defaults to false.
|
|
65
|
+
*/
|
|
66
|
+
bindFounderPeer?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* StrandDatabase manages the sApp schema for a strand using Quereus with the
|
|
71
|
+
* Optimystic backend. Each strand instance has its own isolated database with
|
|
72
|
+
* the sApp's schema applied.
|
|
73
|
+
*
|
|
74
|
+
* This class owns the `Database` lifecycle (creation, `getDatabase()`, `close()`)
|
|
75
|
+
* but delegates the actual SQL-surface composition — plugin registration, node
|
|
76
|
+
* wiring, catalog hydration, schema apply — to `connectToStrand` from
|
|
77
|
+
* `@serfab/quereus-plugin-sereus`, the single shared composition. The libp2p
|
|
78
|
+
* node is injected here, so `connectToStrand` neither creates nor stops it:
|
|
79
|
+
* `StrandInstanceManager` owns the node lifecycle, and a failed or closed
|
|
80
|
+
* database leaves the node running.
|
|
81
|
+
*
|
|
82
|
+
* One instance is one attempt: a failed {@link initialize} is closed and a new
|
|
83
|
+
* instance built for the next attempt.
|
|
84
|
+
*/
|
|
85
|
+
export class StrandDatabase {
|
|
86
|
+
private db: Database | null = null;
|
|
87
|
+
private shutdownStrand: SereusPluginResult['shutdown'] | null = null;
|
|
88
|
+
private resolvedTransactor: SereusPluginResult['transactor'] | null = null;
|
|
89
|
+
private createEmptyCollections: SereusPluginResult['createEmptyCollections'] | null = null;
|
|
90
|
+
private readonly config: StrandDatabaseConfig;
|
|
91
|
+
private initialized = false;
|
|
92
|
+
private readonly commits: StrandCommitRecord;
|
|
93
|
+
private unsubscribeCommits: (() => void) | null = null;
|
|
94
|
+
/** Subscriptions handed out by {@link watchTables}. */
|
|
95
|
+
private readonly tableWatches = new TableWatchRegistry();
|
|
96
|
+
|
|
97
|
+
constructor(config: StrandDatabaseConfig) {
|
|
98
|
+
this.config = config;
|
|
99
|
+
this.commits = new StrandCommitRecord(config.strandId, config);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Initialize the database — create the `Database` and delegate the strand
|
|
104
|
+
* SQL-surface composition (plugins, node wiring, hydrate, schema apply) to
|
|
105
|
+
* `connectToStrand` with the injected libp2p node.
|
|
106
|
+
*/
|
|
107
|
+
async initialize(): Promise<void> {
|
|
108
|
+
if (this.initialized) {
|
|
109
|
+
log('StrandDatabase for strand %s already initialized', this.config.strandId);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const sid = this.config.strandId;
|
|
114
|
+
const { sAppConfig } = this.config;
|
|
115
|
+
if (this.config.founder === true) {
|
|
116
|
+
// Before composing anything: a founder that cannot write its Header must not
|
|
117
|
+
// leave a half-built connection behind.
|
|
118
|
+
this.founderSApp();
|
|
119
|
+
}
|
|
120
|
+
log('Initializing StrandDatabase for strand: %s (%s)', sid,
|
|
121
|
+
sAppConfig ? `sApp: ${sAppConfig.id} v${sAppConfig.version}` : 'storage replica');
|
|
122
|
+
|
|
123
|
+
this.db = new Database();
|
|
124
|
+
|
|
125
|
+
// Delegate to the shared composition. The node is injected, so:
|
|
126
|
+
// - `connectToStrand` never creates or stops a node; `StrandInstanceManager`
|
|
127
|
+
// owns the node lifecycle;
|
|
128
|
+
// - no `storage` is passed: the plugin consumes it only to build a local
|
|
129
|
+
// transactor's raw-storage factory or to create a node it was not given,
|
|
130
|
+
// and cadre-core always injects the node and runs the network transactor.
|
|
131
|
+
// (The plugin keeps its `storage` option for the browser entry point.)
|
|
132
|
+
const t0 = performance.now();
|
|
133
|
+
//
|
|
134
|
+
// A storage replica passes no schema, so no `App` tables are declared. A warm restart's
|
|
135
|
+
// hydrate may still load `App` table definitions persisted in the replicated store into
|
|
136
|
+
// the catalog — declarative metadata, nothing the app supplied runs — and the first-sync
|
|
137
|
+
// probe then reads those tables once from blocks this node holds.
|
|
138
|
+
const result = await connectToStrand(this.db, {
|
|
139
|
+
strandId: sid,
|
|
140
|
+
schema: sAppConfig?.schema,
|
|
141
|
+
libp2pNode: this.config.libp2pNode,
|
|
142
|
+
coordinatedRepo: this.config.coordinatedRepo,
|
|
143
|
+
enableCache: true,
|
|
144
|
+
});
|
|
145
|
+
this.shutdownStrand = result.shutdown;
|
|
146
|
+
this.resolvedTransactor = result.transactor;
|
|
147
|
+
this.createEmptyCollections = result.createEmptyCollections;
|
|
148
|
+
// Before the founder bootstrap, so its commits are reported like any other.
|
|
149
|
+
this.subscribeToCommits(result);
|
|
150
|
+
timing('[strandDb:%s] connectToStrand: %dms (hydrated tables=%d, indexes=%d)',
|
|
151
|
+
sid, Math.round(performance.now() - t0),
|
|
152
|
+
result.hydrated?.tables ?? 0, result.hydrated?.indexes ?? 0);
|
|
153
|
+
|
|
154
|
+
// Founder-only: write the one-time membership bootstrap now that the schema is
|
|
155
|
+
// applied (connectToStrand has returned). A throw here propagates out of
|
|
156
|
+
// initialize(), and the caller closes this half-built database (close() runs the
|
|
157
|
+
// composition's shutdown). Joiners skip this — their rows arrive via sync.
|
|
158
|
+
if (this.config.founder === true) {
|
|
159
|
+
await this.bootstrapFounder();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.initialized = true;
|
|
163
|
+
log('StrandDatabase for strand %s initialized successfully', sid);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private subscribeToCommits(result: SereusPluginResult): void {
|
|
167
|
+
const unsubscribeDurability = result.onCommitDurability(report => this.commits.reportCommit(report));
|
|
168
|
+
const unsubscribeHeld = result.onCommitFullyHeld(event => this.commits.reportHeld(event));
|
|
169
|
+
this.unsubscribeCommits = () => {
|
|
170
|
+
unsubscribeDurability();
|
|
171
|
+
unsubscribeHeld();
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Run the founder membership bootstrap against the freshly-composed strand DB.
|
|
177
|
+
*
|
|
178
|
+
* Derives the founding keypair from this PARTY's own `partyMemberPrivateKey` (the
|
|
179
|
+
* `Member.Key`/`Manager.MemberKey` are its public key) — deliberately NOT from the
|
|
180
|
+
* strand row's shared `MemberPrivateKey`, which every joining party receives and could
|
|
181
|
+
* therefore use to forge the founder's identity. A closed strand with no
|
|
182
|
+
* `partyMemberPrivateKey` throws, because it could never seat a founding manager.
|
|
183
|
+
* The shared key's public half IS passed, but only so the bootstrap can refuse a strand
|
|
184
|
+
* founded under it before the split (`PreSplitStrandIdentityError`). The strand node's
|
|
185
|
+
* peer id is passed under {@link StrandDatabaseConfig.bindFounderPeer}: the same node, so
|
|
186
|
+
* the same id, the membership reconciler binds. Open strands derive no keypair (Header
|
|
187
|
+
* only). Idempotent — see {@link bootstrapFounderMembership}.
|
|
188
|
+
*/
|
|
189
|
+
private async bootstrapFounder(): Promise<void> {
|
|
190
|
+
const { strandId, strandType, memberPrivateKey, partyMemberPrivateKey, libp2pNode, bindFounderPeer } = this.config;
|
|
191
|
+
const closed = strandType === 'c';
|
|
192
|
+
await bootstrapFounderMembership(this.db!, {
|
|
193
|
+
strandId,
|
|
194
|
+
type: strandType,
|
|
195
|
+
sApp: this.founderSApp(),
|
|
196
|
+
founderKeyPair: closed ? this.deriveFounderKeyPair(strandId, partyMemberPrivateKey) : undefined,
|
|
197
|
+
sharedMemberPublicKey: closed && memberPrivateKey
|
|
198
|
+
? strandMemberKeyPair(memberPrivateKey).publicKeyB64
|
|
199
|
+
: undefined,
|
|
200
|
+
founderPeerId: closed && bindFounderPeer === true ? libp2pNode.peerId.toString() : undefined,
|
|
201
|
+
createEmptyCollections: this.createEmptyCollections!,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* The sApp a founder bootstrap records in the `Strand.Header`. Throws for a storage
|
|
207
|
+
* replica (no sApp config): its Header's sApp columns would have nothing to say.
|
|
208
|
+
* `CadreNode` always launches a replica as a joiner, so this guards a future caller.
|
|
209
|
+
*/
|
|
210
|
+
private founderSApp(): SAppConfig {
|
|
211
|
+
const { sAppConfig, strandId } = this.config;
|
|
212
|
+
if (!sAppConfig) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`Cannot found strand ${strandId} as a storage replica: the founder bootstrap writes ` +
|
|
215
|
+
'the sApp id and version into Strand.Header, and a replica has no sApp config.',
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return sAppConfig;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Derive the founding keypair for a closed strand from the party's own
|
|
223
|
+
* `partyMemberPrivateKey`, failing loudly when the key is absent (a closed strand
|
|
224
|
+
* with no founding Manager can never admit anyone).
|
|
225
|
+
*/
|
|
226
|
+
private deriveFounderKeyPair(strandId: string, partyMemberPrivateKey: string | undefined) {
|
|
227
|
+
if (!partyMemberPrivateKey) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`Cannot found closed strand ${strandId}: this party has no StrandPartyKey for it. ` +
|
|
230
|
+
'A closed strand needs a founding Member/Manager derived from the party\'s own ' +
|
|
231
|
+
'membership key (minted when the strand is published, or at the founder launch for ' +
|
|
232
|
+
'a closed row stored without one) — the shared MemberPrivateKey deliberately ' +
|
|
233
|
+
'no longer derives anyone\'s identity.',
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return strandMemberKeyPair(partyMemberPrivateKey);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Run the founder membership bootstrap against an ALREADY-LIVE database — the
|
|
241
|
+
* seam {@link StrandInstanceManager.foundExistingStrand} uses when a founder
|
|
242
|
+
* request arrives for an instance that was first launched as a joiner.
|
|
243
|
+
* Idempotent: every bootstrap write is insert-if-absent
|
|
244
|
+
* ({@link bootstrapFounderMembership}), so calling it on an instance that
|
|
245
|
+
* already founded writes nothing. On success also flips the captured config's
|
|
246
|
+
* `founder`, so this object's own record of how it was launched stays coherent
|
|
247
|
+
* with what actually ran (a construction-time `founder: false` is a statement
|
|
248
|
+
* about the launch, not a permanent identity); a refused bootstrap leaves it a joiner.
|
|
249
|
+
*
|
|
250
|
+
* @param partyMemberPrivateKey - The party's own membership key, for a closed strand
|
|
251
|
+
* whose original (joiner) launch resolved none — e.g. the `StrandPartyKey` row had
|
|
252
|
+
* not been written or replicated yet. The captured config's key wins when both
|
|
253
|
+
* exist: it is the identity this instance launched under.
|
|
254
|
+
*/
|
|
255
|
+
async ensureFounderBootstrap(partyMemberPrivateKey?: string): Promise<void> {
|
|
256
|
+
this.ensureInitialized();
|
|
257
|
+
this.config.partyMemberPrivateKey ??= partyMemberPrivateKey;
|
|
258
|
+
await this.bootstrapFounder();
|
|
259
|
+
this.config.founder = true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Give a live storage replica the app's schema: apply `App` to the already-composed
|
|
264
|
+
* database — same libp2p node, same store, so the new tables read the blocks this node
|
|
265
|
+
* already holds — and record the sApp, so a later {@link ensureFounderBootstrap} has it
|
|
266
|
+
* for the `Header`. Idempotent: the apply is a declarative diff, so re-applying the same
|
|
267
|
+
* schema emits nothing. A failed apply records nothing.
|
|
268
|
+
*/
|
|
269
|
+
async attachAppSchema(sAppConfig: SAppConfig): Promise<void> {
|
|
270
|
+
this.ensureInitialized();
|
|
271
|
+
await applyAppSchema(this.db!, sAppConfig.schema);
|
|
272
|
+
this.config.sAppConfig = sAppConfig;
|
|
273
|
+
log('StrandDatabase for strand %s attached sApp %s v%s', this.config.strandId, sAppConfig.id, sAppConfig.version);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Get the underlying database for queries
|
|
278
|
+
*/
|
|
279
|
+
getDatabase(): Database {
|
|
280
|
+
this.ensureInitialized();
|
|
281
|
+
return this.db!;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Subscribe `onChange` to changes in this strand's tables (`'Strand.Member'`, …): commits
|
|
286
|
+
* made here and changes replicated into this node's storage alike, on the single-flight
|
|
287
|
+
* schedule `table-change-watch.ts` describes. {@link close} disposes every subscription
|
|
288
|
+
* still live; a hibernated strand's database is closed and rebuilt on wake, so a consumer
|
|
289
|
+
* subscribes again on the new instance.
|
|
290
|
+
*/
|
|
291
|
+
watchTables(tables: readonly string[], onChange: () => Promise<void>, options: TableChangeWatchOptions): TableChangeWatch {
|
|
292
|
+
this.ensureInitialized();
|
|
293
|
+
return this.tableWatches.watch(this.db!, tables, onChange, options);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* The durability of a recent commit on this database, by its `StampId()` — the id a single
|
|
298
|
+
* `insert … returning StampId() as Tx` statement returns. Read it after that statement
|
|
299
|
+
* resolves: the plugin reports the commit before the statement returns, and a commit already
|
|
300
|
+
* held everywhere is marked held by then too. `undefined` for a commit that wrote nothing, one
|
|
301
|
+
* made before this database was built, or one more than 256 commits back. Each value is a
|
|
302
|
+
* snapshot: read again after `strand:commit-held` to see the change.
|
|
303
|
+
*/
|
|
304
|
+
commitDurability(transactionId: string): StrandCommitDurability | undefined {
|
|
305
|
+
this.ensureInitialized();
|
|
306
|
+
return this.commits.get(transactionId);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The Optimystic transactor this strand's connection resolved to — always
|
|
311
|
+
* `'network'` here, since cadre-core passes no `transactor` option and takes
|
|
312
|
+
* the plugin's default. Exposed so a spec measuring or asserting the network
|
|
313
|
+
* path (`strand-solo-write-budget.spec.ts`) can pin the engine it ran on
|
|
314
|
+
* rather than assume it.
|
|
315
|
+
*/
|
|
316
|
+
getTransactor(): SereusPluginResult['transactor'] {
|
|
317
|
+
this.ensureInitialized();
|
|
318
|
+
return this.resolvedTransactor!;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Close the database and cleanup resources. Runs the strand-connection
|
|
323
|
+
* shutdown (collection-factory teardown; the injected node keeps running),
|
|
324
|
+
* then closes the `Database`. Safe on an instance whose {@link initialize}
|
|
325
|
+
* failed part-way.
|
|
326
|
+
*/
|
|
327
|
+
async close(): Promise<void> {
|
|
328
|
+
// First, so an event from the closing connection reaches no app handler or table watch.
|
|
329
|
+
this.unsubscribeCommits?.();
|
|
330
|
+
this.unsubscribeCommits = null;
|
|
331
|
+
this.tableWatches.disposeAll();
|
|
332
|
+
if (this.shutdownStrand) {
|
|
333
|
+
await this.shutdownStrand();
|
|
334
|
+
this.shutdownStrand = null;
|
|
335
|
+
}
|
|
336
|
+
// Again: a subscription taken during the await above would keep its timers past close.
|
|
337
|
+
this.tableWatches.disposeAll();
|
|
338
|
+
if (this.db) {
|
|
339
|
+
void this.db.close();
|
|
340
|
+
this.db = null;
|
|
341
|
+
}
|
|
342
|
+
this.resolvedTransactor = null;
|
|
343
|
+
this.initialized = false;
|
|
344
|
+
log('StrandDatabase for strand %s closed', this.config.strandId);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
private ensureInitialized(): void {
|
|
348
|
+
if (!this.initialized || !this.db) {
|
|
349
|
+
throw new Error(`StrandDatabase for strand ${this.config.strandId} not initialized. Call initialize() first.`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|