@serfab/cadre-core 0.6.0 → 0.8.1

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 (166) hide show
  1. package/README.md +9 -8
  2. package/dist/authority-key.d.ts +41 -0
  3. package/dist/authority-key.js +52 -0
  4. package/dist/authority-key.js.map +1 -0
  5. package/dist/cadre-node.d.ts +777 -11
  6. package/dist/cadre-node.js +1912 -82
  7. package/dist/cadre-node.js.map +1 -1
  8. package/dist/canonical-datetime.d.ts +33 -0
  9. package/dist/canonical-datetime.js +38 -0
  10. package/dist/canonical-datetime.js.map +1 -0
  11. package/dist/canonical-json.d.ts +12 -0
  12. package/dist/canonical-json.js +33 -0
  13. package/dist/canonical-json.js.map +1 -0
  14. package/dist/control-cohort.d.ts +63 -0
  15. package/dist/control-cohort.js +80 -0
  16. package/dist/control-cohort.js.map +1 -0
  17. package/dist/control-database.d.ts +259 -5
  18. package/dist/control-database.js +541 -128
  19. package/dist/control-database.js.map +1 -1
  20. package/dist/control-formation-recorder.d.ts +89 -0
  21. package/dist/control-formation-recorder.js +139 -0
  22. package/dist/control-formation-recorder.js.map +1 -0
  23. package/dist/control-schema.d.ts +12 -0
  24. package/dist/control-schema.js +199 -0
  25. package/dist/control-schema.js.map +1 -0
  26. package/dist/control-stream.d.ts +54 -0
  27. package/dist/control-stream.js +104 -0
  28. package/dist/control-stream.js.map +1 -0
  29. package/dist/device-token.d.ts +50 -0
  30. package/dist/device-token.js +66 -0
  31. package/dist/device-token.js.map +1 -0
  32. package/dist/diagnostics/connection-path.d.ts +118 -0
  33. package/dist/diagnostics/connection-path.js +204 -0
  34. package/dist/diagnostics/connection-path.js.map +1 -0
  35. package/dist/diagnostics/webrtc-turn-tracker.d.ts +50 -0
  36. package/dist/diagnostics/webrtc-turn-tracker.js +196 -0
  37. package/dist/diagnostics/webrtc-turn-tracker.js.map +1 -0
  38. package/dist/hibernation-manager.d.ts +79 -0
  39. package/dist/hibernation-manager.js +173 -23
  40. package/dist/hibernation-manager.js.map +1 -1
  41. package/dist/index.d.ts +25 -4
  42. package/dist/index.js +59 -4
  43. package/dist/index.js.map +1 -1
  44. package/dist/key-store-file.d.ts +52 -0
  45. package/dist/key-store-file.js +194 -0
  46. package/dist/key-store-file.js.map +1 -0
  47. package/dist/key-store.d.ts +75 -0
  48. package/dist/key-store.js +66 -0
  49. package/dist/key-store.js.map +1 -0
  50. package/dist/peer-authorization.d.ts +26 -0
  51. package/dist/peer-authorization.js +40 -0
  52. package/dist/peer-authorization.js.map +1 -0
  53. package/dist/peer-record.d.ts +89 -0
  54. package/dist/peer-record.js +117 -0
  55. package/dist/peer-record.js.map +1 -0
  56. package/dist/push-credentials.d.ts +34 -0
  57. package/dist/push-credentials.js +81 -0
  58. package/dist/push-credentials.js.map +1 -0
  59. package/dist/push-fanout.d.ts +137 -0
  60. package/dist/push-fanout.js +197 -0
  61. package/dist/push-fanout.js.map +1 -0
  62. package/dist/push-notifier-apns.d.ts +53 -0
  63. package/dist/push-notifier-apns.js +195 -0
  64. package/dist/push-notifier-apns.js.map +1 -0
  65. package/dist/push-notifier-fcm.d.ts +38 -0
  66. package/dist/push-notifier-fcm.js +183 -0
  67. package/dist/push-notifier-fcm.js.map +1 -0
  68. package/dist/push-notifier-shared.d.ts +18 -0
  69. package/dist/push-notifier-shared.js +25 -0
  70. package/dist/push-notifier-shared.js.map +1 -0
  71. package/dist/push-notifier.d.ts +71 -0
  72. package/dist/push-notifier.js +50 -0
  73. package/dist/push-notifier.js.map +1 -0
  74. package/dist/schema-verification.d.ts +11 -2
  75. package/dist/schema-verification.js +15 -4
  76. package/dist/schema-verification.js.map +1 -1
  77. package/dist/seed-bootstrap.d.ts +210 -7
  78. package/dist/seed-bootstrap.js +429 -131
  79. package/dist/seed-bootstrap.js.map +1 -1
  80. package/dist/seed-trust-policy.d.ts +63 -0
  81. package/dist/seed-trust-policy.js +56 -0
  82. package/dist/seed-trust-policy.js.map +1 -0
  83. package/dist/strand-addr-protocol.d.ts +132 -0
  84. package/dist/strand-addr-protocol.js +313 -0
  85. package/dist/strand-addr-protocol.js.map +1 -0
  86. package/dist/strand-cohort.d.ts +54 -0
  87. package/dist/strand-cohort.js +38 -0
  88. package/dist/strand-cohort.js.map +1 -0
  89. package/dist/strand-database.d.ts +54 -6
  90. package/dist/strand-database.js +88 -89
  91. package/dist/strand-database.js.map +1 -1
  92. package/dist/strand-formation-manager.d.ts +79 -19
  93. package/dist/strand-formation-manager.js +196 -104
  94. package/dist/strand-formation-manager.js.map +1 -1
  95. package/dist/strand-formation-protocol.d.ts +162 -0
  96. package/dist/strand-formation-protocol.js +279 -0
  97. package/dist/strand-formation-protocol.js.map +1 -0
  98. package/dist/strand-instance-manager.d.ts +73 -4
  99. package/dist/strand-instance-manager.js +167 -41
  100. package/dist/strand-instance-manager.js.map +1 -1
  101. package/dist/strand-member-key.d.ts +29 -0
  102. package/dist/strand-member-key.js +38 -0
  103. package/dist/strand-member-key.js.map +1 -0
  104. package/dist/strand-member-registry.d.ts +98 -0
  105. package/dist/strand-member-registry.js +129 -0
  106. package/dist/strand-member-registry.js.map +1 -0
  107. package/dist/strand-membership-writer.d.ts +361 -0
  108. package/dist/strand-membership-writer.js +449 -0
  109. package/dist/strand-membership-writer.js.map +1 -0
  110. package/dist/strand-solicitation.d.ts +77 -2
  111. package/dist/strand-solicitation.js +25 -4
  112. package/dist/strand-solicitation.js.map +1 -1
  113. package/dist/strand-wake-payload.d.ts +30 -0
  114. package/dist/strand-wake-payload.js +20 -0
  115. package/dist/strand-wake-payload.js.map +1 -0
  116. package/dist/strand-wake-protocol.d.ts +114 -0
  117. package/dist/strand-wake-protocol.js +257 -0
  118. package/dist/strand-wake-protocol.js.map +1 -0
  119. package/dist/strand-watcher.d.ts +6 -2
  120. package/dist/strand-watcher.js +57 -17
  121. package/dist/strand-watcher.js.map +1 -1
  122. package/dist/types.d.ts +413 -23
  123. package/dist/types.js +22 -5
  124. package/dist/types.js.map +1 -1
  125. package/package.json +14 -8
  126. package/src/authority-key.ts +68 -0
  127. package/src/cadre-node.ts +2937 -929
  128. package/src/canonical-datetime.ts +39 -0
  129. package/src/canonical-json.ts +30 -0
  130. package/src/control-cohort.ts +103 -0
  131. package/src/control-database.ts +931 -383
  132. package/src/control-formation-recorder.ts +151 -0
  133. package/src/control-schema.ts +198 -0
  134. package/src/control-stream.ts +128 -0
  135. package/src/device-token.ts +89 -0
  136. package/src/diagnostics/connection-path.ts +277 -0
  137. package/src/diagnostics/webrtc-turn-tracker.ts +222 -0
  138. package/src/hibernation-manager.ts +197 -28
  139. package/src/index.ts +273 -79
  140. package/src/key-store-file.ts +203 -0
  141. package/src/key-store.ts +99 -0
  142. package/src/peer-authorization.ts +53 -0
  143. package/src/peer-record.ts +146 -0
  144. package/src/push-credentials.ts +84 -0
  145. package/src/push-fanout.ts +272 -0
  146. package/src/push-notifier-apns.ts +243 -0
  147. package/src/push-notifier-fcm.ts +221 -0
  148. package/src/push-notifier-shared.ts +28 -0
  149. package/src/push-notifier.ts +93 -0
  150. package/src/schema-verification.ts +18 -4
  151. package/src/seed-bootstrap.ts +504 -171
  152. package/src/seed-trust-policy.ts +107 -0
  153. package/src/strand-addr-protocol.ts +410 -0
  154. package/src/strand-cohort.ts +77 -0
  155. package/src/strand-database.ts +113 -126
  156. package/src/strand-formation-manager.ts +233 -142
  157. package/src/strand-formation-protocol.ts +391 -0
  158. package/src/strand-instance-manager.ts +207 -56
  159. package/src/strand-member-key.ts +39 -0
  160. package/src/strand-member-registry.ts +177 -0
  161. package/src/strand-membership-writer.ts +640 -0
  162. package/src/strand-solicitation.ts +93 -4
  163. package/src/strand-wake-payload.ts +32 -0
  164. package/src/strand-wake-protocol.ts +331 -0
  165. package/src/strand-watcher.ts +61 -17
  166. package/src/types.ts +1012 -575
