@ibgib/core-gib 0.1.65 → 0.1.66

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 (21) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/keystone/keystone-service-v1.d.mts +54 -18
  3. package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
  4. package/dist/keystone/keystone-service-v1.mjs +56 -21
  5. package/dist/keystone/keystone-service-v1.mjs.map +1 -1
  6. package/dist/keystone/keystone-service-v1.respec.mjs +744 -471
  7. package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
  8. package/dist/keystone/keystone-types.d.mts +9 -1
  9. package/dist/keystone/keystone-types.d.mts.map +1 -1
  10. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +27 -4
  11. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -1
  12. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +230 -135
  13. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
  14. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs +1 -1
  15. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs.map +1 -1
  16. package/package.json +1 -1
  17. package/src/keystone/keystone-service-v1.mts +112 -32
  18. package/src/keystone/keystone-service-v1.respec.mts +791 -556
  19. package/src/keystone/keystone-types.mts +9 -1
  20. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +294 -131
  21. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mts +1 -1
@@ -373,12 +373,20 @@ export interface DelegateKeystoneInfo {
373
373
  */
374
374
  delegateTjpAddr: IbGibAddr;
375
375
  /**
376
- * concrete addr of **DELEGATE** keystone **at the time when the
376
+ * Concrete addr of **DELEGATE** keystone **at the time when the
377
377
  * delegate was added.**
378
378
  *
379
379
  * So if we have a delegate keystone at D3, then this value would be the
380
380
  * addr of that D3 keystone. This will have both a `punctiliarHash` and a
381
381
  * `tjpGib` in its `GibInfo`.
382
+ *
383
+ * ## intent / architecture (ATOW 07/2026)
384
+ *
385
+ * This address captures the registration context of when the delegate was bound.
386
+ * Delegates are designed to evolve **independently** by design as to *not* require
387
+ * us to update the parent identity keystone whenever a delegate evolves.
388
+ * To resolve the active tip of a delegate's timeline, query the space via
389
+ * `keystoneService.getLatestKeystone({ addr: delegateTjpAddr, metaspace, space })`.
382
390
  */
383
391
  delegateAddr: IbGibAddr;
384
392
  /**
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { HashAlgorithm, extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
9
9
  import { getIbGibAddr, getIbAndGib } from '@ibgib/ts-gib/dist/helper.mjs';
10
+ import { IbGibAddr } from '@ibgib/ts-gib/dist/types.mjs';
10
11
  import { validateIbGibAddr, validateIbGibIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
11
12
  import { getGibInfo } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
12
13
  import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
@@ -14,14 +15,16 @@ import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
14
15
  import { GLOBAL_LOG_A_LOT } from '../../../../core-constants.mjs';
15
16
  import {
16
17
  KeystoneReplenishStrategy, KeystoneChallengeType, KeystoneIbGib_V1,
17
- KeystoneSolution, KeystoneChallenge
18
+ KeystoneSolution, KeystoneChallenge,
19
+ KEYSTONE_CLAIM_TYPE_CHANGE_PASSWORD, KEYSTONE_CLAIM_TYPE_ADD_POOL,
20
+ KEYSTONE_CLAIM_TYPE_REMOVE_POOL, KEYSTONE_CLAIM_TYPE_REPLACE_POOL
18
21
  } from '../../../../keystone/keystone-types.mjs';
19
- import { parseKeystoneIb } from '../../../../keystone/keystone-helpers.mjs';
22
+ import { parseKeystoneIb, validateKeystoneIb } from '../../../../keystone/keystone-helpers.mjs';
20
23
  import { KeystoneStrategyFactory } from '../../../../keystone/strategy/keystone-strategy-factory.mjs';
21
24
  import { KeystoneService_V1 } from '../../../../keystone/keystone-service-v1.mjs';
22
25
  import { IbGibSpaceAny } from '../../../../witness/space/space-base-v1.mjs';
23
26
  import { MetaspaceService } from '../../../../witness/space/metaspace/metaspace-types.mjs';
24
- import { POOL_ID_CONNECT, KEYSTONE_VERB_CONNECT, KEYSTONE_VERB_SYNC } from '../../../../keystone/keystone-constants.mjs';
27
+ import { POOL_ID_CONNECT, KEYSTONE_VERB_CONNECT, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_MANAGE } from '../../../../keystone/keystone-constants.mjs';
25
28
  import { getTjpAddr } from '../../../../common/other/ibgib-helper.mjs';
26
29
 
27
30
  const logalot = GLOBAL_LOG_A_LOT
@@ -270,35 +273,69 @@ export async function validateAndRegisterPostKeystones({
270
273
  }
271
274
 
272
275
  /**
273
- * Isomorphic pure function to validate a keystone evolution transition and related S session keystones,
274
- * and persist them to metaspace. Shared across REST API evolve handlers and future socket evolution channels.
276
+ * Isomorphic pure function to validate a keystone evolution transition and
277
+ * related S session keystones, and persist them to metaspace. Shared across
278
+ * REST API evolve handlers and future socket evolution channels.
275
279
  */
276
280
  export async function validateAndRegisterEvolveKeystone({
277
281
  domainAddr,
278
282
  keystoneIbGib,
279
- relatedIbGibs = [],
283
+ relatedKeystoneIbGibs = [],
280
284
  metaspace,
281
285
  space
282
286
  }: {
287
+ /**
288
+ * Address of the root parent domain identity keystone.
289
+ */
283
290
  domainAddr: string;
291
+ /**
292
+ * The main evolved keystone frame being synchronized.
293
+ */
284
294
  keystoneIbGib: KeystoneIbGib_V1;
285
- relatedIbGibs?: IbGib_V1[];
295
+ /**
296
+ * Array of other keystone frames related to this evolution.
297
+ * Distinction in intent:
298
+ * 1. Ephemeral Session Keystones: Temporary keystones generated to verify
299
+ * WebSocket connection sessions (under 'sync' claims, targeted via
300
+ * claim.target = sAddr).
301
+ * 2. Persistent Delegate Keystones: Long-lived sub-keystones registered in
302
+ * the parent's `delegates` dictionary that are evolved concurrently
303
+ * (e.g., during 'manage' password changes).
304
+ */
305
+ relatedKeystoneIbGibs?: IbGib_V1[];
306
+ /**
307
+ * The MetaspaceService instance.
308
+ */
286
309
  metaspace: MetaspaceService;
310
+ /**
311
+ * The active storage space.
312
+ */
287
313
  space: IbGibSpaceAny;
288
314
  }): Promise<void> {
289
315
  const lc = `[${validateAndRegisterEvolveKeystone.name}]`;
290
316
  try {
291
317
  if (logalot) { console.log(`${lc} starting... (I: 42b826a690881cd5a8fe520cbdfad826)`); }
292
318
 
293
- const addr = getIbGibAddr({ ibGib: keystoneIbGib });
294
-
295
- // 1. Intrinsic hash validation
319
+ // 1. Intrinsic hash validation on primary keystone
296
320
  const intrinsicErrors = await validateIbGibIntrinsically({ ibGib: keystoneIbGib });
297
321
  if (intrinsicErrors && intrinsicErrors.length > 0) {
298
322
  throw new Error(`Intrinsic keystone validation failed: ${intrinsicErrors.join(', ')} (E: 38ea984585edc6ee88d2a698c7895826)`);
299
323
  }
300
324
 
301
- // 2. Load the latest root Domain Identity tip in metaspace
325
+ // 2. Proofs & Verb extraction
326
+ const proofs = keystoneIbGib.data?.proofs ?? [];
327
+ if (proofs.length === 0) {
328
+ throw new Error('No proofs found in evolved keystone data (E: 399f8f51c78d722252ddb2894fdbe25)');
329
+ }
330
+ if (proofs.length !== 1) {
331
+ throw new Error(`evolving keystone with proofs.length !== 1? (E: 14e058a34964dcd7181395382cc60826)`);
332
+ }
333
+ const claim = proofs[0].claim;
334
+ if (!claim) { throw new Error(`proof.claim is falsy (E: 54be88be2c975ef18e571a4286928826)`); }
335
+ const verb = claim.verb;
336
+ if (!verb) { throw new Error(`claim.verb is falsy (E: 92cf38af58db2cd90e7132b8940fe326)`); }
337
+
338
+ // 3. Load latest root Domain Identity tip in space
302
339
  const keystoneService = new KeystoneService_V1();
303
340
  let parentKeystone: KeystoneIbGib_V1;
304
341
  try {
@@ -318,147 +355,273 @@ export async function validateAndRegisterEvolveKeystone({
318
355
  }
319
356
 
320
357
  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
358
 
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
- }
359
+ // 4. Primary Verb Branching
360
+ switch (verb) {
361
+ case KEYSTONE_VERB_MANAGE:
362
+ await validateManageEvolveKeystone({
363
+ keystoneIbGib,
364
+ relatedKeystoneIbGibs,
365
+ parentKeystone,
366
+ isDelegate,
367
+ domainAddr,
368
+ metaspace,
369
+ space
370
+ });
371
+ break;
372
+
373
+ case KEYSTONE_VERB_SYNC:
374
+ await validateSyncEvolveKeystone({
375
+ keystoneIbGib,
376
+ relatedKeystoneIbGibs,
377
+ parentKeystone,
378
+ isDelegate,
379
+ domainAddr,
380
+ metaspace,
381
+ space
382
+ });
383
+ break;
335
384
 
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;
385
+ default:
386
+ throw new Error(`Unsupported keystone evolution verb: ${verb} (E: eca4681b05486d1f784c0cb815ef9926)`);
350
387
  }
351
388
 
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
- }
372
- }
389
+ } catch (error) {
390
+ console.error(`${lc} ${extractErrorMsg(error)}`);
391
+ throw error;
392
+ } finally {
393
+ if (logalot) { console.log(`${lc} complete.`); }
394
+ }
395
+ }
373
396
 
