@ibgib/core-gib 0.1.61 → 0.1.63
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/CHANGELOG.md +13 -0
- package/dist/keystone/keystone-constants.d.mts +0 -1
- package/dist/keystone/keystone-constants.d.mts.map +1 -1
- package/dist/keystone/keystone-constants.mjs +0 -1
- package/dist/keystone/keystone-constants.mjs.map +1 -1
- package/dist/keystone/keystone-helpers.d.mts +23 -1
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +98 -5
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.d.mts +39 -2
- package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.mjs +175 -3
- package/dist/keystone/keystone-service-v1.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.respec.mjs +397 -42
- package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
- package/dist/keystone/keystone-types.d.mts +54 -0
- package/dist/keystone/keystone-types.d.mts.map +1 -1
- package/dist/keystone/policy/keystone-profile-builder.d.mts +1 -1
- package/dist/keystone/policy/keystone-profile-builder.d.mts.map +1 -1
- package/dist/keystone/policy/keystone-profile-builder.mjs +8 -0
- package/dist/keystone/policy/keystone-profile-builder.mjs.map +1 -1
- package/dist/keystone/policy/profiles/profile-domain.json +84 -0
- package/dist/keystone/policy/profiles/profile-sync.json +84 -0
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.d.mts +8 -0
- 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 +15 -1
- package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs.map +1 -1
- package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs +16 -4
- package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs +4 -1
- package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs +4 -1
- package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-text-merge.withid.respec.mjs +10 -4
- package/dist/sync/sync-conflict-text-merge.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-constants.withid.respec.mjs +12 -3
- package/dist/sync/sync-innerspace-constants.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.withid.respec.mjs +4 -1
- package/dist/sync/sync-innerspace.withid.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts +4 -0
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +4 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +90 -22
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +7 -10
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +14 -5
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-types.d.mts +5 -0
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs.map +1 -1
- package/dist/sync/sync-withid.connect.respec.mjs +4 -1
- package/dist/sync/sync-withid.connect.respec.mjs.map +1 -1
- package/dist/sync/sync-withid.establish.respec.mjs +4 -1
- package/dist/sync/sync-withid.establish.respec.mjs.map +1 -1
- package/dist/sync/sync-withid.pingpong.respec.mjs +4 -1
- package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -1
- package/package.json +1 -1
- package/src/keystone/README.md +15 -2
- package/src/keystone/docs/architecture.md +21 -2
- package/src/keystone/docs/delegation.md +57 -0
- package/src/keystone/keystone-constants.mts +0 -1
- package/src/keystone/keystone-helpers.mts +131 -5
- package/src/keystone/keystone-service-v1.mts +223 -2
- package/src/keystone/keystone-service-v1.respec.mts +436 -40
- package/src/keystone/keystone-types.mts +55 -0
- package/src/keystone/policy/keystone-profile-builder.mts +9 -1
- package/src/keystone/policy/profiles/profile-domain.json +84 -0
- package/src/keystone/policy/profiles/profile-sync.json +84 -0
- package/src/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mts +20 -1
- package/src/sync/sync-conflict-adv-multitimelines.withid.respec.mts +16 -4
- package/src/sync/sync-conflict-basic-divergence.withid.respec.mts +4 -1
- package/src/sync/sync-conflict-basic-multitimelines.withid.respec.mts +4 -1
- package/src/sync/sync-conflict-text-merge.withid.respec.mts +10 -4
- package/src/sync/sync-innerspace-constants.withid.respec.mts +12 -3
- package/src/sync/sync-innerspace-deep-updates.withid.respec.mts +4 -1
- package/src/sync/sync-innerspace-dest-ahead.withid.respec.mts +4 -1
- package/src/sync/sync-innerspace-multiple-timelines.withid.respec.mts +4 -1
- package/src/sync/sync-innerspace-partial-update.withid.respec.mts +4 -1
- package/src/sync/sync-innerspace.withid.respec.mts +4 -1
- package/src/sync/sync-peer/sync-peer-types.mts +4 -0
- package/src/sync/sync-peer/sync-peer-v1.mts +5 -1
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +96 -22
- package/src/sync/sync-saga-coordinator.mts +17 -12
- package/src/sync/sync-types.mts +6 -0
- package/src/sync/sync-withid.connect.respec.mts +5 -2
- package/src/sync/sync-withid.establish.respec.mts +4 -1
- package/src/sync/sync-withid.pingpong.respec.mts +4 -1
- package/src/sync/unused-identity-backup.mts.md +0 -311
|
@@ -21,7 +21,8 @@ import { KeystoneStrategyFactory } from '../../../../keystone/strategy/keystone-
|
|
|
21
21
|
import { KeystoneService_V1 } from '../../../../keystone/keystone-service-v1.mjs';
|
|
22
22
|
import { IbGibSpaceAny } from '../../../../witness/space/space-base-v1.mjs';
|
|
23
23
|
import { MetaspaceService } from '../../../../witness/space/metaspace/metaspace-types.mjs';
|
|
24
|
-
import { POOL_ID_CONNECT, KEYSTONE_VERB_CONNECT } from '../../../../keystone/keystone-constants.mjs';
|
|
24
|
+
import { POOL_ID_CONNECT, KEYSTONE_VERB_CONNECT, KEYSTONE_VERB_SYNC } from '../../../../keystone/keystone-constants.mjs';
|
|
25
|
+
import { getTjpAddr } from '../../../../common/other/ibgib-helper.mjs';
|
|
25
26
|
|
|
26
27
|
const logalot = GLOBAL_LOG_A_LOT
|
|
27
28
|
|
|
@@ -297,35 +298,77 @@ export async function validateAndRegisterEvolveKeystone({
|
|
|
297
298
|
throw new Error(`Intrinsic keystone validation failed: ${intrinsicErrors.join(', ')} (E: 38ea984585edc6ee88d2a698c7895826)`);
|
|
298
299
|
}
|
|
299
300
|
|
|
300
|
-
// 2.
|
|
301
|
-
const infoAddr = getGibInfo({ ibGibAddr: addr });
|
|
302
|
-
const tjpGib = infoAddr.tjpGib ?? infoAddr.punctiliarHash;
|
|
303
|
-
const infoDomain = getGibInfo({ ibGibAddr: domainAddr });
|
|
304
|
-
const expectedTjpGib = infoDomain.tjpGib ?? infoDomain.punctiliarHash;
|
|
305
|
-
if (tjpGib !== expectedTjpGib) {
|
|
306
|
-
throw new Error(`Keystone tjpGib (${tjpGib}) does not match URL domainAddr tjpGib (${expectedTjpGib})`);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// 3. Load previous frame (the current tip in metaspace)
|
|
301
|
+
// 2. Load the latest root Domain Identity tip in metaspace
|
|
310
302
|
const keystoneService = new KeystoneService_V1();
|
|
311
|
-
let
|
|
303
|
+
let parentKeystone: KeystoneIbGib_V1;
|
|
312
304
|
try {
|
|
313
|
-
|
|
305
|
+
parentKeystone = await keystoneService.getLatestKeystone({
|
|
314
306
|
addr: domainAddr,
|
|
315
307
|
metaspace,
|
|
316
308
|
space
|
|
317
309
|
});
|
|
318
310
|
} catch (error) {
|
|
319
|
-
throw new Error(`Could not retrieve
|
|
311
|
+
throw new Error(`Could not retrieve root Domain Identity tip for ${domainAddr}: ${extractErrorMsg(error)} (E: b73ed8aeb0d837d248718b487345d326)`);
|
|
320
312
|
}
|
|
321
313
|
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
|
|
314
|
+
const parentTjpAddr = getTjpAddr({ ibGib: parentKeystone });
|
|
315
|
+
const incomingTjpAddr = getTjpAddr({ ibGib: keystoneIbGib });
|
|
316
|
+
if (!incomingTjpAddr) {
|
|
317
|
+
throw new Error(`Incoming keystone does not have a TJP address (E: d288fae39b7d825d)`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const isDelegate = incomingTjpAddr !== parentTjpAddr;
|
|
321
|
+
let prevIbGib: KeystoneIbGib_V1 | undefined;
|
|
322
|
+
|
|
323
|
+
if (isDelegate) {
|
|
324
|
+
// Verify that the delegate is registered under the root Domain Identity
|
|
325
|
+
const delegateInfo = parentKeystone.data?.delegates?.[incomingTjpAddr];
|
|
326
|
+
if (!delegateInfo) {
|
|
327
|
+
throw new Error(`Incoming delegate keystone (${incomingTjpAddr}) is not registered under parent Domain Identity (${parentTjpAddr}) (E: c288fae39b7d82fe)`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Verify that the delegate is authorized for sync
|
|
331
|
+
const allowedVerbs = delegateInfo.allowedVerbs ?? [];
|
|
332
|
+
if (!allowedVerbs.includes(KEYSTONE_VERB_SYNC)) {
|
|
333
|
+
throw new Error(`Incoming delegate keystone is not authorized for sync. Allowed verbs: ${allowedVerbs.join(', ')} (E: a288fae39b7d82ff)`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Load delegate's latest frame from space (if any)
|
|
337
|
+
const delegateLatestAddr = await metaspace.getLatestAddr({
|
|
338
|
+
addr: incomingTjpAddr,
|
|
339
|
+
space
|
|
340
|
+
});
|
|
341
|
+
if (delegateLatestAddr) {
|
|
342
|
+
const resGet = await metaspace.get({ addr: delegateLatestAddr, space });
|
|
343
|
+
if (resGet.success && resGet.ibGibs && resGet.ibGibs.length > 0) {
|
|
344
|
+
prevIbGib = resGet.ibGibs[0] as KeystoneIbGib_V1;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} else {
|
|
348
|
+
// For root Domain Identity evolution, it must transition from the parent keystone tip
|
|
349
|
+
prevIbGib = parentKeystone;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// 3. Validate Evolution Transition or Graph
|
|
353
|
+
if (prevIbGib) {
|
|
354
|
+
const validationErrors = await keystoneService.validate({
|
|
355
|
+
currentIbGib: keystoneIbGib,
|
|
356
|
+
prevIbGib
|
|
357
|
+
});
|
|
358
|
+
if (validationErrors && validationErrors.length > 0) {
|
|
359
|
+
throw new Error(`Keystone evolution validation failed: ${validationErrors.join(', ')} (E: 5212f8492ac839953842631834bcd826)`);
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
// Validate delegate's genesis or entire graph if no previous tip exists
|
|
363
|
+
const validationErrors = await keystoneService.validateKeystoneGraph({
|
|
364
|
+
keystoneIbGib,
|
|
365
|
+
getLatest: false,
|
|
366
|
+
invalidIfMoreRecentKeystoneFoundInSpace: false,
|
|
367
|
+
space
|
|
368
|
+
});
|
|
369
|
+
if (validationErrors && validationErrors.length > 0) {
|
|
370
|
+
throw new Error(`Keystone graph validation failed: ${validationErrors.join(', ')} (E: 3a288fae39b7d82e)`);
|
|
371
|
+
}
|
|
329
372
|
}
|
|
330
373
|
|
|
331
374
|
// 5. Check Single Pool Restriction
|
|
@@ -352,8 +395,39 @@ export async function validateAndRegisterEvolveKeystone({
|
|
|
352
395
|
const { verb } = claim;
|
|
353
396
|
if (!verb) { throw new Error(`claim.verb is falsy (E: 92cf38af58db2cd90e7132b8940fe326)`); }
|
|
354
397
|
|
|
398
|
+
if (verb === 'manage') {
|
|
399
|
+
const prevDelegates = prevIbGib?.data?.delegates ?? {};
|
|
400
|
+
const currDelegates = keystoneIbGib.data?.delegates ?? {};
|
|
401
|
+
for (const [delegateTjpAddr, delegateInfo] of Object.entries(currDelegates)) {
|
|
402
|
+
if (!prevDelegates[delegateTjpAddr]) {
|
|
403
|
+
const delegateAddr = delegateInfo.delegateAddr;
|
|
404
|
+
const delegateIbGib = relatedIbGibs.find(ibGib => getIbGibAddr({ ibGib }) === delegateAddr);
|
|
405
|
+
if (!delegateIbGib) {
|
|
406
|
+
throw new Error(`Evolved delegate keystone ${delegateAddr} must be provided in relatedIbGibs (E: 1fe5a68438e288ab4e86c90d81d142826)`);
|
|
407
|
+
}
|
|
408
|
+
const delegateErrors = await keystoneService.validateGenesisKeystone({ keystoneIbGib: delegateIbGib as any });
|
|
409
|
+
if (delegateErrors && delegateErrors.length > 0) {
|
|
410
|
+
throw new Error(`Genesis validation failed for delegate keystone ${delegateAddr}: ${delegateErrors.join(', ')} (E: d8afdc1dbed48c000455b35585dc26826)`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
await metaspace.put({ ibGibs: [keystoneIbGib, ...relatedIbGibs], space });
|
|
416
|
+
await metaspace.registerNewIbGib({ ibGib: keystoneIbGib, space });
|
|
417
|
+
for (const related of relatedIbGibs) {
|
|
418
|
+
if (related.ib?.startsWith('keystone')) {
|
|
419
|
+
await metaspace.registerNewIbGib({ ibGib: related, space });
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
355
425
|
if (verb !== 'sync') {
|
|
356
|
-
throw new Error(`non-sync keystone evolution not yet implemented (E: eca4681b05486d1f784c0cb815ef9926)`);
|
|
426
|
+
throw new Error(`non-sync/non-manage keystone evolution not yet implemented (E: eca4681b05486d1f784c0cb815ef9926)`);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (!isDelegate) {
|
|
430
|
+
throw new Error(`Root Domain Identity (${parentTjpAddr}) is not allowed to sync directly. A delegate must be used. (E: 2c88fae39b7d82fd)`);
|
|
357
431
|
}
|
|
358
432
|
|
|
359
433
|
const sAddr = claim.target;
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
DomainIbGibAnalysisInfo, NextSagaFrameInfo,
|
|
32
32
|
HandleSagaResponseContextResult,
|
|
33
33
|
SyncSagaFrameDependencyGraph,
|
|
34
|
+
SyncSenderIdentityInfo,
|
|
34
35
|
} from "./sync-types.mjs";
|
|
35
36
|
import { getSyncSagaFrameOrigin, getFullSyncSagaHistory, getSyncIb, getTempSpaceName, isPastFrame, putInSpace_dnasThenNonDnas, validateFullSyncSagaHistory, getAllOrphanedAddresses, getFinalConflictsInfo, getSyncSagaFrameDependencyGraph } from "./sync-helpers.mjs";
|
|
36
37
|
import { getDeltaDependencyGraph, getDependencyGraph, toFlatGraph } from "../common/other/graph-helper.mjs";
|
|
@@ -143,7 +144,7 @@ export class SyncSagaCoordinator {
|
|
|
143
144
|
public async sync({
|
|
144
145
|
peer,
|
|
145
146
|
domainIbGibs,
|
|
146
|
-
|
|
147
|
+
senderIdentityInfo,
|
|
147
148
|
fnSenderSecret,
|
|
148
149
|
conflictStrategy = SyncConflictStrategy.abort,
|
|
149
150
|
metaspace,
|
|
@@ -161,19 +162,16 @@ export class SyncSagaCoordinator {
|
|
|
161
162
|
*/
|
|
162
163
|
domainIbGibs: IbGib_V1[],
|
|
163
164
|
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* This should be the most recent frame (tip) of the senderIdentity's
|
|
168
|
-
* timeline.
|
|
165
|
+
* Optional structure containing both the sender's delegate identity (Alice's device key)
|
|
166
|
+
* and the parent root Domain Identity address (senderDomainAddr).
|
|
169
167
|
*
|
|
170
168
|
* NOTE: {@link fnSenderSecret} must be truthy if this is truthy, and vice versa.
|
|
171
169
|
*/
|
|
172
|
-
|
|
170
|
+
senderIdentityInfo?: SyncSenderIdentityInfo,
|
|
173
171
|
/**
|
|
174
|
-
* secret corresponding to {@link senderIdentity}.
|
|
172
|
+
* secret corresponding to {@link senderIdentityInfo.senderIdentity}.
|
|
175
173
|
*
|
|
176
|
-
* NOTE: {@link
|
|
174
|
+
* NOTE: {@link senderIdentityInfo} must be truthy if this is truthy, and vice versa.
|
|
177
175
|
*/
|
|
178
176
|
fnSenderSecret?: () => Promise<string>;
|
|
179
177
|
/**
|
|
@@ -201,11 +199,17 @@ export class SyncSagaCoordinator {
|
|
|
201
199
|
throw new Error(`${lc} source (or localSpace) required (E: 25df3761f7686a1099a552f83c95d326)`);
|
|
202
200
|
}
|
|
203
201
|
|
|
204
|
-
if (
|
|
205
|
-
if (!
|
|
206
|
-
if (!fnSenderSecret) { throw new Error(`(UNEXPECTED) fnSenderSecret falsy but
|
|
202
|
+
if (senderIdentityInfo || fnSenderSecret) {
|
|
203
|
+
if (!senderIdentityInfo) { throw new Error(`(UNEXPECTED) senderIdentityInfo falsy but fnSenderSecret truthy? if either is used, both must be truthy. (E: e366628a4919c7d727d2cbd8e5b75e26)`); }
|
|
204
|
+
if (!fnSenderSecret) { throw new Error(`(UNEXPECTED) fnSenderSecret falsy but senderIdentityInfo truthy? if either is used, both must be truthy. (E: 7d55ce37ae482fec48e3398158161926)`); }
|
|
205
|
+
|
|
206
|
+
const { senderIdentity, senderDomainAddr } = senderIdentityInfo;
|
|
207
|
+
if (!senderIdentity) { throw new Error(`(UNEXPECTED) senderIdentity falsy in senderIdentityInfo? if senderIdentityInfo is provided, both senderIdentity and senderDomainAddr must be truthy. (E: a3c17fae2978926d)`); }
|
|
208
|
+
if (!senderDomainAddr) { throw new Error(`(UNEXPECTED) senderDomainAddr falsy in senderIdentityInfo? if senderIdentityInfo is provided, both senderIdentity and senderDomainAddr must be truthy. (E: d288fae39b7d825c)`); }
|
|
207
209
|
}
|
|
208
210
|
|
|
211
|
+
const { senderIdentity, senderDomainAddr } = senderIdentityInfo ?? {};
|
|
212
|
+
|
|
209
213
|
// 1. SETUP SAGA METADATA
|
|
210
214
|
const sagaId = await getUUID();
|
|
211
215
|
|
|
@@ -247,6 +251,7 @@ export class SyncSagaCoordinator {
|
|
|
247
251
|
peer.setOptionalOpts({
|
|
248
252
|
sagaId,
|
|
249
253
|
senderIdentity,
|
|
254
|
+
senderDomainAddr,
|
|
250
255
|
fnSenderSecret,
|
|
251
256
|
sessionConnectPoolConfig: this.defaultSessionConnectPoolConfig(),
|
|
252
257
|
sessionSyncPoolConfig: this.defaultSessionSyncPoolConfig(),
|
package/src/sync/sync-types.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs";
|
|
2
2
|
import { IbGib_V1, IbGibData_V1, IbGibRel8ns_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
|
|
3
|
+
import { KeystoneIbGib_V1 } from "../keystone/keystone-types.mjs";
|
|
3
4
|
|
|
4
5
|
import { SubjectWitness } from "../common/pubsub/subject/subject-types.mjs";
|
|
5
6
|
import { SyncSagaContextIbGib_V1 } from "./sync-saga-context/sync-saga-context-types.mjs";
|
|
@@ -221,3 +222,8 @@ export interface SessionGenesisFrameDetails {
|
|
|
221
222
|
* to get this working.
|
|
222
223
|
*/
|
|
223
224
|
export type StageInProtocol = "beforeSend" | "beforeReceive";
|
|
225
|
+
|
|
226
|
+
export interface SyncSenderIdentityInfo {
|
|
227
|
+
senderIdentity: KeystoneIbGib_V1;
|
|
228
|
+
senderDomainAddr: string;
|
|
229
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
|
-
respecfully, ifWe, iReckon,
|
|
13
|
+
respecfully, ifWe, ifWeMight, iReckon,
|
|
14
14
|
} from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
|
|
15
15
|
const maam = `[${import.meta.url}]`, sir = maam;
|
|
16
16
|
import { clone, delay, extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
@@ -184,7 +184,10 @@ await respecfully(sir, `Test Phase 2: peer.connect()`, async () => {
|
|
|
184
184
|
|
|
185
185
|
const syncSaga = await senderCoordinator.sync({
|
|
186
186
|
domainIbGibs: [xStone],
|
|
187
|
-
|
|
187
|
+
senderIdentityInfo: {
|
|
188
|
+
senderIdentity,
|
|
189
|
+
senderDomainAddr: getIbGibAddr({ ibGib: senderIdentity }),
|
|
190
|
+
},
|
|
188
191
|
fnSenderSecret: async () => SENDER_SECRET,
|
|
189
192
|
peer,
|
|
190
193
|
localSpace: sourceSpace,
|
|
@@ -244,7 +244,10 @@ await respecfully(sir, `Test Phase 1: establishSessionIdentity`, async () => {
|
|
|
244
244
|
|
|
245
245
|
const syncSaga = await senderCoordinator.sync({
|
|
246
246
|
domainIbGibs: [xStone],
|
|
247
|
-
|
|
247
|
+
senderIdentityInfo: {
|
|
248
|
+
senderIdentity,
|
|
249
|
+
senderDomainAddr: getIbGibAddr({ ibGib: senderIdentity }),
|
|
250
|
+
},
|
|
248
251
|
fnSenderSecret: async () => { return SENDER_SECRET },
|
|
249
252
|
peer: await newTestPeer(),
|
|
250
253
|
localSpace: sourceSpace,
|
|
@@ -141,7 +141,10 @@ await respecfully(sir, `Test Phase 3A: Ping Pong Sync with Identity`, async () =
|
|
|
141
141
|
|
|
142
142
|
const syncSaga = await senderCoordinator.sync({
|
|
143
143
|
domainIbGibs: [xStone],
|
|
144
|
-
|
|
144
|
+
senderIdentityInfo: {
|
|
145
|
+
senderIdentity,
|
|
146
|
+
senderDomainAddr: getIbGibAddr({ ibGib: senderIdentity }),
|
|
147
|
+
},
|
|
145
148
|
fnSenderSecret: async () => SENDER_SECRET,
|
|
146
149
|
peer,
|
|
147
150
|
localSpace: sourceSpace,
|
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
I am removing ALL identity/session/identities/keystone-related code from sync
|
|
2
|
-
and starting over, now that I have a clearer idea of the requirements.
|
|
3
|
-
|
|
4
|
-
This is code that I thought would be reusable with minimal adjustments later.
|
|
5
|
-
|
|
6
|
-
TODO: I have mangled some of the hits for these terms by adding a space after
|
|
7
|
-
the first letter. After i am done, need to go back through and search for "k
|
|
8
|
-
eystone", "i dentity" "s ession" for these mangled terms.
|
|
9
|
-
|
|
10
|
-
```typescript
|
|
11
|
-
import { KeystoneIbGib_V1, } from "../keystone/keystone-types.mjs";
|
|
12
|
-
import { KeystoneService_V1 } from '../../keystone/keystone-service-v1.mjs';
|
|
13
|
-
import { validateKeystoneGraph, validateKeystoneTransition } from '../../keystone/keystone-helpers.mjs';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* creates a session identity keystone based off of the given args.
|
|
17
|
-
*
|
|
18
|
-
* Then, if the {@link primaryIdentity} keystone is provided, this also
|
|
19
|
-
* **signs** this keystone pointing to the address of the sess
|
|
20
|
-
*/
|
|
21
|
-
public async createSessionIdentity({
|
|
22
|
-
sagaId,
|
|
23
|
-
primaryIdentity,
|
|
24
|
-
nonSessionSecret,
|
|
25
|
-
metaspace,
|
|
26
|
-
localSpace,
|
|
27
|
-
}: {
|
|
28
|
-
/**
|
|
29
|
-
* unique to any one particular saga.
|
|
30
|
-
*/
|
|
31
|
-
sagaId: string,
|
|
32
|
-
/**
|
|
33
|
-
* optional main identity, e.g., Alice's keystone
|
|
34
|
-
*/
|
|
35
|
-
primaryIdentity: KeystoneIbGib_V1 | undefined,
|
|
36
|
-
/**
|
|
37
|
-
* driving secret behind the sync operation. usually, this will be the
|
|
38
|
-
* secret corresponding to a primary identity keystone. But this can
|
|
39
|
-
* also just be a one-time secret just to have more security in the
|
|
40
|
-
* transmission intrinsically.
|
|
41
|
-
*/
|
|
42
|
-
nonSessionSecret: string,
|
|
43
|
-
metaspace: MetaspaceService,
|
|
44
|
-
localSpace: IbGibSpaceAny,
|
|
45
|
-
}): Promise<{
|
|
46
|
-
sessionIdentity: KeystoneIbGib_V1,
|
|
47
|
-
sessionSecret: string,
|
|
48
|
-
/**
|
|
49
|
-
* if truthy, this evolved from the incoming {@link primaryIdentity} and
|
|
50
|
-
* has already persisted/registered in the incoming {@link localSpace}.
|
|
51
|
-
*/
|
|
52
|
-
newPrimaryIdentity: KeystoneIbGib_V1 | undefined
|
|
53
|
-
}> {
|
|
54
|
-
const lc = `${this.lc}[${this.createSessionIdentity.name}]`;
|
|
55
|
-
try {
|
|
56
|
-
if (logalot) { console.log(`${lc} starting... (I: 428392a4ee636b7bd8f7d5d89a87e826)`); }
|
|
57
|
-
|
|
58
|
-
if (!nonSessionSecret) { throw new Error(`(UNEXPECTED) nonSessionSecret falsy? This is expected to be truthy by this point. (E: 8ce053fe59825a6678713128953b9d26)`); }
|
|
59
|
-
|
|
60
|
-
debugger; // step through create session id
|
|
61
|
-
const sessionSecret = await this.deriveSessionSecret({
|
|
62
|
-
sagaId, nonSessionSecret
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Generate keystone with two initial pools in two steps.
|
|
66
|
-
// 1. Create primary pool with genesis method to correspond to the
|
|
67
|
-
// sender/sender's secret/identity.
|
|
68
|
-
// 2. Create a separate pool and add separately because a
|
|
69
|
-
// different pw + config is used for the transition pool.
|
|
70
|
-
|
|
71
|
-
if (!this.sessionKeystonePoolConfig) { throw new Error(`this.sessionKeystonePoolConfig falsy. createSessionIdentity requires the coordinator to have this config set. (E: d65bb868d5e3e72c585d64d594e2b826)`); }
|
|
72
|
-
const sessionIdentity_genesis = await this.keystoneSvc.genesis({
|
|
73
|
-
masterSecret: sessionSecret,
|
|
74
|
-
configs: [this.sessionKeystonePoolConfig],
|
|
75
|
-
metaspace,
|
|
76
|
-
space: localSpace,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// #region sanity validation of genesis keystone
|
|
80
|
-
/**
|
|
81
|
-
* not necessary but since it's a new design, I'm putting in this
|
|
82
|
-
* immediate validation just to put it through its paces. (worth the
|
|
83
|
-
* slight perf hit).
|
|
84
|
-
*/
|
|
85
|
-
const validationErrors = await validateGenesisKeystone({
|
|
86
|
-
keystoneIbGib: sessionIdentity_genesis
|
|
87
|
-
});
|
|
88
|
-
if (validationErrors && validationErrors.length > 0) { throw new Error(`(UNEXPECTED) the sessionIdentity_genesis that we just created already has validation errors just after creation? errors: ${validationErrors} (E: e9ca08cf0f8858bb1ace8b9fa89f8726)`); }
|
|
89
|
-
// #endregion sanity validation of genesis keystone
|
|
90
|
-
|
|
91
|
-
let newPrimaryIdentity: KeystoneIbGib_V1 | undefined = undefined;
|
|
92
|
-
if (primaryIdentity) {
|
|
93
|
-
newPrimaryIdentity = await this.keystoneSvc.sign({
|
|
94
|
-
latestKeystone: primaryIdentity,
|
|
95
|
-
poolId: this.sessionKeystonePoolConfig.id,
|
|
96
|
-
claim: {
|
|
97
|
-
verb: KEYSTONE_VERB_SIGN,
|
|
98
|
-
target: getIbGibAddr({ ibGib: sessionIdentity_genesis }),
|
|
99
|
-
},
|
|
100
|
-
masterSecret: nonSessionSecret,
|
|
101
|
-
metaspace,
|
|
102
|
-
space: localSpace,
|
|
103
|
-
// frameDetails: undefined, // anything to put here?
|
|
104
|
-
// requiredChallengeIds: undefined, // not relevant I think
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// --- IMMEDIATE PERSISTENCE (Audit Trail Rule) ---
|
|
109
|
-
// The initial session keystone is trusted locally and must be stored
|
|
110
|
-
// in the durable space immediately so the FSM and validation steps
|
|
111
|
-
// can use it to sign outgoing contexts.
|
|
112
|
-
const identityIbGibs: IbGib_V1[] = [
|
|
113
|
-
sessionIdentity_genesis
|
|
114
|
-
];
|
|
115
|
-
if (newPrimaryIdentity) {
|
|
116
|
-
identityIbGibs.push(newPrimaryIdentity);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// identity ibgibs are single framed without dna, so we only have to
|
|
120
|
-
// worry about each individual frame (i.e. no dependency graph)
|
|
121
|
-
await metaspace.put({
|
|
122
|
-
ibGibs: identityIbGibs,
|
|
123
|
-
space: localSpace,
|
|
124
|
-
});
|
|
125
|
-
for (const identityIbGib of identityIbGibs) {
|
|
126
|
-
await registerNewIbGib({
|
|
127
|
-
ibGib: identityIbGib,
|
|
128
|
-
space: localSpace,
|
|
129
|
-
fnBroadcast: undefined,
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
// ------------------------------------------------
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
sessionIdentity: sessionIdentity_genesis,
|
|
136
|
-
sessionSecret,
|
|
137
|
-
newPrimaryIdentity,
|
|
138
|
-
}
|
|
139
|
-
} catch (error) {
|
|
140
|
-
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
141
|
-
throw error;
|
|
142
|
-
} finally {
|
|
143
|
-
if (logalot) { console.log(`${lc} complete.`); }
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* helper that KDFs the given identitySecret, using {@link sagaId} to do so.
|
|
149
|
-
*
|
|
150
|
-
* @returns deterministically derived session secret
|
|
151
|
-
*/
|
|
152
|
-
private async deriveSessionSecret({
|
|
153
|
-
sagaId,
|
|
154
|
-
nonSessionSecret,
|
|
155
|
-
}: {
|
|
156
|
-
sagaId: string,
|
|
157
|
-
/**
|
|
158
|
-
* driving secret behind the sync operation. usually, this will be the
|
|
159
|
-
* secret corresponding to a primary identity keystone. But this can
|
|
160
|
-
* also just be a one-time secret just to have more security in the
|
|
161
|
-
* transmission intrinsically.
|
|
162
|
-
*/
|
|
163
|
-
nonSessionSecret: string,
|
|
164
|
-
}): Promise<string> {
|
|
165
|
-
const lc = `${this.lc}[${this.deriveSessionSecret.name}]`;
|
|
166
|
-
try {
|
|
167
|
-
if (logalot) { console.log(`${lc} starting... (I: 0de03f8dcd3e32f1fca244e8f2a8a826)`); }
|
|
168
|
-
|
|
169
|
-
// Derive session-specific secret using KDF
|
|
170
|
-
const sessionSecret = await deriveKey({
|
|
171
|
-
masterSecret: nonSessionSecret,
|
|
172
|
-
kdfOpts: {
|
|
173
|
-
strategy: KdfStrategy.recursive_salt_wrap,
|
|
174
|
-
salt: sagaId,
|
|
175
|
-
rounds: 10000,
|
|
176
|
-
algorithm: 'SHA-256'
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
return sessionSecret;
|
|
181
|
-
} catch (error) {
|
|
182
|
-
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
183
|
-
throw error;
|
|
184
|
-
} finally {
|
|
185
|
-
if (logalot) { console.log(`${lc} complete.`); }
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* move to sync-peer-helpers.mts as a pure function?
|
|
193
|
-
*/
|
|
194
|
-
export async function authenticateContext({
|
|
195
|
-
context,
|
|
196
|
-
space,
|
|
197
|
-
keystoneSvc,
|
|
198
|
-
}: {
|
|
199
|
-
context: SyncSagaContextIbGib_V1,
|
|
200
|
-
space: IbGibSpaceAny,
|
|
201
|
-
keystoneSvc?: KeystoneService_V1,
|
|
202
|
-
}): Promise<string[]> {
|
|
203
|
-
const lc = `[${authenticateContext.name}]`;
|
|
204
|
-
try {
|
|
205
|
-
if (logalot) { console.log(`${lc} starting... (I: 2677a482dfa873dcd1aa04a3031ff826)`); }
|
|
206
|
-
|
|
207
|
-
const errors: string[] = [];
|
|
208
|
-
if (!keystoneSvc) {
|
|
209
|
-
if (logalot) { console.warn(`${lc} No keystoneSvc provided. Skipping context authentication. (W: d34b8ad93d84a1ba8d8f7facd288826)`); }
|
|
210
|
-
return errors;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Bill Architecture: We only sign at the context level.
|
|
214
|
-
// If the context refers to a session keystone, we must have a signedSessionKeystone
|
|
215
|
-
// as well to verify the most recent turn.
|
|
216
|
-
const { sessionKeystone: prevKeystoneAddrs } = context.rel8ns || {};
|
|
217
|
-
const { signedSessionKeystone: currKeystone } = context;
|
|
218
|
-
|
|
219
|
-
if (prevKeystoneAddrs && prevKeystoneAddrs.length > 0) {
|
|
220
|
-
if (!currKeystone) {
|
|
221
|
-
errors.push(`context.rel8ns.sessionKeystone present but context.signedSessionKeystone falsy. (E: b6e5a8ad93d84260a8d8e7facd288826)`);
|
|
222
|
-
return errors;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Retrieve the previous keystone frame from space
|
|
226
|
-
const prevKeystoneAddr = prevKeystoneAddrs[0];
|
|
227
|
-
const getPrevRes = await getFromSpace({ addr: prevKeystoneAddr, space });
|
|
228
|
-
if (!getPrevRes.success || !getPrevRes.ibGibs || getPrevRes.ibGibs.length === 0) {
|
|
229
|
-
errors.push(`couldn't find previous session keystone (${prevKeystoneAddr}) in space (${space.ib}). (E: 7c34b8ad94d84a9ba8cbe7facd288826)`);
|
|
230
|
-
return errors;
|
|
231
|
-
}
|
|
232
|
-
const prevKeystone = getPrevRes.ibGibs[0] as KeystoneIbGib_V1;
|
|
233
|
-
|
|
234
|
-
// 1. Validate the transition (API replay of evolution + intrinsic validation)
|
|
235
|
-
const transitionErrors = await keystoneSvc.validate({
|
|
236
|
-
currentIbGib: currKeystone,
|
|
237
|
-
prevIbGib: prevKeystone,
|
|
238
|
-
});
|
|
239
|
-
if (transitionErrors.length > 0) {
|
|
240
|
-
errors.push(`Invalid session keystone transition: ${transitionErrors.join(', ')} (E: d34b8ad95d84b90a8d8ef7facd288826)`);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// 2. Verify that the signature in current keystone actually targets this context
|
|
244
|
-
const contextAddr = getIbGibAddr({ ibGib: context });
|
|
245
|
-
const proofTargetsThisContext = currKeystone.data?.proofs.some(p => p.claim.target === contextAddr);
|
|
246
|
-
if (!proofTargetsThisContext) {
|
|
247
|
-
errors.push(`Session keystone signature does not target the current context ibgib (${contextAddr}). (E: f3e5a8ad96d84c1ba8d8f7facd288826)`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return errors;
|
|
252
|
-
} catch (error) {
|
|
253
|
-
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
254
|
-
throw error;
|
|
255
|
-
} finally {
|
|
256
|
-
if (logalot) { console.log(`${lc} complete.`); }
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
// #region this sync peer innerspace sendContextRequest
|
|
263
|
-
|
|
264
|
-
// Bill architecture: Keystone identity transportation.
|
|
265
|
-
// On each turn, the sender must include the current signed session
|
|
266
|
-
// keystone. If it's the first turn (Init), we include the entire
|
|
267
|
-
// keystone graph to ensure the receiver has the primary-to-session
|
|
268
|
-
// authorized link.
|
|
269
|
-
const identityIbGibs: IbGib_V1[] = [];
|
|
270
|
-
const { signedSessionKeystone } = context;
|
|
271
|
-
if (signedSessionKeystone) {
|
|
272
|
-
if (msg.data.stage === SyncStage.init) {
|
|
273
|
-
// transmit full keystone graph on the first connect handshake
|
|
274
|
-
const keystoneGraph = await getDependencyGraph({
|
|
275
|
-
ibGib: signedSessionKeystone,
|
|
276
|
-
space: localSpace,
|
|
277
|
-
});
|
|
278
|
-
identityIbGibs.push(...Object.values(keystoneGraph));
|
|
279
|
-
} else {
|
|
280
|
-
// transmit only the latest evolution for subsequent turns
|
|
281
|
-
identityIbGibs.push(signedSessionKeystone);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// #endregion this sync peer innerspace sendContextRequest
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
// #region SyncSagaContextRel8ns_V1
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* The Ephemeral Session Keystone Identity used for this saga. Required for
|
|
292
|
-
* validating the saga frame and this context.
|
|
293
|
-
*
|
|
294
|
-
* WARNING!!!: THIS DOES NOT POINT TO THE CURRENT SESSION KEYSTONE IN
|
|
295
|
-
* {@link SyncSagaContextIbGib_V1.signedSessionKeystone}. This points to the
|
|
296
|
-
* PREVIOUS FRAME (immediate past) of that frame. That session keystone
|
|
297
|
-
* signs with THIS context's frame as its target, so it is logically
|
|
298
|
-
* impossible because the hash would be different.
|
|
299
|
-
*
|
|
300
|
-
* ## notes
|
|
301
|
-
*
|
|
302
|
-
* ATOW (02/18/2026), this is a single address that will have a primary pool
|
|
303
|
-
* for the sender and a delegated pool for the receiver.
|
|
304
|
-
*
|
|
305
|
-
* @see {@link SyncSagaContextIbGib_V1.signedSessionKeystone}
|
|
306
|
-
*/
|
|
307
|
-
sessionKeystone?: IbGibAddr[];
|
|
308
|
-
|
|
309
|
-
// #endregion SyncSagaContextRel8ns_V1
|
|
310
|
-
|
|
311
|
-
```
|