@@ -7,9 +7,10 @@ const timing = debug('sereus:cadre:timing');
7
7
  /**
8
8
  * Get the isolated storage path for a specific strand.
9
9
  *
10
- * @deprecated This function uses Node.js path module which is not available in React Native.
11
- * Use a storage provider factory function instead, which receives the strandId and can
12
- * create strand-specific storage paths using platform-appropriate methods.
10
+ * @deprecated This helper is Node-only and throws in React Native (it assumes a
11
+ * filesystem layout). Use a storage provider factory function instead, which
12
+ * receives the strandId and can create strand-specific storage paths using
13
+ * platform-appropriate methods.
13
14
  *
14
15
  * @example
15
16
  * // Instead of using getStrandStoragePath, use a storage provider factory:
@@ -23,12 +24,15 @@ export function getStrandStoragePath(basePath, strandId) {
23
24
  throw new Error('getStrandStoragePath is not available in React Native. ' +
24
25
  'Use a storage provider factory function instead.');
25
26
  }
26
- // Dynamically require path only in Node.js
27
- // eslint-disable-next-line @typescript-eslint/no-require-imports
28
- const path = require('path');
29
27
  // Sanitize strandId for filesystem safety (UUIDs should be safe, but just in case)
30
28
  const safeId = strandId.replace(/[^a-zA-Z0-9-]/g, '_');
31
- return path.join(basePath, 'strands', safeId);
29
+ // Build the path with plain string joins rather than the Node `path` module.
30
+ // A static `require('path')` forces RN bundlers (e.g. Metro) to *resolve* the
31
+ // module at bundle time even though this Node-only helper throws above before
32
+ // ever reaching here — joining by hand keeps the module free of any Node
33
+ // built-in reference, so RN bundles need no `path` shim.
34
+ const trimmedBase = basePath.replace(/[\\/]+$/, '');
35
+ return `${trimmedBase}/strands/${safeId}`;
32
36
  }
33
37
  /**
34
38
  * Resolve a storage provider for a specific strand.
@@ -51,6 +55,12 @@ function resolveStrandStorage(provider, strandId) {
51
55
  export class StrandInstanceManager {
52
56
  constructor() {
53
57
  this.instances = new Map();
58
+ /**
59
+ * Retained launch config per strand, captured in `startStrand` and cleared in
60
+ * `stopStrand`. `resumeStrand` reuses it to rebuild a quiesced strand's runtime
61
+ * without the caller re-threading storage/network/profile/key/sApp config.
62
+ */
63
+ this.launchConfigs = new Map();
54
64
  this.stopping = false;