374
- // 5. Check Single Pool Restriction
375
- const proofs = keystoneIbGib.data?.proofs ?? [];
376
- if (proofs.length === 0) {
377
- throw new Error('No proofs found in evolved keystone data');
378
- }
379
- const poolIds = new Set<string>();
380
- for (const proof of proofs) {
381
- for (const solution of proof.solutions || []) {
382
- if (solution.poolId) {
383
- poolIds.add(solution.poolId);
397
+ /**
398
+ * Validates and registers a keystone evolution authorized by a 'manage' verb claim.
399
+ *
400
+ * ## Assumptions (ATOW 07/2026)
401
+ * - The primary Domain Identity evolution step is authorized by a single proof.
402
+ * - Sub-verb details (`claim.details?.type`) specify the structural management operation
403
+ * (e.g. 'change-password', 'add-pool', 'remove-pool').
404
+ * - Under 'change-password', relatedKeystoneIbGibs contains outward delegate keystones being
405
+ * registered or evolved concurrently.
406
+ * - Delegates evolve independently by design, so `delegateInfo.delegateAddr` in the parent keystone
407
+ * captures historical registration context. Active delegate tip resolution queries the space via
408
+ * `keystoneService.getLatestKeystone({ addr: delegateTjpAddr, metaspace, space })`.
409
+ */
410
+ async function validateManageEvolveKeystone({
411
+ keystoneIbGib,
412
+ relatedKeystoneIbGibs,
413
+ parentKeystone,
414
+ isDelegate,
415
+ domainAddr,
416
+ metaspace,
417
+ space,
418
+ }: {
419
+ keystoneIbGib: KeystoneIbGib_V1;
420
+ relatedKeystoneIbGibs: IbGib_V1[];
421
+ parentKeystone: KeystoneIbGib_V1;
422
+ isDelegate: boolean;
423
+ domainAddr: IbGibAddr;
424
+ metaspace: MetaspaceService;
425
+ space: IbGibSpaceAny;
426
+ }): Promise<void> {
427
+ const lc = `[validateManageEvolveKeystone]`;
428
+ const keystoneService = new KeystoneService_V1();
429
+
430
+ // 1. Validate parent Domain Identity keystone transition from current tip in space
431
+ const transitionErrors = await keystoneService.validate({
432
+ currentIbGib: keystoneIbGib,
433
+ prevIbGib: parentKeystone
434
+ });
435
+ if (transitionErrors && transitionErrors.length > 0) {
436
+ throw new Error(`Parent Domain Identity evolution validation failed: ${transitionErrors.join(', ')} (E: 5212f8492ac839953842631834bcd826)`);
437
+ }
438
+
439
+ const proof = keystoneIbGib.data?.proofs?.[0];
440
+ const claim = proof?.claim;
441
+ const claimType = claim?.details?.type;
442
+
443
+ const prevDelegates = parentKeystone.data?.delegates ?? {};
444
+ const currDelegates = keystoneIbGib.data?.delegates ?? {};
445
+
446
+ // 2. Sub-verb branching based on claim details type
447
+ switch (claimType) {
448
+ case KEYSTONE_CLAIM_TYPE_CHANGE_PASSWORD:
449
+ case KEYSTONE_CLAIM_TYPE_ADD_POOL:
450
+ case KEYSTONE_CLAIM_TYPE_REMOVE_POOL:
451
+ case KEYSTONE_CLAIM_TYPE_REPLACE_POOL:
452
+ default:
453
+ // Validate all related delegate keystones provided in cargo
454
+ for (const related of relatedKeystoneIbGibs) {
455
+ const ibIsValid = await validateKeystoneIb({ ib: related.ib });
456
+ if (!ibIsValid) {
457
+ throw new Error(`Invalid related keystone ibgib. related.ib (${related.ib}) is not a valid keystone ib. (E: 89dff2f48fa8c33b449081e2478c6826)`);
384
458
  }
385
- }
386
- }
387
- if (poolIds.size > 1) {
388
- throw new Error(`Multiple pools evolved (${Array.from(poolIds).join(', ')}). Only one pool evolution allowed at a time. (E: 4a9b28ae33e8e3eb4ddbad981aa7b126)`);
389
- }
390
459
 
391
- // 6. Check that the verb is 'sync'
392
- if (proofs.length !== 1) { throw new Error(`evolving keystone with proofs.length !== 1? (E: 14e058a34964dcd7181395382cc60826)`); }
393
- const claim = proofs[0].claim;
394
- if (!claim) { throw new Error(`proof.claim is falsy (E: 54be88be2c975ef18e571a4286928826)`); }
395
- const { verb } = claim;
396
- if (!verb) { throw new Error(`claim.verb is falsy (E: 92cf38af58db2cd90e7132b8940fe326)`); }
460
+ const relatedTjp = getTjpAddr({ ibGib: related });
461
+ if (!relatedTjp) {
462
+ throw new Error(`Related keystone in relatedKeystoneIbGibs is missing a TJP address (E: 2fe5a68438e288ab4e86c90d81d142826)`);
463
+ }
464
+ const relatedAddr = getIbGibAddr({ ibGib: related });
465
+
466
+ // Assert it is registered under the current delegates map of the parent
467
+ const delegateInfo = currDelegates[relatedTjp];
468
+ if (!delegateInfo) {
469
+ throw new Error(`Related keystone (${relatedTjp}) is not a registered delegate under parent Domain Identity (E: 3fe5a68438e288ab4e86c90d81d142826)`);
470
+ }
397
471
 
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)`);
472
+ const delegateIsNewlyRegistered = !prevDelegates[relatedTjp];
473
+ if (delegateIsNewlyRegistered) {
474
+ const delegateKeystone = related as KeystoneIbGib_V1;
475
+ if (delegateKeystone.data?.n !== 0) {
476
+ throw new Error(`Newly registered delegate keystone (${relatedAddr}) has data.n !== 0 (${delegateKeystone.data?.n}). Registering non-genesis delegate keystones is not supported at this time. (E: 4cdd0dee42d7d437ef6af698150cfe26)`);
407
477
  }
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)`);
478
+ const genesisErrors = await keystoneService.validateGenesisKeystone({
479
+ keystoneIbGib: delegateKeystone
480
+ });
481
+ if (genesisErrors && genesisErrors.length > 0) {
482
+ throw new Error(`Genesis validation failed for new delegate keystone ${relatedAddr}: ${genesisErrors.join(', ')} (E: d8afdc1dbed48c000455b35585dc26826)`);
483
+ }
484
+ } else {
485
+ // Transition/Evolution validation against the delegate's active tip in space
486
+ let prevDelegateTip: KeystoneIbGib_V1 | undefined;
487
+ try {
488
+ prevDelegateTip = await keystoneService.getLatestKeystone({
489
+ addr: relatedTjp,
490
+ metaspace,
491
+ space
492
+ });
493
+ } catch (err) {
494
+ throw new Error(`Could not find previous delegate tip for ${relatedTjp} in space: ${extractErrorMsg(err)} (E: 5fe5a68438e288ab4e86c90d81d142826)`);
411
495
  }
412
- }
413
- }
414
496
 
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 });
497
+ const evolutionErrors = await keystoneService.validate({
498
+ currentIbGib: related as KeystoneIbGib_V1,
499
+ prevIbGib: prevDelegateTip
500
+ });
501
+ if (evolutionErrors && evolutionErrors.length > 0) {
502
+ throw new Error(`Evolution transition validation failed for delegate keystone ${relatedAddr}: ${evolutionErrors.join(', ')} (E: 6fe5a68438e288ab4e86c90d81d142826)`);
503
+ }
420
504
  }
