@ibgib/core-gib 0.1.43 → 0.1.45
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/dist/keystone/kdf/kdf-constants.d.mts +25 -0
- package/dist/keystone/kdf/kdf-constants.d.mts.map +1 -0
- package/dist/keystone/kdf/kdf-constants.mjs +28 -0
- package/dist/keystone/kdf/kdf-constants.mjs.map +1 -0
- package/dist/keystone/kdf/kdf-helpers.d.mts +45 -0
- package/dist/keystone/kdf/kdf-helpers.d.mts.map +1 -0
- package/dist/keystone/kdf/kdf-helpers.mjs +94 -0
- package/dist/keystone/kdf/kdf-helpers.mjs.map +1 -0
- package/dist/keystone/kdf/kdf-types.d.mts +49 -0
- package/dist/keystone/kdf/kdf-types.d.mts.map +1 -0
- package/dist/keystone/kdf/kdf-types.mjs +2 -0
- package/dist/keystone/kdf/kdf-types.mjs.map +1 -0
- package/dist/keystone/keystone-config-builder.d.mts +65 -12
- package/dist/keystone/keystone-config-builder.d.mts.map +1 -1
- package/dist/keystone/keystone-config-builder.mjs +138 -46
- package/dist/keystone/keystone-config-builder.mjs.map +1 -1
- package/dist/keystone/keystone-config-builder.respec.mjs +21 -13
- package/dist/keystone/keystone-config-builder.respec.mjs.map +1 -1
- package/dist/keystone/keystone-constants.d.mts +15 -0
- package/dist/keystone/keystone-constants.d.mts.map +1 -1
- package/dist/keystone/keystone-constants.mjs +16 -0
- package/dist/keystone/keystone-constants.mjs.map +1 -1
- package/dist/keystone/keystone-helpers.d.mts +8 -4
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +76 -6
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.d.mts +1 -1
- package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.mjs +6 -5
- package/dist/keystone/keystone-service-v1.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.respec.mjs +72 -45
- package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
- package/dist/keystone/keystone-types.d.mts +28 -18
- package/dist/keystone/keystone-types.d.mts.map +1 -1
- package/dist/keystone/keystone-types.mjs +26 -15
- package/dist/keystone/keystone-types.mjs.map +1 -1
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.d.mts.map +1 -1
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs +7 -10
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs.map +1 -1
- package/dist/sync/sync-constants.d.mts +9 -0
- package/dist/sync/sync-constants.d.mts.map +1 -1
- package/dist/sync/sync-constants.mjs +10 -0
- package/dist/sync/sync-constants.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs +49 -19
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +3 -3
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +0 -38
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +1 -83
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +24 -4
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +36 -13
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +246 -38
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +1 -7
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +11 -0
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs.map +1 -1
- package/package.json +1 -1
- package/src/keystone/README.md +4 -3
- package/src/keystone/docs/architecture.md +3 -1
- package/src/keystone/kdf/kdf-constants.mts +34 -0
- package/src/keystone/kdf/kdf-helpers.mts +105 -0
- package/src/keystone/kdf/kdf-types.mts +58 -0
- package/src/keystone/keystone-config-builder.mts +170 -47
- package/src/keystone/keystone-config-builder.respec.mts +21 -14
- package/src/keystone/keystone-constants.mts +21 -2
- package/src/keystone/keystone-helpers.mts +100 -14
- package/src/keystone/keystone-service-v1.mts +23 -22
- package/src/keystone/keystone-service-v1.respec.mts +71 -44
- package/src/keystone/keystone-types.mts +37 -23
- package/src/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mts +9 -13
- package/src/sync/sync-constants.mts +12 -0
- package/src/sync/sync-innerspace-dest-ahead-withid.respec.mts +53 -20
- package/src/sync/sync-peer/sync-peer-v1.mts +3 -3
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +3 -107
- package/src/sync/sync-saga-context/sync-saga-context-types.mts +25 -4
- package/src/sync/sync-saga-coordinator.mts +313 -40
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +1 -7
- package/src/sync/sync-types.mts +12 -0
- package/tmp.md +0 -274
|
@@ -16,13 +16,16 @@ import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs";
|
|
|
16
16
|
import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
|
|
17
17
|
import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
|
|
18
18
|
import { putInSpace, getLatestAddrs, getFromSpace, registerNewIbGib } from "../witness/space/space-helper.mjs";
|
|
19
|
-
import { KeystoneIbGib_V1 } from "../keystone/keystone-types.mjs";
|
|
19
|
+
import { KeystoneIbGib_V1, KeystonePoolConfig_HashV1 } from "../keystone/keystone-types.mjs";
|
|
20
20
|
import { KeystoneService_V1 } from "../keystone/keystone-service-v1.mjs";
|
|
21
|
+
import { deriveKey } from "../keystone/kdf/kdf-helpers.mjs";
|
|
22
|
+
import { KdfStrategy } from "../keystone/kdf/kdf-constants.mjs";
|
|
21
23
|
import { MetaspaceService } from "../witness/space/metaspace/metaspace-types.mjs";
|
|
22
24
|
import {
|
|
23
25
|
SyncStage, SYNC_ATOM, SYNC_MSG_REL8N_NAME, SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN,
|
|
24
26
|
SyncConflictStrategy,
|
|
25
27
|
SYNC_CONFLICT_STRATEGY_VALID_VALUES,
|
|
28
|
+
DEFAULT_SESSION_IDENTITY_INITIAL_DELEGATE_SECRET,
|
|
26
29
|
} from "./sync-constants.mjs";
|
|
27
30
|
import {
|
|
28
31
|
appendToTimeline, createTimeline, getHistory, getHistoryAddrs, Rel8nInfo, Rel8nRemovalInfo
|
|
@@ -47,8 +50,8 @@ import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-consta
|
|
|
47
50
|
import { SyncSagaInfo } from "./sync-types.mjs";
|
|
48
51
|
import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib, getIbGibsFromCache_fallbackToSpaces } from "../common/other/ibgib-helper.mjs";
|
|
49
52
|
import { SyncPeerWitness } from "./sync-peer/sync-peer-types.mjs";
|
|
50
|
-
import { SyncSagaContextIbGib_V1, } from "./sync-saga-context/sync-saga-context-types.mjs";
|
|
51
|
-
import {
|
|
53
|
+
import { SyncSagaContextData_V1, SyncSagaContextIbGib_V1, SyncSagaContextRel8ns_V1, } from "./sync-saga-context/sync-saga-context-types.mjs";
|
|
54
|
+
import { getSyncSagaContextIb, validateContextAndSagaFrame } from "./sync-saga-context/sync-saga-context-helpers.mjs";
|
|
52
55
|
import { newupSubject, } from "../common/pubsub/subject/subject-helper.mjs";
|
|
53
56
|
import { SubjectWitness } from "../common/pubsub/subject/subject-types.mjs";
|
|
54
57
|
import { getSyncSagaMessageFromFrame } from "./sync-saga-message/sync-saga-message-helpers.mjs";
|
|
@@ -60,6 +63,9 @@ import { graftTimelines, } from "./graft-info/graft-info-helpers.mjs";
|
|
|
60
63
|
import { GRAFT_INFO_REL8N_NAME } from "./graft-info/graft-info-constants.mjs";
|
|
61
64
|
import { GraftInfoIbGib_V1 } from "./graft-info/graft-info-types.mjs";
|
|
62
65
|
import { validateIbGibIntrinsically } from "@ibgib/ts-gib/dist/V1/validate-helper.mjs";
|
|
66
|
+
import { KEYSTONE_VERB_MANAGE } from "../keystone/keystone-constants.mjs";
|
|
67
|
+
import { validateKeystoneGraph } from "../keystone/keystone-helpers.mjs";
|
|
68
|
+
import { SYNC_SAGA_CONTEXT_ATOM } from "./sync-saga-context/sync-saga-context-constants.mjs";
|
|
63
69
|
|
|
64
70
|
|
|
65
71
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
@@ -83,7 +89,7 @@ export class SyncSagaCoordinator {
|
|
|
83
89
|
private lc: string = `[${SyncSagaCoordinator.name}]`;
|
|
84
90
|
|
|
85
91
|
constructor(
|
|
86
|
-
private
|
|
92
|
+
private keystoneSvc: KeystoneService_V1
|
|
87
93
|
) {
|
|
88
94
|
|
|
89
95
|
}
|
|
@@ -94,18 +100,14 @@ export class SyncSagaCoordinator {
|
|
|
94
100
|
* @remarks
|
|
95
101
|
* **Execution Context**: **Sender (Local)**.
|
|
96
102
|
* This method is the entry point for starting a sync session.
|
|
97
|
-
*
|
|
98
|
-
* @param opts.peer - The remote peer witness to communicate with.
|
|
99
|
-
* @param opts.localSpace - The local space that will be read from and written to.
|
|
100
|
-
* @param opts.metaspace - Service for creating temp spaces and managing ibgibs.
|
|
101
|
-
* @param opts.domainIbGibs - The root ibgibs defining the scope of the sync.
|
|
102
|
-
* @param opts.useSessionIdentity - (Optional) Whether to create an ephemeral session identity. Default: true.
|
|
103
103
|
*/
|
|
104
104
|
public async sync({
|
|
105
105
|
peer,
|
|
106
106
|
domainIbGibs,
|
|
107
107
|
conflictStrategy = SyncConflictStrategy.abort,
|
|
108
108
|
useSessionIdentity = true,
|
|
109
|
+
identity,
|
|
110
|
+
identitySecret,
|
|
109
111
|
metaspace,
|
|
110
112
|
localSpace,
|
|
111
113
|
}: {
|
|
@@ -121,7 +123,8 @@ export class SyncSagaCoordinator {
|
|
|
121
123
|
*/
|
|
122
124
|
domainIbGibs: IbGib_V1[],
|
|
123
125
|
/**
|
|
124
|
-
* The space containing the {@link domainIbGibs} we want to sync.
|
|
126
|
+
* The space containing the {@link domainIbGibs} we want to sync. If
|
|
127
|
+
* sync is successful, any updates to timelines will be stored here.
|
|
125
128
|
*/
|
|
126
129
|
localSpace: IbGibSpaceAny;
|
|
127
130
|
/**
|
|
@@ -129,11 +132,17 @@ export class SyncSagaCoordinator {
|
|
|
129
132
|
*/
|
|
130
133
|
metaspace: MetaspaceService;
|
|
131
134
|
/**
|
|
132
|
-
* The identity authorizing this sync.
|
|
135
|
+
* The primary (i.e. non-session) identity authorizing this sync.
|
|
136
|
+
*
|
|
137
|
+
* If this is truthy, then {@link identitySecret} must also be provided.
|
|
133
138
|
*/
|
|
134
139
|
identity?: KeystoneIbGib_V1;
|
|
135
140
|
/**
|
|
136
141
|
* The secret for the identity (to sign the commit).
|
|
142
|
+
*
|
|
143
|
+
* If provided, this will drive both signing {@link identity} keystone
|
|
144
|
+
* (if provided) AND session keystone (if {@link useSessionIdentity} is
|
|
145
|
+
* true).
|
|
137
146
|
*/
|
|
138
147
|
identitySecret?: string;
|
|
139
148
|
/**
|
|
@@ -144,8 +153,10 @@ export class SyncSagaCoordinator {
|
|
|
144
153
|
*/
|
|
145
154
|
conflictStrategy?: SyncConflictStrategy;
|
|
146
155
|
/**
|
|
147
|
-
* If true, creates an ephemeral session identity for the sync process
|
|
148
|
-
* secure the sync transaction itself.
|
|
156
|
+
* If true, creates an ephemeral session identity for the sync process
|
|
157
|
+
* to secure the sync transaction itself.
|
|
158
|
+
*
|
|
159
|
+
* If this is true, {@link identitySecret} must also be provided.
|
|
149
160
|
*
|
|
150
161
|
* @default true
|
|
151
162
|
*/
|
|
@@ -196,9 +207,16 @@ export class SyncSagaCoordinator {
|
|
|
196
207
|
try {
|
|
197
208
|
|
|
198
209
|
// BOOTSTRAP IDENTITY (Session Keystone)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
210
|
+
let sessionIdentity: KeystoneIbGib_V1 | undefined = undefined;
|
|
211
|
+
|
|
212
|
+
if (useSessionIdentity) {
|
|
213
|
+
if (!identitySecret) { throw new Error(`useSessionIdentity is true, but identitySecret is falsy. Must provide a secret if you want to use a session identity. (E: 81915860c4dd3ea4dfd81825fa74c126)`); }
|
|
214
|
+
// creates the initial session identity (keystone). the flow
|
|
215
|
+
// (i think) will go: evolve saga frame, then sign keystone,
|
|
216
|
+
// then create/send context.
|
|
217
|
+
sessionIdentity = await this.getSessionIdentity({ sagaId, identitySecret, metaspace, localSpace });
|
|
218
|
+
}
|
|
219
|
+
|
|
202
220
|
// if (logalot) { console.log(`${lc} sessionIdentity: ${sessionIdentity ? pretty(sessionIdentity) : 'undefined'} (I: abc01872800b3a66b819a05898bba826)`); }
|
|
203
221
|
|
|
204
222
|
// CREATE INITIAL FRAME (Stage.init)
|
|
@@ -214,6 +232,7 @@ export class SyncSagaCoordinator {
|
|
|
214
232
|
await this.executeSagaLoop({
|
|
215
233
|
initFrame, initDomainGraph,
|
|
216
234
|
peer,
|
|
235
|
+
identitySecret,
|
|
217
236
|
sessionIdentity,
|
|
218
237
|
updates$,
|
|
219
238
|
localSpace,
|
|
@@ -266,7 +285,13 @@ export class SyncSagaCoordinator {
|
|
|
266
285
|
* Local temp space relative to the execution context's POV
|
|
267
286
|
*/
|
|
268
287
|
myTempSpace: IbGibSpaceAny,
|
|
288
|
+
/**
|
|
289
|
+
* @see {@link sync} `identity` param.
|
|
290
|
+
*/
|
|
269
291
|
identity?: KeystoneIbGib_V1,
|
|
292
|
+
/**
|
|
293
|
+
* @see {@link sync} `identitySecret` param.
|
|
294
|
+
*/
|
|
270
295
|
identitySecret?: string,
|
|
271
296
|
metaspace: MetaspaceService,
|
|
272
297
|
}): Promise<SyncSagaContextIbGib_V1 | null> {
|
|
@@ -279,7 +304,6 @@ export class SyncSagaCoordinator {
|
|
|
279
304
|
mySpace,
|
|
280
305
|
myTempSpace,
|
|
281
306
|
identity,
|
|
282
|
-
identitySecret,
|
|
283
307
|
metaspace,
|
|
284
308
|
});
|
|
285
309
|
|
|
@@ -303,12 +327,13 @@ export class SyncSagaCoordinator {
|
|
|
303
327
|
// create the return context
|
|
304
328
|
const { frame, payloadIbGibsDomain } = contextResult.nextFrameInfo;
|
|
305
329
|
|
|
306
|
-
const responseCtx = await createSyncSagaContext({
|
|
330
|
+
const responseCtx = await this.createSyncSagaContext({
|
|
307
331
|
sagaFrame: frame,
|
|
308
332
|
localSpace: mySpace,
|
|
309
333
|
payloadIbGibsDomain,
|
|
310
|
-
|
|
311
|
-
|
|
334
|
+
sessionKeystone: identity,
|
|
335
|
+
sessionSecret: identitySecret,
|
|
336
|
+
metaspace,
|
|
312
337
|
});
|
|
313
338
|
|
|
314
339
|
const immediateValidationErrors = await validateContextAndSagaFrame({
|
|
@@ -325,12 +350,74 @@ export class SyncSagaCoordinator {
|
|
|
325
350
|
}
|
|
326
351
|
}
|
|
327
352
|
|
|
353
|
+
private async getSessionSecret({
|
|
354
|
+
sagaId,
|
|
355
|
+
identitySecret,
|
|
356
|
+
}: {
|
|
357
|
+
sagaId: string,
|
|
358
|
+
identitySecret: string,
|
|
359
|
+
}): Promise<string> {
|
|
360
|
+
const lc = `${this.lc}[${this.getSessionSecret.name}]`;
|
|
361
|
+
try {
|
|
362
|
+
if (logalot) { console.log(`${lc} starting... (I: 0de03f8dcd3e32f1fca244e8f2a8a826)`); }
|
|
363
|
+
|
|
364
|
+
// Derive session-specific secret using KDF
|
|
365
|
+
const sessionSecret = await deriveKey({
|
|
366
|
+
masterSecret: identitySecret,
|
|
367
|
+
kdfOpts: {
|
|
368
|
+
strategy: KdfStrategy.recursive_salt_wrap,
|
|
369
|
+
salt: sagaId,
|
|
370
|
+
rounds: 10000,
|
|
371
|
+
algorithm: 'SHA-256'
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
return sessionSecret;
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
378
|
+
throw error;
|
|
379
|
+
} finally {
|
|
380
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private async getInitialWeakDelegateSessionSecret({
|
|
385
|
+
sagaId,
|
|
386
|
+
}: {
|
|
387
|
+
sagaId: string,
|
|
388
|
+
}): Promise<string> {
|
|
389
|
+
const lc = `${this.lc}[${this.getInitialWeakDelegateSessionSecret.name}]`;
|
|
390
|
+
try {
|
|
391
|
+
if (logalot) { console.log(`${lc} starting... (I: 872ab81a78827b9f2822b78459203226)`); }
|
|
392
|
+
|
|
393
|
+
// Create delegate pool bootstrap secret (publicly derivable)
|
|
394
|
+
const initialDelegateSecret = await deriveKey({
|
|
395
|
+
masterSecret: DEFAULT_SESSION_IDENTITY_INITIAL_DELEGATE_SECRET, // Weak, publicly derivable secret
|
|
396
|
+
kdfOpts: {
|
|
397
|
+
strategy: KdfStrategy.recursive_salt_wrap,
|
|
398
|
+
salt: sagaId,
|
|
399
|
+
rounds: 1, // Minimal rounds - this is meant to be weak
|
|
400
|
+
algorithm: 'SHA-256'
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
return initialDelegateSecret;
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
407
|
+
throw error;
|
|
408
|
+
} finally {
|
|
409
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
328
413
|
private async getSessionIdentity({
|
|
329
414
|
sagaId,
|
|
415
|
+
identitySecret,
|
|
330
416
|
metaspace,
|
|
331
417
|
localSpace,
|
|
332
418
|
}: {
|
|
333
419
|
sagaId: string,
|
|
420
|
+
identitySecret: string,
|
|
334
421
|
metaspace: MetaspaceService,
|
|
335
422
|
localSpace: IbGibSpaceAny,
|
|
336
423
|
}): Promise<KeystoneIbGib_V1> {
|
|
@@ -338,20 +425,59 @@ export class SyncSagaCoordinator {
|
|
|
338
425
|
try {
|
|
339
426
|
if (logalot) { console.log(`${lc} starting... (I: 428392a4ee636b7bd8f7d5d89a87e826)`); }
|
|
340
427
|
|
|
341
|
-
|
|
342
|
-
|
|
428
|
+
if (!identitySecret) { throw new Error(`(UNEXPECTED) identitySecret falsy? This is expected to be truthy by this point. (E: 8ce053fe59825a6678713128953b9d26)`); }
|
|
429
|
+
|
|
430
|
+
const sessionSecret = await this.getSessionSecret({ sagaId, identitySecret });
|
|
431
|
+
|
|
432
|
+
const init = await this.getInitialWeakDelegateSessionSecret({ sagaId });
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
// Create TWO pool configs: Primary (strong) + Delegate (weak bootstrap)
|
|
436
|
+
const primaryPoolConfig: KeystonePoolConfig_HashV1 = {
|
|
437
|
+
allowedVerbs: [KEYSTONE_VERB_MANAGE],
|
|
438
|
+
id: 'primary',
|
|
439
|
+
type: 'hash-reveal-v1',
|
|
440
|
+
salt: sagaId,
|
|
441
|
+
behavior: {
|
|
442
|
+
size: 100, // Large pool for many signatures
|
|
443
|
+
replenish: 'top-up',
|
|
444
|
+
selectSequentially: 2,
|
|
445
|
+
selectRandomly: 2,
|
|
446
|
+
targetBindingChars: 10
|
|
447
|
+
},
|
|
448
|
+
algo: 'SHA-256',
|
|
449
|
+
rounds: 1
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const delegatePoolConfig: any = {
|
|
453
|
+
id: 'delegate',
|
|
343
454
|
type: 'hash-reveal-v1',
|
|
344
455
|
salt: sagaId,
|
|
345
|
-
behavior: {
|
|
346
|
-
|
|
456
|
+
behavior: {
|
|
457
|
+
size: 10, // Small pool - only for initial handshake
|
|
458
|
+
replenish: 'top-up',
|
|
459
|
+
selectSequentially: 1,
|
|
460
|
+
selectRandomly: 1,
|
|
461
|
+
targetBindingChars: 0
|
|
462
|
+
},
|
|
463
|
+
algo: 'SHA-256',
|
|
464
|
+
rounds: 1
|
|
347
465
|
};
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
466
|
+
|
|
467
|
+
// Generate keystone with DUAL pools. We have to first genesis with
|
|
468
|
+
// one and then add the other, because we have two distinct
|
|
469
|
+
// secrets.
|
|
470
|
+
const sessionIdentity = await this.keystoneSvc.genesis({
|
|
471
|
+
masterSecret: sessionSecret,
|
|
472
|
+
configs: [primaryPoolConfig],
|
|
351
473
|
metaspace,
|
|
352
|
-
space: localSpace
|
|
474
|
+
space: localSpace
|
|
353
475
|
});
|
|
354
476
|
|
|
477
|
+
// TODO: Store delegate pool separate challenges derived from bootstrap secret
|
|
478
|
+
// This allows receiver to verify/evolve the delegate pool
|
|
479
|
+
// For now, the keystone genesis with multiple configs handles this
|
|
480
|
+
|
|
355
481
|
return sessionIdentity;
|
|
356
482
|
} catch (error) {
|
|
357
483
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
@@ -381,6 +507,7 @@ export class SyncSagaCoordinator {
|
|
|
381
507
|
initDomainGraph,
|
|
382
508
|
peer,
|
|
383
509
|
sessionIdentity,
|
|
510
|
+
identitySecret,
|
|
384
511
|
updates$,
|
|
385
512
|
localSpace,
|
|
386
513
|
tempSpace,
|
|
@@ -396,6 +523,10 @@ export class SyncSagaCoordinator {
|
|
|
396
523
|
initDomainGraph: FlatIbGibGraph,
|
|
397
524
|
peer: SyncPeerWitness,
|
|
398
525
|
sessionIdentity?: KeystoneIbGib_V1,
|
|
526
|
+
/**
|
|
527
|
+
* if {@link sessionIdentity} provided, this must also be truthy
|
|
528
|
+
*/
|
|
529
|
+
identitySecret?: string,
|
|
399
530
|
updates$: SubjectWitness<SyncSagaContextIbGib_V1>,
|
|
400
531
|
metaspace: MetaspaceService
|
|
401
532
|
localSpace: IbGibSpaceAny,
|
|
@@ -444,9 +575,10 @@ export class SyncSagaCoordinator {
|
|
|
444
575
|
// #endregion set up peer observable for any domainPayloadsMap
|
|
445
576
|
|
|
446
577
|
// ...create/compose the Request Context itself...
|
|
447
|
-
const requestCtx = await createSyncSagaContext({
|
|
578
|
+
const requestCtx = await this.createSyncSagaContext({
|
|
448
579
|
sagaFrame: currentFrame,
|
|
449
|
-
|
|
580
|
+
sessionKeystone: sessionIdentity,
|
|
581
|
+
sessionSecret: identitySecret,
|
|
450
582
|
/**
|
|
451
583
|
* init frame: empty
|
|
452
584
|
* ack frame: possible push offers
|
|
@@ -455,6 +587,7 @@ export class SyncSagaCoordinator {
|
|
|
455
587
|
*/
|
|
456
588
|
payloadIbGibsDomain: nextDomainIbGibs,
|
|
457
589
|
localSpace,
|
|
590
|
+
metaspace,
|
|
458
591
|
});
|
|
459
592
|
|
|
460
593
|
// #region Log what we're sending
|
|
@@ -582,6 +715,146 @@ export class SyncSagaCoordinator {
|
|
|
582
715
|
}
|
|
583
716
|
}
|
|
584
717
|
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Creates new SyncSagaContext stone. Puts/registers in {@link localSpace}
|
|
721
|
+
* immediately after creation.
|
|
722
|
+
*
|
|
723
|
+
* @returns The context ibGib.
|
|
724
|
+
*
|
|
725
|
+
* ## notes
|
|
726
|
+
*
|
|
727
|
+
* the other ibgibs that are related to this context stone should already be
|
|
728
|
+
* put/registered in {@link localSpace}.
|
|
729
|
+
*/
|
|
730
|
+
private async createSyncSagaContext({
|
|
731
|
+
sagaFrame,
|
|
732
|
+
sessionKeystone,
|
|
733
|
+
sessionSecret,
|
|
734
|
+
payloadIbGibsDomain,
|
|
735
|
+
metaspace,
|
|
736
|
+
localSpace,
|
|
737
|
+
}: {
|
|
738
|
+
/**
|
|
739
|
+
* The main saga frame (Init, Ack, etc.).
|
|
740
|
+
*/
|
|
741
|
+
sagaFrame: SyncIbGib_V1;
|
|
742
|
+
/**
|
|
743
|
+
* Session identity keystone.
|
|
744
|
+
*/
|
|
745
|
+
sessionKeystone: KeystoneIbGib_V1 | undefined;
|
|
746
|
+
/**
|
|
747
|
+
* If using session ({@link sessionKeystone} is truthy), this must be
|
|
748
|
+
* provided in order to sign it.
|
|
749
|
+
*/
|
|
750
|
+
sessionSecret: string | undefined;
|
|
751
|
+
/**
|
|
752
|
+
* Domain payload ibgibs when the sync saga frame includes actual domain
|
|
753
|
+
* payloads to send, e.g., in a Delta frame.
|
|
754
|
+
*/
|
|
755
|
+
payloadIbGibsDomain?: IbGib_V1[];
|
|
756
|
+
/**
|
|
757
|
+
* reference to the current metaspace
|
|
758
|
+
*/
|
|
759
|
+
metaspace: MetaspaceService,
|
|
760
|
+
/**
|
|
761
|
+
* we persist the context in the local/sender space (relative to our
|
|
762
|
+
* execution POV) right when we create it.
|
|
763
|
+
*/
|
|
764
|
+
localSpace: IbGibSpaceAny;
|
|
765
|
+
}): Promise<SyncSagaContextIbGib_V1> {
|
|
766
|
+
const lc = `[${this.createSyncSagaContext.name}]`;
|
|
767
|
+
try {
|
|
768
|
+
if (logalot) { console.log(`${lc} starting... (I: 6b87bee313e811d1d2fc90e87fbec826)`); }
|
|
769
|
+
|
|
770
|
+
// #region sanity/validation
|
|
771
|
+
if (!sagaFrame.data) { throw new Error(`(UNEXPECTED) sagaFrame.data falsy? (E: 04c49b4cccba6842a8b52e4c6f570726)`); }
|
|
772
|
+
if (!sagaFrame.data.n && sagaFrame.data.n !== 0) { throw new Error(`(UNEXPECTED) sagaFrame.data.n falsy and not 0? (E: 45b508da64a8b28428b11765d684b826)`); }
|
|
773
|
+
if (sessionKeystone && !sessionSecret) { throw new Error(`(UNEXPECTED) sessionKeystone truthy but sessionSecret falsy? (E: 705ecc25038b12df0e94c90c5561e426)`); }
|
|
774
|
+
// #endregion sanity/validation
|
|
775
|
+
|
|
776
|
+
const date = new Date();
|
|
777
|
+
const timestamp = getTimestamp(date);
|
|
778
|
+
const timestampMs = date.getMilliseconds();
|
|
779
|
+
|
|
780
|
+
const data: SyncSagaContextData_V1 = {
|
|
781
|
+
timestamp,
|
|
782
|
+
timestampMs,
|
|
783
|
+
sagaN: sagaFrame.data.n,
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// Domain Payloads
|
|
787
|
+
const payloadAddrsDomain = payloadIbGibsDomain ?
|
|
788
|
+
payloadIbGibsDomain?.map(x => getIbGibAddr({ ibGib: x })) :
|
|
789
|
+
undefined;
|
|
790
|
+
if (payloadAddrsDomain && payloadAddrsDomain.length > 0) {
|
|
791
|
+
data[SYNC_SAGA_PAYLOAD_ADDRS_DOMAIN] = payloadAddrsDomain;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// rel8ns should always have saga frame, sometimes have keystone
|
|
795
|
+
const rel8ns: SyncSagaContextRel8ns_V1 = {
|
|
796
|
+
sagaFrame: [getIbGibAddr({ ibGib: sagaFrame })],
|
|
797
|
+
};
|
|
798
|
+
if (sessionKeystone) {
|
|
799
|
+
const keystoneErrors = await validateKeystoneGraph({
|
|
800
|
+
keystoneIbGib: sessionKeystone,
|
|
801
|
+
space: localSpace
|
|
802
|
+
});
|
|
803
|
+
if (keystoneErrors.length > 0) {
|
|
804
|
+
throw new Error(`invalid sessionKeystone. errors: ${keystoneErrors} (E: 3881b8caf2d803767a331e1141e84826)`);
|
|
805
|
+
}
|
|
806
|
+
// this addr is BEFORE we sign. So each context ibgib itself will
|
|
807
|
+
// point to the frame of the keystone just before that keystone
|
|
808
|
+
// signs with this context as its target.
|
|
809
|
+
rel8ns.sessionKeystone = [getIbGibAddr({ ibGib: sessionKeystone })];
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Generate standard ib
|
|
813
|
+
const ib = await getSyncSagaContextIb({ data });
|
|
814
|
+
|
|
815
|
+
const contextIbGib = await Factory_V1.stone<SyncSagaContextData_V1, SyncSagaContextRel8ns_V1>({
|
|
816
|
+
parentPrimitiveIb: SYNC_SAGA_CONTEXT_ATOM,
|
|
817
|
+
ib,
|
|
818
|
+
data,
|
|
819
|
+
rel8ns,
|
|
820
|
+
}) as SyncSagaContextIbGib_V1;
|
|
821
|
+
|
|
822
|
+
// put/register immediately. Note that contextIbGib at this point is
|
|
823
|
+
// pure DTO, i.e., only ib, gib, data, rel8ns props.
|
|
824
|
+
await putInSpace({ ibGib: contextIbGib, space: localSpace, });
|
|
825
|
+
await registerNewIbGib({
|
|
826
|
+
ibGib: contextIbGib,
|
|
827
|
+
space: localSpace,
|
|
828
|
+
fnBroadcast: undefined,
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
// Attach actual ibgibs for transport (not pure DTO now)
|
|
832
|
+
contextIbGib.sagaFrame = sagaFrame;
|
|
833
|
+
if (payloadIbGibsDomain && payloadIbGibsDomain.length > 0) {
|
|
834
|
+
contextIbGib.payloadIbGibsDomain = payloadIbGibsDomain;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (sessionKeystone) {
|
|
838
|
+
if (!sessionSecret) { throw new Error(`(UNEXPECTED) sessionKeystone truthy but sessionSecret falsy? we should have thrown before now (E: a2b0517a37b559543968b888f2067826)`); }
|
|
839
|
+
const contextAddr = getIbGibAddr({ ibGib: contextIbGib });
|
|
840
|
+
contextIbGib.signedSessionKeystone = await this.keystoneSvc.sign({
|
|
841
|
+
latestKeystone: sessionKeystone,
|
|
842
|
+
claim: { target: contextAddr, }, // verb?
|
|
843
|
+
space: localSpace,
|
|
844
|
+
masterSecret: sessionSecret,
|
|
845
|
+
metaspace,
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
return contextIbGib;
|
|
850
|
+
} catch (error) {
|
|
851
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
852
|
+
throw error;
|
|
853
|
+
} finally {
|
|
854
|
+
if (logalot) { console.log(`${lc} complete.`); }
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
585
858
|
/**
|
|
586
859
|
* Helper to get Knowledge Map for specific domain ibGibs or TJPs.
|
|
587
860
|
* Useful for testing and external validation.
|
|
@@ -755,7 +1028,7 @@ export class SyncSagaCoordinator {
|
|
|
755
1028
|
const sagaFrame = await this.evolveSyncSagaIbGib({
|
|
756
1029
|
msgStones: [initStone],
|
|
757
1030
|
conflictStrategy,
|
|
758
|
-
sessionIdentity
|
|
1031
|
+
sessionIdentity,
|
|
759
1032
|
metaspace,
|
|
760
1033
|
localSpace,
|
|
761
1034
|
});
|
|
@@ -873,7 +1146,6 @@ export class SyncSagaCoordinator {
|
|
|
873
1146
|
mySpace,
|
|
874
1147
|
myTempSpace,
|
|
875
1148
|
identity,
|
|
876
|
-
identitySecret,
|
|
877
1149
|
metaspace,
|
|
878
1150
|
}: {
|
|
879
1151
|
sagaContext: SyncSagaContextIbGib_V1,
|
|
@@ -893,7 +1165,6 @@ export class SyncSagaCoordinator {
|
|
|
893
1165
|
*/
|
|
894
1166
|
myTempSpace: IbGibSpaceAny,
|
|
895
1167
|
identity?: KeystoneIbGib_V1,
|
|
896
|
-
identitySecret?: string,
|
|
897
1168
|
metaspace: MetaspaceService,
|
|
898
1169
|
}): Promise<HandleSagaResponseContextResult> {
|
|
899
1170
|
const lc = `${this.lc}[${this.handleResponseSagaContext.name}]`;
|
|
@@ -916,7 +1187,7 @@ export class SyncSagaCoordinator {
|
|
|
916
1187
|
sagaIbGib,
|
|
917
1188
|
messageData: messageData as SyncSagaMessageInitData_V1,
|
|
918
1189
|
metaspace, mySpace, myTempSpace,
|
|
919
|
-
identity,
|
|
1190
|
+
identity,
|
|
920
1191
|
});
|
|
921
1192
|
break;
|
|
922
1193
|
|
|
@@ -987,7 +1258,6 @@ export class SyncSagaCoordinator {
|
|
|
987
1258
|
myTempSpace,
|
|
988
1259
|
metaspace,
|
|
989
1260
|
identity,
|
|
990
|
-
// identitySecret,
|
|
991
1261
|
}: {
|
|
992
1262
|
sagaIbGib: SyncIbGib_V1,
|
|
993
1263
|
messageData: SyncSagaMessageInitData_V1,
|
|
@@ -1004,7 +1274,6 @@ export class SyncSagaCoordinator {
|
|
|
1004
1274
|
myTempSpace: IbGibSpaceAny,
|
|
1005
1275
|
metaspace: MetaspaceService,
|
|
1006
1276
|
identity?: KeystoneIbGib_V1,
|
|
1007
|
-
identitySecret?: string,
|
|
1008
1277
|
}): Promise<NextSagaFrameInfo> {
|
|
1009
1278
|
const lc = `${this.lc}[${this.handleInitFrame.name}]`;
|
|
1010
1279
|
try {
|
|
@@ -2530,10 +2799,9 @@ export class SyncSagaCoordinator {
|
|
|
2530
2799
|
localSpace: IbGibSpaceAny,
|
|
2531
2800
|
metaspace: MetaspaceService,
|
|
2532
2801
|
/**
|
|
2533
|
-
* does NOT evolve the keystone.
|
|
2534
|
-
* (NOT IMPLEMENTED YET ANYWAY)
|
|
2802
|
+
* does NOT evolve the keystone.
|
|
2535
2803
|
*/
|
|
2536
|
-
sessionIdentity
|
|
2804
|
+
sessionIdentity: KeystoneIbGib_V1 | undefined,
|
|
2537
2805
|
}): Promise<SyncIbGib_V1> {
|
|
2538
2806
|
const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;
|
|
2539
2807
|
try {
|
|
@@ -2617,6 +2885,11 @@ export class SyncSagaCoordinator {
|
|
|
2617
2885
|
const rel8ns: SyncRel8ns_V1 = { [SYNC_MSG_REL8N_NAME]: stoneAddrs, };
|
|
2618
2886
|
if (identityAddr) { rel8ns.identity = [identityAddr]; }
|
|
2619
2887
|
|
|
2888
|
+
// Attach session keystone to saga frame via hard rel8n
|
|
2889
|
+
if (sessionIdentity) {
|
|
2890
|
+
rel8ns.sessionKeystones = [getIbGibAddr({ ibGib: sessionIdentity })];
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2620
2893
|
const resNew = await createTimeline({
|
|
2621
2894
|
space: localSpace,
|
|
2622
2895
|
metaspace,
|
|
@@ -55,7 +55,7 @@ export interface SyncSagaMessageIbGib_V1 extends IbGib_V1<SyncSagaMessageData_V1
|
|
|
55
55
|
/**
|
|
56
56
|
* The "Hello" of the sync protocol.
|
|
57
57
|
*
|
|
58
|
-
* Exchanges knowledge maps (what I have)
|
|
58
|
+
* Exchanges knowledge maps (what I have).
|
|
59
59
|
*/
|
|
60
60
|
export interface SyncSagaMessageInitData_V1 extends SyncSagaMessageData_V1 {
|
|
61
61
|
stage: typeof SyncStage.init;
|
|
@@ -68,12 +68,6 @@ export interface SyncSagaMessageInitData_V1 extends SyncSagaMessageData_V1 {
|
|
|
68
68
|
*/
|
|
69
69
|
knowledgeMap: { [tjp: string]: IbGibAddr };
|
|
70
70
|
|
|
71
|
-
/**
|
|
72
|
-
* The Keystone Identity of the sender.
|
|
73
|
-
* Required for the first handshake or if the receiver doesn't know the sender.
|
|
74
|
-
*/
|
|
75
|
-
identity?: KeystoneIbGib_V1;
|
|
76
|
-
|
|
77
71
|
/**
|
|
78
72
|
* What the sender wants to do.
|
|
79
73
|
* e.g. "push" (I have data for you) or "pull" (Give me data).
|
package/src/sync/sync-types.mts
CHANGED
|
@@ -195,6 +195,18 @@ export interface SyncRel8ns_V1 extends IbGibRel8ns_V1 {
|
|
|
195
195
|
*/
|
|
196
196
|
identity?: string[];
|
|
197
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Session keystones used for signing saga frames.
|
|
200
|
+
*
|
|
201
|
+
* Array contains addresses of keystone evolution chain:
|
|
202
|
+
* - Index 0: Genesis keystone (dual-pool architecture)
|
|
203
|
+
* - Index N: Latest evolved keystone after signing operations
|
|
204
|
+
*
|
|
205
|
+
* Each sync endpoint retrieves the session keystone from this rel8n
|
|
206
|
+
* rather than searching spaces. Keystones are stored in durable spaces.
|
|
207
|
+
*/
|
|
208
|
+
sessionKeystones?: IbGibAddr[];
|
|
209
|
+
|
|
198
210
|
/**
|
|
199
211
|
* The message stone that contains the information about the particular
|
|
200
212
|
* stage of the sync process we are in.
|