55
65
  log('StrandInstanceManager created');
56
66
  }
@@ -87,8 +97,9 @@ export class StrandInstanceManager {
87
97
  }
88
98
  log('Starting strand instance: %s (sApp: %s v%s)', strandId, sAppConfig.id, sAppConfig.version);
89
99
  const tTotal = performance.now();
90
- // Verify schema signature before proceeding
91
- assertSchemaSignature(sAppConfig);
100
+ // Verify schema signature before proceeding (fail-closed by default)
101
+ const requireSignature = config.requireSignedSchemas ?? true;
102
+ assertSchemaSignature(sAppConfig, { requireSignature });
92
103
  log('Strand %s sApp schema signature verified (author: %s)', strandId, sAppConfig.id);
93
104
  // Convert SAppConfig to SAppInfo for the instance
94
105
  const sAppInfo = {
@@ -109,25 +120,49 @@ export class StrandInstanceManager {
109
120
  latencyHint
110
121
  };
111
122
  this.instances.set(strandId, instance);
123
+ this.launchConfigs.set(strandId, config);
124
+ try {
125
+ await this.buildStrandRuntime(instance, config);
126
+ timing('[startStrand:%s] total: %dms', strandId, Math.round(performance.now() - tTotal));
127
+ log('Strand %s started successfully with sApp %s', strandId, sAppConfig.id);
128
+ return instance;
129
+ }
130
+ catch (error) {
131
+ instance.status = 'error';
132
+ instance.error = error instanceof Error ? error.message : String(error);
133
+ log('Failed to start strand %s: %s', strandId, instance.error);
134
+ throw error;
135
+ }
136
+ }
137
+ /**
138
+ * Build (or rebuild) the libp2p node + StrandDatabase for an instance and
139
+ * attach them, transitioning it to `active`. Shared by `startStrand` (fresh
140
+ * launch) and `resumeStrand` (rehydrating a quiesced instance). Reads all
141
+ * volatile inputs (bootstrapNodes, mode, storage, network, profile,
142
+ * privateKey, sApp config) from `config`, so the caller controls the
143
+ * cohort-derived values.
144
+ */
145
+ async buildStrandRuntime(instance, config) {
146
+ const strandId = instance.strandId;
147
+ const { sAppConfig } = config;
148
+ // Resolve storage for this strand. If a factory function is provided, it is
149
+ // called with the strandId to create strand-specific storage (e.g.,
150
+ // strand-isolated directories).
151
+ const strandStorage = resolveStrandStorage(config.storage?.provider, strandId);
152
+ if (strandStorage) {
153
+ log('Strand %s using provided storage provider', strandId);
154
+ }
155
+ // Note: protocolPrefix would be used by libp2p services, but createLibp2pNode
156
+ // may not support it yet - tracked for future implementation.
157
+ const _protocolPrefix = `/sereus/strand/${strandId}`;
158
+ // Determine relay mode: if explicitly set in config, use that;
159
+ // otherwise default to true for storage profile nodes.
160
+ const enableRelay = config.network?.enableRelay ?? (config.profile === 'storage');
112
161
  try {
113
- // Resolve storage for this strand
114
- // If a factory function is provided, it will be called with the strandId
115
- // to create strand-specific storage (e.g., strand-isolated directories)
116
- const strandStorage = resolveStrandStorage(config.storage?.provider, strandId);
117
- if (strandStorage) {
118
- log('Strand %s using provided storage provider', strandId);
119
- }
120
- // Create isolated libp2p node for this strand
121
- // Note: protocolPrefix would be used by libp2p services, but createLibp2pNode
122
- // may not support it yet - tracked for future implementation
123
- const _protocolPrefix = `/sereus/strand/${strandId}`;
124
- // Determine relay mode: if explicitly set in config, use that;
125
- // otherwise default to true for storage profile nodes
126
- const enableRelay = config.network?.enableRelay ?? (config.profile === 'storage');
127
162
  let t0 = performance.now();
128
163
  const node = await createLibp2pNode({
129
164
  port: 0, // Random port
130
- bootstrapNodes: [], // Will be populated from strand cohort
165
+ bootstrapNodes: config.bootstrapNodes ?? [],
131
166
  networkName: `strand-${strandId}`,
132
167
  storage: strandStorage,
133
168
  fretProfile: config.profile === 'storage' ? 'core' : 'edge',
@@ -142,15 +177,19 @@ export class StrandInstanceManager {
142
177
  },
143
178
  ...(config.privateKey && { privateKey: config.privateKey }),
144
179
  ...(config.network?.transports && { transports: config.network.transports }),
145
- ...(config.network?.listenAddrs && { listenAddrs: config.network.listenAddrs })
180
+ ...(config.network?.listenAddrs && { listenAddrs: config.network.listenAddrs }),
181
+ ...(config.network?.connectionGater && { connectionGater: config.network.connectionGater })
146
182
  });
147
- timing('[startStrand:%s] createLibp2pNode: %dms', strandId, Math.round(performance.now() - t0));
183
+ timing('[buildStrandRuntime:%s] createLibp2pNode: %dms', strandId, Math.round(performance.now() - t0));
148
184
  instance.libp2pNode = node;
149
185
  // Create and initialize the StrandDatabase. In bootstrap mode the same
150
186
  // strandStorage instance also backs the optimystic local transactor so
151
187
  // DML lands on the host's persistent storage (e.g. MMKV on RN). Sharing
152
188
  // the instance — not creating a second one over the same id+prefix —
153
189
  // avoids cache divergence between the libp2p and database paths.
190
+ //
191
+ // Attach before initialize so a failed init is cleaned up by
192
+ // releaseRuntime below (close() is safe on a partially-initialized db).
154
193
  t0 = performance.now();
155
194
  const strandDb = new StrandDatabase({
156
195
  strandId,
@@ -158,21 +197,115 @@ export class StrandInstanceManager {
158
197
  libp2pNode: node,
159
198
  coordinatedRepo: node.coordinatedRepo,
160
199
  mode: config.mode ?? 'networked',
161
- rawStorage: strandStorage
200
+ rawStorage: strandStorage,
201
+ // Founder bootstrap inputs: the strand's type drives which membership rows
202
+ // are written, and the closed-strand MemberPrivateKey derives the founding
203
+ // Member/Authority key. Both come off the control-network strand row.
204
+ strandType: config.strandRow.Type,
205
+ memberPrivateKey: config.strandRow.MemberPrivateKey ?? undefined,
206
+ founder: config.founder
162
207
  });
163
- await strandDb.initialize();
164
- timing('[startStrand:%s] strandDatabase.initialize: %dms', strandId, Math.round(performance.now() - t0));
165
208
  instance.database = strandDb;
209
+ await strandDb.initialize();
210
+ timing('[buildStrandRuntime:%s] strandDatabase.initialize: %dms', strandId, Math.round(performance.now() - t0));
166
211
  instance.status = 'active';
167
212
  instance.lastActivity = new Date();
168
- timing('[startStrand:%s] total: %dms', strandId, Math.round(performance.now() - tTotal));
169
- log('Strand %s started successfully with sApp %s', strandId, sAppConfig.id);
213
+ }
214
+ catch (error) {
215
+ // Roll back any partially-attached runtime so the instance is left with
216
+ // NEITHER handle. Otherwise the `libp2pNode || database` "already live"
217
+ // guard in resumeStrand/handleStrandWake would treat a half-built strand
218
+ // as healthy — leaking the libp2p node and never retrying the rebuild.
219
+ await this.releaseRuntime(instance).catch((cleanupErr) => {
220
+ log('buildStrandRuntime cleanup for strand %s also failed: %o', strandId, cleanupErr);
221
+ });
222
+ throw error;
223
+ }
224
+ }
225
+ /**
226
+ * Release an instance's strand-network runtime: close the StrandDatabase, then
227
+ * stop the libp2p node (construction order in reverse), clearing both fields
228
+ * and zeroing connectedPeers. Tolerant of partially-built state — either handle
229
+ * may be absent — so it doubles as rollback for a failed `buildStrandRuntime`.
230
+ * Shared by `quiesceStrand`, `stopStrand`, and that rollback path.
231
+ */
232
+ async releaseRuntime(instance) {
233
+ if (instance.database) {
234
+ await instance.database.close();
235
+ instance.database = undefined;
236
+ }
237
+ if (instance.libp2pNode) {
238
+ await instance.libp2pNode.stop();
239
+ instance.libp2pNode = undefined;
240
+ }
241
+ instance.connectedPeers = 0;
242
+ }
243
+ /**
244
+ * Quiesce a strand: release its strand-network resources (stop the libp2p node,
245
+ * close the StrandDatabase) while RETAINING the instance record — identity,
246
+ * sAppInfo, keys, latency hint, metadata — and its launch config so it can be
247
+ * resumed later. Mechanically this is `stopStrand` minus the instance/config
248
+ * deletion. The caller sets the post-quiesce status (e.g. `hibernating`).
249
+ * No-ops when the strand is missing or already quiesced.
250
+ */
251
+ async quiesceStrand(strandId) {
252
+ const instance = this.instances.get(strandId);
253
+ if (!instance) {
254
+ log('quiesceStrand: strand %s not found', strandId);
255
+ return;
256
+ }
257
+ if (!instance.libp2pNode && !instance.database) {
258
+ log('quiesceStrand: strand %s already quiesced', strandId);
259
+ return;
260
+ }
261
+ log('Quiescing strand instance: %s', strandId);
262
+ await this.releaseRuntime(instance);
263
+ log('Strand %s quiesced (resources released, instance retained)', strandId);
264
+ }
265
+ /**
266
+ * Resume a previously-quiesced strand: rebuild its libp2p node + StrandDatabase
267
+ * from the retained launch config and re-attach them, transitioning it back to
268
+ * `active`. `overrides` re-applies volatile inputs that may have changed since
269
+ * launch (cohort `bootstrapNodes`, lifecycle `mode`) and updates the retained
270
+ * config so a later resume reuses the latest values. Returns the live instance
271
+ * unchanged if it is already running.
272
+ */
273
+ async resumeStrand(strandId, overrides) {
274
+ if (this.stopping) {
275
+ throw new Error('StrandInstanceManager is stopping');
276
+ }
277
+ const instance = this.instances.get(strandId);
278
+ if (!instance) {
279
+ throw new Error(`Cannot resume strand ${strandId}: not tracked`);
280
+ }
281
+ const launchConfig = this.launchConfigs.get(strandId);
282
+ if (!launchConfig) {
283
+ throw new Error(`Cannot resume strand ${strandId}: no retained launch config`);
284
+ }
285
+ if (instance.libp2pNode || instance.database) {
286
+ log('resumeStrand: strand %s already live', strandId);
287
+ return instance;
288
+ }
289
+ log('Resuming strand instance: %s', strandId);
290
+ const tTotal = performance.now();
291
+ // Re-apply volatile inputs and persist them so a subsequent resume reuses them.
292
+ const resumeConfig = {
293
+ ...launchConfig,
294
+ bootstrapNodes: overrides?.bootstrapNodes ?? launchConfig.bootstrapNodes,
295
+ mode: overrides?.mode ?? launchConfig.mode
296
+ };
297
+ this.launchConfigs.set(strandId, resumeConfig);
298
+ instance.status = 'starting';
299
+ try {
300
+ await this.buildStrandRuntime(instance, resumeConfig);
301
+ timing('[resumeStrand:%s] total: %dms', strandId, Math.round(performance.now() - tTotal));
302
+ log('Strand %s resumed successfully', strandId);
170
303
  return instance;
171
304
  }
172
305
  catch (error) {
173
306
  instance.status = 'error';
174
307
  instance.error = error instanceof Error ? error.message : String(error);
175
- log('Failed to start strand %s: %s', strandId, instance.error);
308
+ log('Failed to resume strand %s: %s', strandId, instance.error);
176
309
  throw error;
177
310
  }
178
311
  }
@@ -188,17 +321,10 @@ export class StrandInstanceManager {
188
321
  log('Stopping strand instance: %s', strandId);
189
322
  instance.status = 'stopping';
190
323
  try {
191
- // Close the database before stopping libp2p
192
- if (instance.database) {
193
- await instance.database.close();
194
- instance.database = undefined;
195
- }
196
- if (instance.libp2pNode) {
197
- await instance.libp2pNode.stop();
198
- instance.libp2pNode = undefined;
199
- }
324
+ await this.releaseRuntime(instance);
200
325
  instance.status = 'stopped';
201
326
  this.instances.delete(strandId);
327
+ this.launchConfigs.delete(strandId);
202
328
  log('Strand %s stopped successfully', strandId);
203
329
  }
204
330
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"strand-instance-manager.js","sourceRoot":"","sources":["../src/strand-instance-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strand-instance-manager.js","sourceRoot":"","sources":["../src/strand-instance-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { type AuthorityKeyPair } from './authority-key.js';
2
+ /**
3
+ * Mint a fresh ed25519 strand member private key, encoded as base64 protobuf.
4
+ *
5
+ * Closed strands (`Type:'c'`) gate membership on a `MemberPrivateKey` column
6
+ * (see the `Strand` control-schema table). This produces one in the SAME
7
+ * encoding {@link StrandSolicitationService.formStrand} uses for its
8
+ * `invitePrivateKey`, so a host-minted key and a formation-issued key are
9
+ * interchangeable when attaching a closed strand.
10
+ */
11
+ export declare function generateStrandMemberKey(): Promise<string>;
12
+ /**
13
+ * Bridge a strand's `MemberPrivateKey` (base64 protobuf libp2p ed25519 key, as
14
+ * minted by {@link generateStrandMemberKey} or issued by formation) into the
15
+ * base64url keypair the strand RBAC constraints consume.
16
+ *
17
+ * The `Strand` control-layer `MemberPrivateKey` is the closed-strand read-gating
18
+ * secret; the founding `Member.Key`/`Authority.MemberKey` (the strand RBAC layer)
19
+ * are *derived from it* — they are the `publicKeyB64` of the keypair this returns.
20
+ * Decode the protobuf to a libp2p private key, then reuse
21
+ * {@link authorityKeyFromLibp2p} so the same seed→public derivation used for node
22
+ * authority keys yields a stable `{ privateKeyB64, publicKeyB64 }` whose public
23
+ * key a later strand signature verifies against.
24
+ *
25
+ * @param memberPrivateKey - The strand's `MemberPrivateKey` (base64 protobuf).
26
+ * @returns The base64url seed/public-key pair for founding membership/authority.
27
+ * @throws If the decoded key is not Ed25519 or the raw bytes are malformed.
28
+ */
29
+ export declare function strandMemberKeyPair(memberPrivateKey: string): AuthorityKeyPair;
@@ -0,0 +1,38 @@
1
+ import { generateKeyPair, privateKeyToProtobuf, privateKeyFromProtobuf } from '@libp2p/crypto/keys';
2
+ import { toString as uint8ArrayToString, fromString as uint8ArrayFromString } from 'uint8arrays';
3
+ import { authorityKeyFromLibp2p } from './authority-key.js';
4
+ /**
5
+ * Mint a fresh ed25519 strand member private key, encoded as base64 protobuf.
6
+ *
7
+ * Closed strands (`Type:'c'`) gate membership on a `MemberPrivateKey` column
8
+ * (see the `Strand` control-schema table). This produces one in the SAME
9
+ * encoding {@link StrandSolicitationService.formStrand} uses for its
10
+ * `invitePrivateKey`, so a host-minted key and a formation-issued key are
11
+ * interchangeable when attaching a closed strand.
12
+ */
13
+ export async function generateStrandMemberKey() {
14
+ const privateKey = await generateKeyPair('Ed25519');
15
+ return uint8ArrayToString(privateKeyToProtobuf(privateKey), 'base64');
16
+ }
17
+ /**
18
+ * Bridge a strand's `MemberPrivateKey` (base64 protobuf libp2p ed25519 key, as
19
+ * minted by {@link generateStrandMemberKey} or issued by formation) into the
20
+ * base64url keypair the strand RBAC constraints consume.
21
+ *
22
+ * The `Strand` control-layer `MemberPrivateKey` is the closed-strand read-gating
23
+ * secret; the founding `Member.Key`/`Authority.MemberKey` (the strand RBAC layer)
24
+ * are *derived from it* — they are the `publicKeyB64` of the keypair this returns.
25
+ * Decode the protobuf to a libp2p private key, then reuse
26
+ * {@link authorityKeyFromLibp2p} so the same seed→public derivation used for node
27
+ * authority keys yields a stable `{ privateKeyB64, publicKeyB64 }` whose public
28
+ * key a later strand signature verifies against.
29
+ *
30
+ * @param memberPrivateKey - The strand's `MemberPrivateKey` (base64 protobuf).
31
+ * @returns The base64url seed/public-key pair for founding membership/authority.
32
+ * @throws If the decoded key is not Ed25519 or the raw bytes are malformed.
33
+ */
34
+ export function strandMemberKeyPair(memberPrivateKey) {
35
+ const privateKey = privateKeyFromProtobuf(uint8ArrayFromString(memberPrivateKey, 'base64'));
36
+ return authorityKeyFromLibp2p(privateKey);
37
+ }
38
+ //# sourceMappingURL=strand-member-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strand-member-key.js","sourceRoot":"","sources":["../src/strand-member-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,UAAU,IAAI,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAE,sBAAsB,EAAyB,MAAM,oBAAoB,CAAC;AAEnF;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;IACpD,OAAO,kBAAkB,CAAC,oBAAoB,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,mBAAmB,CAAC,gBAAwB;IAC1D,MAAM,UAAU,GAAG,sBAAsB,CAAC,oBAAoB,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC5F,OAAO,sBAAsB,CAAC,UAAU,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,98 @@
1
+ import type { Database } from '@quereus/quereus';
2
+ import type { MemberRegistration } from './types.js';
3
+ import type { MemberRegistry, MemberVerifier } from './enrollment.js';
4
+ import type { AuthorityKeyPair } from './authority-key.js';
5
+ /**
6
+ * The canonical payload a joining member signs to prove ownership of its strand
7
+ * member key, verified by {@link StrandMemberVerifier.verifyMember}.
8
+ *
9
+ * Binds the member key to the strand id so a registration signature cannot be
10
+ * replayed against a different strand. Peer-id binding is intentionally NOT part
11
+ * of this payload — `MemberPeer` rows carry their OWN per-peer self-proofs
12
+ * (`MemberKey || '|' || PeerId`, signed by the member) and are written by the
13
+ * dedicated `registerMemberPeer` writer; see {@link StrandMemberRegistry}.
14
+ */
15
+ export declare function memberRegistrationPayload(registration: MemberRegistration): string;
16
+ /** How a {@link StrandMemberRegistry} admits a member into the strand. */
17
+ export type StrandAdmission = {
18
+ /** Redeem a specific single-use invite (the invitee-side join). */
19
+ readonly mode: 'invite';
20
+ /** The `Invite.Key` (invite public key, base64url) to redeem. */
21
+ readonly inviteKey: string;
22
+ /** The matching invite private seed (base64url), received out-of-band. */
23
+ readonly invitePrivateKey: string;
24
+ } | {
25
+ /** Admit directly by authority signature (the authority-side join). */
26
+ readonly mode: 'authority';
27
+ /** The admitting authority's strand keypair. */
28
+ readonly authorityKeyPair: AuthorityKeyPair;
29
+ };
30
+ /**
31
+ * Strand-`Database`-backed {@link MemberVerifier} — the reconciliation that lets
32
+ * {@link EnrollmentService} make real authorization decisions against a strand's
33
+ * `Invite`/`ConsumedInvite`/`Member` tables instead of a placeholder.
34
+ *
35
+ * Each instance is scoped to ONE strand's database (the `Strand.*` tables live
36
+ * inside the connected strand DB), so the `strandId` arguments on the interface
37
+ * are accepted for signature compatibility but the queries run against this db.
38
+ */
39
+ export declare class StrandMemberVerifier implements MemberVerifier {
40
+ private readonly db;
41
+ constructor(db: Database);
42
+ /**
43
+ * Verify the member's self-proof over {@link memberRegistrationPayload} against
44
+ * the registration's own `key` (the member's ed25519 public key). This is the
45
+ * off-engine counterpart to the on-engine signature checks — a pre-flight gate
46
+ * before the constraint-enforced write.
47
+ */
48
+ verifyMember(registration: MemberRegistration, signature: string): Promise<boolean>;
49
+ /**
50
+ * A member is authorized to join when either it has already consumed an invite
51
+ * (a `ConsumedInvite` row bears its key) OR the strand has at least one
52
+ * outstanding `Invite` it could redeem.
53
+ *
54
+ * Strand invites are anonymous — `Invite.Key` is an invite public key, not a
55
+ * member key, so an un-consumed invite cannot be bound to a specific member
56
+ * here. This check is therefore a "door is open" pre-flight; the binding,
57
+ * single-use, and cryptographic gates are all enforced by the deferred
58
+ * `Strand.*` constraints when the member is actually written.
59
+ *
60
+ * Expired invites are filtered out so this pre-flight matches the on-engine
61
+ * `ConsumedInvite.NotExpired` gate — `Now` is canonicalised the same way the
62
+ * writer canonicalises it, so `Expiration > ?` compares like-for-like canonical
63
+ * strings. A member that already holds a `ConsumedInvite` short-circuits above,
64
+ * regardless of any invite's expiry.
65
+ */
66
+ isAuthorizedToJoin(_strandId: string, memberKey: string): Promise<boolean>;
67
+ }
68
+ /**
69
+ * Strand-`Database`-backed {@link MemberRegistry} — writes real `Strand.Member`
70
+ * (and, in invite mode, `Strand.ConsumedInvite`) rows through the signed writer
71
+ * primitives, so {@link EnrollmentService.registerMember} performs the actual
72
+ * per-strand join handshake rather than returning "MemberRegistry not configured".
73
+ *
74
+ * The {@link StrandAdmission} chosen at construction selects which branch of
75
+ * `Member.Authorized` is taken:
76
+ * - `invite` → {@link consumeInvite} (atomic `Member` + `ConsumedInvite`), the
77
+ * invitee-side flow that redeems a single-use invite.
78
+ * - `authority` → {@link addMemberByAuthority}, the authority-side flow that
79
+ * seats a member already trusted by an authority.
80
+ *
81
+ * Scoped to one strand's db; the `strandId` argument is accepted for interface
82
+ * compatibility but writes target this db's `Strand.*` tables.
83
+ *
84
+ * NOTE: `peerIds` are NOT written as `MemberPeer` rows here. The signed peer path
85
+ * now exists as the standalone `registerMemberPeer` writer (each peer self-proof is
86
+ * signed by the member's own key), but wiring the enrollment `peerIds` through it is
87
+ * a deliberate follow-on — the member self-proof available here does not carry the
88
+ * per-peer signatures `MemberPeer.Authorized` requires. A non-empty `peerIds` is
89
+ * logged and otherwise ignored so the member still lands; peer rows are reconciled
90
+ * separately via `registerMemberPeer`.
91
+ */
92
+ export declare class StrandMemberRegistry implements MemberRegistry {
93
+ private readonly db;
94
+ private readonly admission;
95
+ constructor(db: Database, admission: StrandAdmission);
96
+ registerMember(strandId: string, memberKey: string, peerIds: string[]): Promise<void>;
97
+ isMemberRegistered(_strandId: string, memberKey: string): Promise<boolean>;
98
+ }
@@ -0,0 +1,129 @@
1
+ import debug from 'debug';
2
+ import { verifyStrandPayload, consumeInvite, addMemberByAuthority, } from './strand-membership-writer.js';
3
+ import { canonicalDatetime } from './canonical-datetime.js';
4
+ const log = debug('sereus:cadre:strand-member-registry');
5
+ /**
6
+ * The canonical payload a joining member signs to prove ownership of its strand
7
+ * member key, verified by {@link StrandMemberVerifier.verifyMember}.
8
+ *
9
+ * Binds the member key to the strand id so a registration signature cannot be
10
+ * replayed against a different strand. Peer-id binding is intentionally NOT part
11
+ * of this payload — `MemberPeer` rows carry their OWN per-peer self-proofs
12
+ * (`MemberKey || '|' || PeerId`, signed by the member) and are written by the
13
+ * dedicated `registerMemberPeer` writer; see {@link StrandMemberRegistry}.
14
+ */
15
+ export function memberRegistrationPayload(registration) {
16
+ return `${registration.strandId}|${registration.key}`;
17
+ }
18
+ /**
19
+ * Strand-`Database`-backed {@link MemberVerifier} — the reconciliation that lets
20
+ * {@link EnrollmentService} make real authorization decisions against a strand's
21
+ * `Invite`/`ConsumedInvite`/`Member` tables instead of a placeholder.
22
+ *
23
+ * Each instance is scoped to ONE strand's database (the `Strand.*` tables live
24
+ * inside the connected strand DB), so the `strandId` arguments on the interface
25
+ * are accepted for signature compatibility but the queries run against this db.
26
+ */
27
+ export class StrandMemberVerifier {
28
+ constructor(db) {
29
+ this.db = db;
30
+ }
31
+ /**
32
+ * Verify the member's self-proof over {@link memberRegistrationPayload} against
33
+ * the registration's own `key` (the member's ed25519 public key). This is the
34
+ * off-engine counterpart to the on-engine signature checks — a pre-flight gate
35
+ * before the constraint-enforced write.
36
+ */
37
+ async verifyMember(registration, signature) {
38
+ const ok = verifyStrandPayload(memberRegistrationPayload(registration), signature, registration.key);
39
+ if (!ok) {
40
+ log('verifyMember: bad self-proof for member %s on strand %s', registration.key, registration.strandId);
41
+ }
42
+ return ok;
43
+ }
44
+ /**
45
+ * A member is authorized to join when either it has already consumed an invite
46
+ * (a `ConsumedInvite` row bears its key) OR the strand has at least one
47
+ * outstanding `Invite` it could redeem.
48
+ *
49
+ * Strand invites are anonymous — `Invite.Key` is an invite public key, not a
50
+ * member key, so an un-consumed invite cannot be bound to a specific member
51
+ * here. This check is therefore a "door is open" pre-flight; the binding,
52
+ * single-use, and cryptographic gates are all enforced by the deferred
53
+ * `Strand.*` constraints when the member is actually written.
54
+ *
55
+ * Expired invites are filtered out so this pre-flight matches the on-engine
56
+ * `ConsumedInvite.NotExpired` gate — `Now` is canonicalised the same way the
57
+ * writer canonicalises it, so `Expiration > ?` compares like-for-like canonical
58
+ * strings. A member that already holds a `ConsumedInvite` short-circuits above,
59
+ * regardless of any invite's expiry.
60
+ */
61
+ async isAuthorizedToJoin(_strandId, memberKey) {
62
+ const consumed = await scalarCount(this.db, 'select count(1) as c from Strand.ConsumedInvite where MemberKey = ?', [memberKey]);
63
+ if (consumed > 0) {
64
+ return true;
65
+ }
66
+ const nowCanonical = await canonicalDatetime(this.db, Date.now());
67
+ const invites = await scalarCount(this.db, 'select count(1) as c from Strand.Invite where Expiration is null or Expiration > ?', [nowCanonical]);
68
+ return invites > 0;
69
+ }
70
+ }
71
+ /**
72
+ * Strand-`Database`-backed {@link MemberRegistry} — writes real `Strand.Member`
73
+ * (and, in invite mode, `Strand.ConsumedInvite`) rows through the signed writer
74
+ * primitives, so {@link EnrollmentService.registerMember} performs the actual
75
+ * per-strand join handshake rather than returning "MemberRegistry not configured".
76
+ *
77
+ * The {@link StrandAdmission} chosen at construction selects which branch of
78
+ * `Member.Authorized` is taken:
79
+ * - `invite` → {@link consumeInvite} (atomic `Member` + `ConsumedInvite`), the
80
+ * invitee-side flow that redeems a single-use invite.
81
+ * - `authority` → {@link addMemberByAuthority}, the authority-side flow that
82
+ * seats a member already trusted by an authority.
83
+ *
84
+ * Scoped to one strand's db; the `strandId` argument is accepted for interface
85
+ * compatibility but writes target this db's `Strand.*` tables.
86
+ *
87
+ * NOTE: `peerIds` are NOT written as `MemberPeer` rows here. The signed peer path
88
+ * now exists as the standalone `registerMemberPeer` writer (each peer self-proof is
89
+ * signed by the member's own key), but wiring the enrollment `peerIds` through it is
90
+ * a deliberate follow-on — the member self-proof available here does not carry the
91
+ * per-peer signatures `MemberPeer.Authorized` requires. A non-empty `peerIds` is
92
+ * logged and otherwise ignored so the member still lands; peer rows are reconciled
93
+ * separately via `registerMemberPeer`.
94
+ */
95
+ export class StrandMemberRegistry {
96
+ constructor(db, admission) {
97
+ this.db = db;
98
+ this.admission = admission;
99
+ }
100
+ async registerMember(strandId, memberKey, peerIds) {
101
+ if (peerIds.length > 0) {
102
+ log('registerMember: %d peerId(s) for member %s on strand %s deferred to the registerMemberPeer path (needs per-peer self-proofs); registering member only', peerIds.length, memberKey, strandId);
103
+ }
104
+ if (this.admission.mode === 'invite') {
105
+ await consumeInvite(this.db, {
106
+ inviteKey: this.admission.inviteKey,
107
+ invitePrivateKey: this.admission.invitePrivateKey,
108
+ memberKey,
109
+ });
110
+ return;
111
+ }
112
+ await addMemberByAuthority(this.db, {
113
+ authorityKeyPair: this.admission.authorityKeyPair,
114
+ memberKey,
115
+ });
116
+ }
117
+ async isMemberRegistered(_strandId, memberKey) {
118
+ const count = await scalarCount(this.db, 'select count(1) as c from Strand.Member where Key = ?', [memberKey]);
119
+ return count > 0;
120
+ }
121
+ }
122
+ /** Run a `select count(1) as c ...` and return the scalar (0 if no row). */
123
+ async function scalarCount(db, sql, params) {
124
+ for await (const row of db.eval(sql, params)) {
125
+ return row.c ?? 0;
126
+ }
127
+ return 0;
128
+ }
129
+ //# sourceMappingURL=strand-member-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strand-member-registry.js","sourceRoot":"","sources":["../src/strand-member-registry.ts"],"names":[],"mappings":"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"}