421
505
  }
422
- return;
423
- }
506
+ break;
507
+ }
424
508
 
425
- if (verb !== 'sync') {
426
- throw new Error(`non-sync/non-manage keystone evolution not yet implemented (E: eca4681b05486d1f784c0cb815ef9926)`);
509
+ // Persist parent and related keystones
510
+ await metaspace.put({ ibGibs: [keystoneIbGib, ...relatedKeystoneIbGibs], space });
511
+ await metaspace.registerNewIbGib({ ibGib: keystoneIbGib, space });
512
+ for (const related of relatedKeystoneIbGibs) {
513
+ if (related.ib?.startsWith('keystone')) {
514
+ await metaspace.registerNewIbGib({ ibGib: related, space });
427
515
  }
516
+ }
517
+ }
428
518
 
429
- if (!isDelegate) {
430
- throw new Error(`Root Domain Identity (${parentTjpAddr}) is not allowed to sync directly. A delegate must be used. (E: 2c88fae39b7d82fd)`);
431
- }
519
+ /**
520
+ * Validates and registers a sync delegate keystone evolution and its associated session keystone.
521
+ *
522
+ * ## Assumptions (ATOW 07/2026)
523
+ * - The evolving keystone must be an authorized outward delegate (Root Domain Identity cannot sync directly).
524
+ * - The claim verb is 'sync'.
525
+ * - relatedKeystoneIbGibs contains the newly created session keystone (S^Stjp) target address specified in claim.target.
526
+ */
527
+ async function validateSyncEvolveKeystone({
528
+ keystoneIbGib,
529
+ relatedKeystoneIbGibs,
530
+ parentKeystone,
531
+ isDelegate,
532
+ domainAddr,
533
+ metaspace,
534
+ space,
535
+ }: {
536
+ keystoneIbGib: KeystoneIbGib_V1;
537
+ relatedKeystoneIbGibs: IbGib_V1[];
538
+ parentKeystone: KeystoneIbGib_V1;
539
+ isDelegate: boolean;
540
+ domainAddr: IbGibAddr;
541
+ metaspace: MetaspaceService;
542
+ space: IbGibSpaceAny;
543
+ }): Promise<void> {
544
+ const lc = `[validateSyncEvolveKeystone]`;
545
+ const keystoneService = new KeystoneService_V1();
432
546
 
433
- const sAddr = claim.target;
434
- if (!sAddr) {
435
- throw new Error('No target address found in sync claim');
436
- }
547
+ if (!isDelegate) {
548
+ throw new Error(`Root Domain Identity (${domainAddr}) is not allowed to sync directly. A delegate must be used. (E: 2c88fae39b7d82fd)`);
549
+ }
437
550
 
438
- // Validate S^Stjp (session keystone ibgib) from relatedIbGibs
439
- const sIbGib = relatedIbGibs.find(ibGib => getIbGibAddr({ ibGib }) === sAddr) as KeystoneIbGib_V1 | undefined;
440
- if (!sIbGib) {
441
- throw new Error(`Session keystone ${sAddr} must be provided in relatedIbGibs (E: fe5a68438e288ab4e86c90d81d142826)`);
442
- }
551
+ const incomingTjpAddr = getTjpAddr({ ibGib: keystoneIbGib });
552
+ if (!incomingTjpAddr) {
553
+ throw new Error(`Incoming delegate keystone missing TJP address (E: d288fae39b7d825e)`);
554
+ }
443
555
 
444
- const sIntrinsicErrors = await validateIbGibIntrinsically({ ibGib: sIbGib });
445
- if (sIntrinsicErrors && sIntrinsicErrors.length > 0) {
446
- throw new Error(`Intrinsic validation failed for S^Stjp: ${sIntrinsicErrors.join(', ')} (E: 802d8350c3cccbd7184dd43866c05526)`);
447
- }
556
+ // Verify delegate is registered under root Domain Identity
557
+ const delegateInfo = parentKeystone.data?.delegates?.[incomingTjpAddr];
558
+ if (!delegateInfo) {
559
+ throw new Error(`Incoming delegate keystone (${incomingTjpAddr}) is not registered under parent Domain Identity (E: c288fae39b7d82fe)`);
560
+ }
561
+
562
+ // Verify delegate is authorized for sync
563
+ const allowedVerbs = delegateInfo.allowedVerbs ?? [];
564
+ if (!allowedVerbs.includes(KEYSTONE_VERB_SYNC)) {
565
+ throw new Error(`Incoming delegate keystone is not authorized for sync. Allowed verbs: ${allowedVerbs.join(', ')} (E: a288fae39b7d82ff)`);
566
+ }
567
+
568
+ // Load delegate's previous tip from space using keystoneService.getLatestKeystone if it exists
569
+ let prevDelegateTip: KeystoneIbGib_V1 | undefined;
570
+ try {
571
+ prevDelegateTip = await keystoneService.getLatestKeystone({
572
+ addr: incomingTjpAddr,
573
+ metaspace,
574
+ space
575
+ });
576
+ } catch (err) {
577
+ // No previous tip found in space yet; will validate graph below
578
+ }
448
579
 
449
- const sGenesisErrors = await keystoneService.validateGenesisKeystone({ keystoneIbGib: sIbGib });
450
- if (sGenesisErrors && sGenesisErrors.length > 0) {
451
- throw new Error(`Genesis validation failed for S^Stjp: ${sGenesisErrors.join(', ')} (E: 8afdc1dbed48c000455b35585dc26826)`);
580
+ if (prevDelegateTip) {
581
+ const validationErrors = await keystoneService.validate({
582
+ currentIbGib: keystoneIbGib,
583
+ prevIbGib: prevDelegateTip
584
+ });
585
+ if (validationErrors && validationErrors.length > 0) {
586
+ throw new Error(`Delegate keystone evolution validation failed: ${validationErrors.join(', ')} (E: 5212f8492ac839953842631834bcd827)`);
587
+ }
588
+ } else {
589
+ const validationErrors = await keystoneService.validateKeystoneGraph({
590
+ keystoneIbGib,
591
+ getLatest: false,
592
+ invalidIfMoreRecentKeystoneFoundInSpace: false,
593
+ space
594
+ });
595
+ if (validationErrors && validationErrors.length > 0) {
596
+ throw new Error(`Delegate keystone graph validation failed: ${validationErrors.join(', ')} (E: 3a288fae39b7d82f)`);
452
597
  }
598
+ }
453
599
 
454
- // Persist and Register both I and S
455
- await metaspace.put({ ibGibs: [keystoneIbGib, sIbGib], space });
456
- await metaspace.registerNewIbGib({ ibGib: keystoneIbGib, space });
457
- await metaspace.registerNewIbGib({ ibGib: sIbGib, space });
458
- } catch (error) {
459
- console.error(`${lc} ${extractErrorMsg(error)}`);
460
- throw error;
461
- } finally {
462
- if (logalot) { console.log(`${lc} complete.`); }
600
+ const proof = keystoneIbGib.data?.proofs?.[0];
601
+ const claim = proof?.claim;
602
+ const sAddr = claim?.target;
603
+ if (!sAddr) {
604
+ throw new Error('No target address found in sync claim');
463
605
  }
606
+
607
+ // Validate S^Stjp (session keystone ibgib) from relatedKeystoneIbGibs
608
+ const sIbGib = relatedKeystoneIbGibs.find(ibGib => getIbGibAddr({ ibGib }) === sAddr) as KeystoneIbGib_V1 | undefined;
609
+ if (!sIbGib) {
610
+ throw new Error(`Session keystone ${sAddr} must be provided in relatedKeystoneIbGibs (E: fe5a68438e288ab4e86c90d81d142826)`);
611
+ }
612
+
613
+ const sIntrinsicErrors = await validateIbGibIntrinsically({ ibGib: sIbGib });
614
+ if (sIntrinsicErrors && sIntrinsicErrors.length > 0) {
615
+ throw new Error(`Intrinsic validation failed for S^Stjp: ${sIntrinsicErrors.join(', ')} (E: 802d8350c3cccbd7184dd43866c05526)`);
616
+ }
617
+
618
+ const sGenesisErrors = await keystoneService.validateGenesisKeystone({ keystoneIbGib: sIbGib });
619
+ if (sGenesisErrors && sGenesisErrors.length > 0) {
620
+ throw new Error(`Genesis validation failed for S^Stjp: ${sGenesisErrors.join(', ')} (E: 8afdc1dbed48c000455b35585dc26826)`);
621
+ }
622
+
623
+ // Persist and Register both I and S
624
+ await metaspace.put({ ibGibs: [keystoneIbGib, sIbGib], space });
625
+ await metaspace.registerNewIbGib({ ibGib: keystoneIbGib, space });
626
+ await metaspace.registerNewIbGib({ ibGib: sIbGib, space });
464
627
  }
@@ -105,7 +105,7 @@ export class SyncPeerWebSocketSender_V1
105
105
  },
106
106
  body: JSON.stringify({
107
107
  keystoneIbGib: newSenderIdentity,
108
- relatedIbGibs: [sessionIdentity]
108
+ relatedKeystoneIbGibs: [sessionIdentity]
109
109
  }),
110
110
  });
111
111