@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.
Files changed (100) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/keystone/keystone-constants.d.mts +0 -1
  3. package/dist/keystone/keystone-constants.d.mts.map +1 -1
  4. package/dist/keystone/keystone-constants.mjs +0 -1
  5. package/dist/keystone/keystone-constants.mjs.map +1 -1
  6. package/dist/keystone/keystone-helpers.d.mts +23 -1
  7. package/dist/keystone/keystone-helpers.d.mts.map +1 -1
  8. package/dist/keystone/keystone-helpers.mjs +98 -5
  9. package/dist/keystone/keystone-helpers.mjs.map +1 -1
  10. package/dist/keystone/keystone-service-v1.d.mts +39 -2
  11. package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
  12. package/dist/keystone/keystone-service-v1.mjs +175 -3
  13. package/dist/keystone/keystone-service-v1.mjs.map +1 -1
  14. package/dist/keystone/keystone-service-v1.respec.mjs +397 -42
  15. package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
  16. package/dist/keystone/keystone-types.d.mts +54 -0
  17. package/dist/keystone/keystone-types.d.mts.map +1 -1
  18. package/dist/keystone/policy/keystone-profile-builder.d.mts +1 -1
  19. package/dist/keystone/policy/keystone-profile-builder.d.mts.map +1 -1
  20. package/dist/keystone/policy/keystone-profile-builder.mjs +8 -0
  21. package/dist/keystone/policy/keystone-profile-builder.mjs.map +1 -1
  22. package/dist/keystone/policy/profiles/profile-domain.json +84 -0
  23. package/dist/keystone/policy/profiles/profile-sync.json +84 -0
  24. package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.d.mts +8 -0
  25. package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.d.mts.map +1 -1
  26. package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs +15 -1
  27. package/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs.map +1 -1
  28. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs +16 -4
  29. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs.map +1 -1
  30. package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs +4 -1
  31. package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs.map +1 -1
  32. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs +4 -1
  33. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs.map +1 -1
  34. package/dist/sync/sync-conflict-text-merge.withid.respec.mjs +10 -4
  35. package/dist/sync/sync-conflict-text-merge.withid.respec.mjs.map +1 -1
  36. package/dist/sync/sync-innerspace-constants.withid.respec.mjs +12 -3
  37. package/dist/sync/sync-innerspace-constants.withid.respec.mjs.map +1 -1
  38. package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs +4 -1
  39. package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs.map +1 -1
  40. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs +4 -1
  41. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs.map +1 -1
  42. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs +4 -1
  43. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs.map +1 -1
  44. package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs +4 -1
  45. package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs.map +1 -1
  46. package/dist/sync/sync-innerspace.withid.respec.mjs +4 -1
  47. package/dist/sync/sync-innerspace.withid.respec.mjs.map +1 -1
  48. package/dist/sync/sync-peer/sync-peer-types.d.mts +4 -0
  49. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  50. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  51. package/dist/sync/sync-peer/sync-peer-v1.mjs +4 -1
  52. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  53. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -1
  54. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +90 -22
  55. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
  56. package/dist/sync/sync-saga-coordinator.d.mts +7 -10
  57. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  58. package/dist/sync/sync-saga-coordinator.mjs +14 -5
  59. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  60. package/dist/sync/sync-types.d.mts +5 -0
  61. package/dist/sync/sync-types.d.mts.map +1 -1
  62. package/dist/sync/sync-types.mjs.map +1 -1
  63. package/dist/sync/sync-withid.connect.respec.mjs +4 -1
  64. package/dist/sync/sync-withid.connect.respec.mjs.map +1 -1
  65. package/dist/sync/sync-withid.establish.respec.mjs +4 -1
  66. package/dist/sync/sync-withid.establish.respec.mjs.map +1 -1
  67. package/dist/sync/sync-withid.pingpong.respec.mjs +4 -1
  68. package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -1
  69. package/package.json +1 -1
  70. package/src/keystone/README.md +15 -2
  71. package/src/keystone/docs/architecture.md +21 -2
  72. package/src/keystone/docs/delegation.md +57 -0
  73. package/src/keystone/keystone-constants.mts +0 -1
  74. package/src/keystone/keystone-helpers.mts +131 -5
  75. package/src/keystone/keystone-service-v1.mts +223 -2
  76. package/src/keystone/keystone-service-v1.respec.mts +436 -40
  77. package/src/keystone/keystone-types.mts +55 -0
  78. package/src/keystone/policy/keystone-profile-builder.mts +9 -1
  79. package/src/keystone/policy/profiles/profile-domain.json +84 -0
  80. package/src/keystone/policy/profiles/profile-sync.json +84 -0
  81. package/src/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mts +20 -1
  82. package/src/sync/sync-conflict-adv-multitimelines.withid.respec.mts +16 -4
  83. package/src/sync/sync-conflict-basic-divergence.withid.respec.mts +4 -1
  84. package/src/sync/sync-conflict-basic-multitimelines.withid.respec.mts +4 -1
  85. package/src/sync/sync-conflict-text-merge.withid.respec.mts +10 -4
  86. package/src/sync/sync-innerspace-constants.withid.respec.mts +12 -3
  87. package/src/sync/sync-innerspace-deep-updates.withid.respec.mts +4 -1
  88. package/src/sync/sync-innerspace-dest-ahead.withid.respec.mts +4 -1
  89. package/src/sync/sync-innerspace-multiple-timelines.withid.respec.mts +4 -1
  90. package/src/sync/sync-innerspace-partial-update.withid.respec.mts +4 -1
  91. package/src/sync/sync-innerspace.withid.respec.mts +4 -1
  92. package/src/sync/sync-peer/sync-peer-types.mts +4 -0
  93. package/src/sync/sync-peer/sync-peer-v1.mts +5 -1
  94. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +96 -22
  95. package/src/sync/sync-saga-coordinator.mts +17 -12
  96. package/src/sync/sync-types.mts +6 -0
  97. package/src/sync/sync-withid.connect.respec.mts +5 -2
  98. package/src/sync/sync-withid.establish.respec.mts +4 -1
  99. package/src/sync/sync-withid.pingpong.respec.mts +4 -1
  100. package/src/sync/unused-identity-backup.mts.md +0 -311
@@ -10,10 +10,12 @@ import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
10
10
  import { KeystoneStrategyFactory } from './strategy/keystone-strategy-factory.mjs';
11
11
  import { KeystoneChallengePool, KeystoneClaim, KeystoneIbGib_V1, KeystonePoolConfig_HashV1 } from './keystone-types.mjs';
12
12
  import { createRevocationPoolConfig, createStandardPoolConfig } from './keystone-config-builder.mjs';
13
- import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE } from './keystone-constants.mjs';
14
- import { KeystoneService_V1 } from './keystone-service-v1.mjs';
15
- import { selectChallengeIds, validateChallengePool } from './keystone-helpers.mjs';
13
+ import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE, POOL_ID_MANAGE, KEYSTONE_VERB_SYNC } from './keystone-constants.mjs';
14
+ import { KeystoneService_V1, } from './keystone-service-v1.mjs';
15
+ import { selectChallengeIds, validateChallengePool, validateKeystoneIb, validateKeystoneTransition, validateGenesisKeystone } from './keystone-helpers.mjs';
16
16
  import { KeystoneProfileBuilder } from './policy/keystone-profile-builder.mjs';
17
+ import { getTjpAddr } from '../common/other/ibgib-helper.mjs';
18
+ import { deriveDelegateSecret } from './strategy/hash-reveal-v1/hash-reveal-v1.mjs';
17
19
 
18
20
  const logalot = GLOBAL_LOG_A_LOT;
19
21
 
@@ -37,16 +39,55 @@ class MockIbGibSpace {
37
39
  return data ? JSON.parse(JSON.stringify(data)) : null;
38
40
  }
39
41
 
42
+ async argy({ argData, ibGibs }: { argData: any; ibGibs?: IbGib_V1[] }): Promise<any> {
43
+ return {
44
+ ib: 'arg^gib',
45
+ gib: 'arg_gib',
46
+ data: argData,
47
+ ibGibs,
48
+ };
49
+ }
50
+
40
51
  async witness(arg: any): Promise<any> {
41
52
  const cmd = arg.data?.cmd;
53
+ const cmdModifiers = arg.data?.cmdModifiers || [];
42
54
  if (cmd === 'get') {
43
55
  const addrs = arg.data.ibGibAddrs || [];
44
- const ibGibs: IbGib_V1[] = [];
45
- for (const addr of addrs) {
46
- const x = await this.get({ addr });
47
- if (x) { ibGibs.push(x); }
56
+ if (cmdModifiers.includes('latest') && cmdModifiers.includes('addrs')) {
57
+ const latestAddrsMap: { [addr: string]: string } = {};
58
+ for (const queryAddr of addrs) {
59
+ const queryTjpGib = queryAddr.includes('.') ? queryAddr.split('.')[1] : queryAddr.split('^')[1];
60
+ let latestAddr = queryAddr;
61
+ let maxN = -1;
62
+ for (const [addr, ibGib] of this.store.entries()) {
63
+ const tjpGib = addr.includes('.') ? addr.split('.')[1] : addr.split('^')[1];
64
+ if (tjpGib === queryTjpGib) {
65
+ const n = ibGib.data?.n ?? 0;
66
+ if (n > maxN) {
67
+ maxN = n;
68
+ latestAddr = addr;
69
+ }
70
+ }
71
+ }
72
+ latestAddrsMap[queryAddr] = latestAddr;
73
+ }
74
+ return {
75
+ data: {
76
+ success: true,
77
+ latestAddrsMap,
78
+ }
79
+ };
80
+ } else {
81
+ const ibGibs: IbGib_V1[] = [];
82
+ for (const addr of addrs) {
83
+ const x = await this.get({ addr });
84
+ if (x) { ibGibs.push(x); }
85
+ }
86
+ return {
87
+ data: { success: true },
88
+ ibGibs,
89
+ };
48
90
  }
49
- return { ibGibs };
50
91
  }
51
92
  return undefined;
52
93
  }
@@ -81,6 +122,15 @@ class MockMetaspaceService {
81
122
  return target.put(args);
82
123
  }
83
124
 
125
+ async get(args: { addr: string, space?: any }): Promise<{ success: boolean, ibGibs?: IbGib_V1[] }> {
126
+ const target = args.space || this.space;
127
+ const x = await target.get({ addr: args.addr });
128
+ return {
129
+ success: !!x,
130
+ ibGibs: x ? [x] : [],
131
+ };
132
+ }
133
+
84
134
  /**
85
135
  * Tracks the latest version of an ibGib timeline.
86
136
  */
@@ -132,7 +182,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
132
182
 
133
183
  await respecfully(sir, 'Derivation Logic', async () => {
134
184
 
135
- await ifWeMight(sir, 'derivePoolSecret with same inputs returns same output', async () => {
185
+ await ifWe(sir, 'derivePoolSecret with same inputs returns same output', async () => {
136
186
  const strategy = KeystoneStrategyFactory.create({ config });
137
187
 
138
188
  const secretA = await strategy.derivePoolSecret({ masterSecret });
@@ -142,7 +192,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
142
192
  iReckon(sir, secretA).asTo('secret length').isGonnaBeTruthy();
143
193
  });
144
194
 
145
- await ifWeMight(sir, 'derivePoolSecret with different master secret returns different output', async () => {
195
+ await ifWe(sir, 'derivePoolSecret with different master secret returns different output', async () => {
146
196
  const strategy = KeystoneStrategyFactory.create({ config });
147
197
 
148
198
  const secretA = await strategy.derivePoolSecret({ masterSecret });
@@ -151,7 +201,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
151
201
  iReckon(sir, secretA).asTo('secrets differ').not.willEqual(secretB);
152
202
  });
153
203
 
154
- await ifWeMight(sir, 'derivePoolSecret with different salt returns different output', async () => {
204
+ await ifWe(sir, 'derivePoolSecret with different salt returns different output', async () => {
155
205
  // Modify salt in a copy of config
156
206
  const configB = { ...config, salt: "OtherPool" };
157
207
  const strategyA = KeystoneStrategyFactory.create({ config });
@@ -162,11 +212,22 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
162
212
 
163
213
  iReckon(sir, secretA).asTo('salt affects secret').not.willEqual(secretB);
164
214
  });
215
+
216
+ await ifWe(sir, 'deriveDelegateSecret derives a consistent secret using KDF', async () => {
217
+ const secretA = await deriveDelegateSecret({ masterSecret });
218
+ const secretB = await deriveDelegateSecret({ masterSecret });
219
+
220
+ iReckon(sir, secretA).asTo('consistency').willEqual(secretB);
221
+ iReckon(sir, secretA).asTo('truthy').isGonnaBeTruthy();
222
+
223
+ const secretC = await deriveDelegateSecret({ masterSecret: masterSecret + '_other' });
224
+ iReckon(sir, secretA).asTo('differ').not.willEqual(secretC);
225
+ });
165
226
  });
166
227
 
167
228
  await respecfully(sir, 'Challenge/Solution Logic', async () => {
168
229
 
169
- await ifWeMight(sir, 'generateSolution -> generateChallenge -> validateSolution loop works', async () => {
230
+ await ifWe(sir, 'generateSolution -> generateChallenge -> validateSolution loop works', async () => {
170
231
  const strategy = KeystoneStrategyFactory.create({ config });
171
232
  const poolSecret = await strategy.derivePoolSecret({ masterSecret });
172
233
  const challengeId = "a3ff7843552870fc28bef2b"; // arbitrary random challengeId
@@ -185,7 +246,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
185
246
  iReckon(sir, isValid).asTo('valid pair should pass').isGonnaBeTrue();
186
247
  });
187
248
 
188
- await ifWeMight(sir, 'validateSolution fails for mismatched values', async () => {
249
+ await ifWe(sir, 'validateSolution fails for mismatched values', async () => {
189
250
  const strategy = KeystoneStrategyFactory.create({ config });
190
251
  const poolSecret = await strategy.derivePoolSecret({ masterSecret });
191
252
  const challengeId = "8c994f3ed598f150e25513"; // arbitrary random challengeId
@@ -201,7 +262,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
201
262
  iReckon(sir, isValid).asTo('tampered solution should fail').isGonnaBeFalse();
202
263
  });
203
264
 
204
- await ifWeMight(sir, 'validateSolution fails for mismatched challenge hashes', async () => {
265
+ await ifWe(sir, 'validateSolution fails for mismatched challenge hashes', async () => {
205
266
  const strategy = KeystoneStrategyFactory.create({ config });
206
267
  const poolSecret = await strategy.derivePoolSecret({ masterSecret });
207
268
 
@@ -242,7 +303,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
242
303
  });
243
304
 
244
305
  await respecfully(sir, 'Genesis', async () => {
245
- await ifWeMight(sir, 'creates a valid genesis frame and persists it', async () => {
306
+ await ifWe(sir, 'creates a valid genesis frame and persists it', async () => {
246
307
  const salt = (await getUUID()).substring(0, 16);
247
308
  const config = createStandardPoolConfig({
248
309
  id: POOL_ID_DEFAULT,
@@ -274,7 +335,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
274
335
  });
275
336
 
276
337
  await respecfully(sir, 'Signing (Evolution)', async () => {
277
- await ifWeMight(sir, 'evolves the keystone with a valid proof', async () => {
338
+ await ifWe(sir, 'evolves the keystone with a valid proof', async () => {
278
339
  const claim: Partial<KeystoneClaim> = {
279
340
  target: "comment 123^gib",
280
341
  verb: "post"
@@ -304,7 +365,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
304
365
  });
305
366
 
306
367
  await respecfully(sir, 'Validation', async () => {
307
- await ifWeMight(sir, 'validates the genesis->signed transition', async () => {
368
+ await ifWe(sir, 'validates the genesis->signed transition', async () => {
308
369
  const errors = await service.validate({
309
370
  prevIbGib: genesisKeystone,
310
371
  currentIbGib: signedKeystone,
@@ -353,7 +414,7 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
353
414
  });
354
415
 
355
416
  await respecfully(sir, 'Wrong Secret (Forgery)', async () => {
356
- await ifWeMight(sir, 'prevents creation of forged frames', async () => {
417
+ await ifWe(sir, 'prevents creation of forged frames', async () => {
357
418
  const claim: Partial<KeystoneClaim> = { target: "comment 123^gib", verb: "post" };
358
419
 
359
420
  let errorCaught = false;
@@ -382,7 +443,7 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
382
443
  });
383
444
 
384
445
  await respecfully(sir, 'Policy Violation (Restricted Verbs)', async () => {
385
- await ifWeMight(sir, 'throws error if signing forbidden verb with restricted pool', async () => {
446
+ await ifWe(sir, 'throws error if signing forbidden verb with restricted pool', async () => {
386
447
  // Create a specific restricted pool config manually
387
448
  const restrictedPoolId = "read_only_pool";
388
449
  const restrictedConfig = createStandardPoolConfig({
@@ -462,7 +523,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
462
523
  await respecfully(sir, 'Revoke Lifecycle', async () => {
463
524
  let revokedKeystone: KeystoneIbGib_V1;
464
525
 
465
- await ifWeMight(sir, 'successfully creates a revocation frame', async () => {
526
+ await ifWe(sir, 'successfully creates a revocation frame', async () => {
466
527
  revokedKeystone = await service.revoke({
467
528
  latestKeystone: genesisKeystone,
468
529
  masterSecret,
@@ -480,7 +541,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
480
541
  iReckon(sir, data.revocationInfo!.proof.claim.verb).willEqual(KEYSTONE_VERB_REVOKE);
481
542
  });
482
543
 
483
- await ifWeMight(sir, 'validates the revocation frame', async () => {
544
+ await ifWe(sir, 'validates the revocation frame', async () => {
484
545
  const errors = await service.validate({
485
546
  prevIbGib: genesisKeystone,
486
547
  currentIbGib: revokedKeystone!,
@@ -491,7 +552,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
491
552
  iReckon(sir, errors.length).asTo('no validation errors').willEqual(0);
492
553
  });
493
554
 
494
- await ifWeMight(sir, 'consumed the revocation pool (Scorched Earth)', async () => {
555
+ await ifWe(sir, 'consumed the revocation pool (Scorched Earth)', async () => {
495
556
  const data = revokedKeystone!.data!;
496
557
  const revokePool = data.challengePools.find(p => p.id === POOL_ID_REVOKE);
497
558
 
@@ -567,7 +628,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
567
628
  });
568
629
 
569
630
  await respecfully(sir, 'Happy Path', async () => {
570
- await ifWeMight(sir, 'authorizes and adds a foreign pool', async () => {
631
+ await ifWe(sir, 'authorizes and adds a foreign pool', async () => {
571
632
  const bobPool = await createForeignPool("pool_bob", ["post"]);
572
633
 
573
634
  const updatedKeystone = await service.addPools({
@@ -606,7 +667,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
606
667
  });
607
668
 
608
669
  await respecfully(sir, 'Permissions & Logic', async () => {
609
- await ifWeMight(sir, 'fails if no pool allows "manage" verb', async () => {
670
+ await ifWe(sir, 'fails if no pool allows "manage" verb', async () => {
610
671
  // 1. Create a restricted keystone
611
672
  let id = "read_only";
612
673
  const restrictedConfig = createStandardPoolConfig({ id, salt: id });
@@ -639,7 +700,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
639
700
  iReckon(sir, errorCaught).asTo('permission denied').isGonnaBeTrue();
640
701
  });
641
702
 
642
- await ifWeMight(sir, 'fails on ID collision', async () => {
703
+ await ifWe(sir, 'fails on ID collision', async () => {
643
704
  // Try to add "pool_bob" again (it was added in Happy Path)
644
705
  const duplicatePool = await createForeignPool("pool_bob");
645
706
 
@@ -727,7 +788,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
727
788
  });
728
789
 
729
790
  await respecfully(sir, 'Happy Path', async () => {
730
- await ifWeMight(sir, 'authorizes and adds a foreign pool', async () => {
791
+ await ifWe(sir, 'authorizes and adds a foreign pool', async () => {
731
792
  const bobPool = await createForeignPool("pool_bob", ["post"]);
732
793
 
733
794
  const updatedKeystone = await service.addPools({
@@ -766,7 +827,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
766
827
  });
767
828
 
768
829
  await respecfully(sir, 'Permissions & Logic', async () => {
769
- await ifWeMight(sir, 'fails if no pool allows "manage" verb', async () => {
830
+ await ifWe(sir, 'fails if no pool allows "manage" verb', async () => {
770
831
  // 1. Create a restricted keystone (read-only)
771
832
  let id = "read_only";
772
833
  const restrictedConfig = createStandardPoolConfig({ id, salt: id });
@@ -799,7 +860,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
799
860
  iReckon(sir, errorCaught).asTo('permission denied').isGonnaBeTrue();
800
861
  });
801
862
 
802
- await ifWeMight(sir, 'fails on ID collision', async () => {
863
+ await ifWe(sir, 'fails on ID collision', async () => {
803
864
  // Try to add "pool_bob" again (it was added in Happy Path)
804
865
  const duplicatePool = await createForeignPool("pool_bob");
805
866
 
@@ -861,7 +922,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
861
922
 
862
923
  await respecfully(sir, 'Proof Granularity & Math', async () => {
863
924
 
864
- await ifWeMight(sir, 'generates exactly the expected number of solutions', async () => {
925
+ await ifWe(sir, 'generates exactly the expected number of solutions', async () => {
865
926
  signedKeystone = await service.sign({
866
927
  latestKeystone: genesisKeystone,
867
928
  masterSecret: aliceSecret,
@@ -878,7 +939,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
878
939
  iReckon(sir, solutions.length).asTo('solution count').willEqual(4);
879
940
  });
880
941
 
881
- await ifWeMight(sir, 'verifies the math manually (White-box Crypto Check)', async () => {
942
+ await ifWe(sir, 'verifies the math manually (White-box Crypto Check)', async () => {
882
943
  const proof = signedKeystone.data!.proofs[0];
883
944
  const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === poolId)!;
884
945
 
@@ -907,7 +968,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
907
968
  }
908
969
  });
909
970
 
910
- await ifWeMight(sir, 'verifies FIFO logic (Deterministic Selection)', async () => {
971
+ await ifWe(sir, 'verifies FIFO logic (Deterministic Selection)', async () => {
911
972
  const proof = signedKeystone.data!.proofs[0];
912
973
  const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === poolId)!;
913
974
 
@@ -926,7 +987,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
926
987
  iReckon(sir, hasSecond).asTo(`Solution includes 2nd FIFO ID (${expectedFifoIds[1]})`).isGonnaBeTrue();
927
988
  });
928
989
 
929
- await ifWeMight(sir, 'verifies Next-Gen Hash-Based Target Binding Selection', async () => {
990
+ await ifWe(sir, 'verifies Next-Gen Hash-Based Target Binding Selection', async () => {
930
991
  const bindingConfig = createStandardPoolConfig({
931
992
  id: "binding_pool",
932
993
  salt: "binding_salt",
@@ -974,7 +1035,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
974
1035
  }
975
1036
  });
976
1037
 
977
- await ifWeMight(sir, 'fails validation if total requested challenges >= size', async () => {
1038
+ await ifWe(sir, 'fails validation if total requested challenges >= size', async () => {
978
1039
  const invalidConfig = createStandardPoolConfig({
979
1040
  id: "invalid_pool",
980
1041
  salt: "invalid_salt",
@@ -993,7 +1054,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
993
1054
 
994
1055
  await respecfully(sir, 'DTO & Serialization', async () => {
995
1056
 
996
- await ifWeMight(sir, 'survives a clone/JSON-cycle without corruption', async () => {
1057
+ await ifWe(sir, 'survives a clone/JSON-cycle without corruption', async () => {
997
1058
  // 1. Create a DTO (simulate network transmission/storage)
998
1059
  // 'clone' does a JSON stringify/parse under the hood (usually) or structured clone.
999
1060
  const dto = clone(signedKeystone);
@@ -1013,7 +1074,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
1013
1074
  iReckon(sir, errors.length).asTo('DTO validation errors').willEqual(0);
1014
1075
  });
1015
1076
 
1016
- await ifWeMight(sir, 'ensures data contains no functions or circular refs', async () => {
1077
+ await ifWe(sir, 'ensures data contains no functions or circular refs', async () => {
1017
1078
  // A crude but effective test: ensure JSON.stringify doesn't throw
1018
1079
  // and the result is equal to the object (if we parsed it back).
1019
1080
 
@@ -1040,7 +1101,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
1040
1101
 
1041
1102
  await respecfully(sir, 'KeystoneProfileBuilder and Modular Schemas', async () => {
1042
1103
 
1043
- await ifWeMight(sir, 'compiles configs successfully with medium profile', async () => {
1104
+ await ifWe(sir, 'compiles configs successfully with medium profile', async () => {
1044
1105
  const builder = KeystoneProfileBuilder.buildKeystone('medium')
1045
1106
  .withUsername('alice')
1046
1107
  .withEmail('alice@example.com')
@@ -1063,7 +1124,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
1063
1124
  iReckon(sir, details.extraInfo).asTo('extraInfo').willEqual('abc');
1064
1125
  });
1065
1126
 
1066
- await ifWeMight(sir, 'supports customized overrides', async () => {
1127
+ await ifWe(sir, 'supports customized overrides', async () => {
1067
1128
  const builder = KeystoneProfileBuilder.buildKeystone('test')
1068
1129
  .customizePool('sync', {
1069
1130
  behavior: {
@@ -1082,7 +1143,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
1082
1143
 
1083
1144
  await respecfully(sir, 'Keystone Metadata Validation', async () => {
1084
1145
 
1085
- await ifWeMight(sir, 'validates correct username and description', async () => {
1146
+ await ifWe(sir, 'validates correct username and description', async () => {
1086
1147
  const service = new KeystoneService_V1();
1087
1148
  const space = new MockIbGibSpace();
1088
1149
  const metaspace = new MockMetaspaceService(space);
@@ -1108,7 +1169,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
1108
1169
  iReckon(sir, errors.length).asTo('errors count').willEqual(0);
1109
1170
  });
1110
1171
 
1111
- await ifWeMight(sir, 'fails validation for invalid username length or character', async () => {
1172
+ await ifWe(sir, 'fails validation for invalid username length or character', async () => {
1112
1173
  const service = new KeystoneService_V1();
1113
1174
  const space = new MockIbGibSpace();
1114
1175
  const metaspace = new MockMetaspaceService(space);
@@ -1133,7 +1194,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
1133
1194
  iReckon(sir, errors[0]).includes('invalid username');
1134
1195
  });
1135
1196
 
1136
- await ifWeMight(sir, 'fails validation for invalid description characters', async () => {
1197
+ await ifWe(sir, 'fails validation for invalid description characters', async () => {
1137
1198
  const service = new KeystoneService_V1();
1138
1199
  const space = new MockIbGibSpace();
1139
1200
  const metaspace = new MockMetaspaceService(space);
@@ -1160,4 +1221,339 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
1160
1221
  });
1161
1222
 
1162
1223
  });
1224
+ });
1225
+
1226
+
1227
+ // ===========================================================================
1228
+ // SUITE E: DELEGATION (Register/Unregister)
1229
+ // ===========================================================================
1230
+
1231
+ await respecfully(sir, 'Suite E: Delegation (Register/Unregister)', async () => {
1232
+
1233
+ const service = new KeystoneService_V1();
1234
+ const parentSecret = "ParentMasterSecret_123";
1235
+ const delegateSecret = "DelegateMasterSecret_456";
1236
+
1237
+ let mockSpace: MockIbGibSpace;
1238
+ let mockMetaspace: any;
1239
+
1240
+ let parentKeystone: KeystoneIbGib_V1;
1241
+ let delegateKeystone: KeystoneIbGib_V1;
1242
+
1243
+ firstOfEach(sir, async () => {
1244
+ mockSpace = new MockIbGibSpace();
1245
+ mockMetaspace = new MockMetaspaceService(mockSpace);
1246
+
1247
+ const salt = (await getUUID()).substring(0, 16);
1248
+ const defaultConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt });
1249
+ const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: salt + "_manage" });
1250
+ manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE];
1251
+
1252
+ // 1. Create Parent Keystone (with manage pool)
1253
+ parentKeystone = await service.genesis({
1254
+ masterSecret: parentSecret,
1255
+ configs: [defaultConfig, manageConfig],
1256
+ metaspace: mockMetaspace,
1257
+ space: mockSpace as any,
1258
+ });
1259
+
1260
+ // 2. Create Delegate Keystone
1261
+ const delegateConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt + "_delegate" });
1262
+ delegateKeystone = await service.genesis({
1263
+ masterSecret: delegateSecret,
1264
+ configs: [delegateConfig],
1265
+ metaspace: mockMetaspace,
1266
+ space: mockSpace as any,
1267
+ });
1268
+ });
1269
+
1270
+ await ifWe(sir, 'registers a delegate keystone and evolves parent using manage pool', async () => {
1271
+ // Register the delegate
1272
+ const evolvedParent = await service.registerDelegate({
1273
+ parentKeystone,
1274
+ delegateKeystone,
1275
+ masterSecret: parentSecret,
1276
+ metaspace: mockMetaspace,
1277
+ space: mockSpace as any,
1278
+ allowedVerbs: [KEYSTONE_VERB_SYNC],
1279
+ });
1280
+
1281
+ iReckon(sir, evolvedParent).asTo('evolved parent exists').isGonnaBeTruthy();
1282
+ iReckon(sir, evolvedParent.data?.delegates).asTo('delegates map exists').isGonnaBeTruthy();
1283
+
1284
+ const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
1285
+ if (!delegateTjpAddr) { throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 312e986ce4b2ed6c084c03e8f45fa826)`); }
1286
+ const delegateInfo = evolvedParent.data?.delegates?.[delegateTjpAddr];
1287
+ iReckon(sir, delegateInfo).asTo('delegate info registered').isGonnaBeTruthy();
1288
+ iReckon(sir, delegateInfo?.delegateTjpAddr).asTo('delegate tjp matches').willEqual(delegateTjpAddr);
1289
+ });
1290
+
1291
+ await ifWe(sir, 'gets correct delegate status via parent TJP address', async () => {
1292
+ const parentTjpAddr = getTjpAddr({ ibGib: parentKeystone });
1293
+ if (!parentTjpAddr) { throw new Error(`(UNEXPECTED) parentTjpAddr falsy? (E: b7ce2835ccd951d8c99257328be3b826)`); }
1294
+ const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
1295
+ if (!delegateTjpAddr) { throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 3e4878ea19dd8034e827466a8bf09526)`); }
1296
+
1297
+ // Before registration
1298
+ const statusBefore = await service.getDelegateStatus({
1299
+ parentTjpAddr: parentTjpAddr,
1300
+ delegateTjpAddr: delegateTjpAddr,
1301
+ metaspace: mockMetaspace,
1302
+ space: mockSpace as any,
1303
+ });
1304
+ iReckon(sir, statusBefore.registered).asTo('initially unregistered').isGonnaBeFalse();
1305
+
1306
+ // Register
1307
+ const evolvedParent = await service.registerDelegate({
1308
+ parentKeystone,
1309
+ delegateKeystone,
1310
+ masterSecret: parentSecret,
1311
+ metaspace: mockMetaspace,
1312
+ space: mockSpace as any,
1313
+ allowedVerbs: [KEYSTONE_VERB_SYNC],
1314
+ });
1315
+
1316
+ // After registration
1317
+ const statusAfter = await service.getDelegateStatus({
1318
+ parentTjpAddr: parentTjpAddr,
1319
+ delegateTjpAddr: delegateTjpAddr,
1320
+ metaspace: mockMetaspace,
1321
+ space: mockSpace as any,
1322
+ });
1323
+ iReckon(sir, statusAfter.registered).asTo('registered status').isGonnaBeTrue();
1324
+ iReckon(sir, statusAfter.delegateInfo?.delegateTjpAddr).asTo('status tjp matches').willEqual(delegateTjpAddr);
1325
+ });
1326
+
1327
+ await ifWe(sir, 'unregisters a registered delegate keystone', async () => {
1328
+ // Register first
1329
+ const evolvedParent = await service.registerDelegate({
1330
+ parentKeystone,
1331
+ delegateKeystone,
1332
+ masterSecret: parentSecret,
1333
+ metaspace: mockMetaspace,
1334
+ space: mockSpace as any,
1335
+ allowedVerbs: [KEYSTONE_VERB_SYNC],
1336
+ });
1337
+
1338
+ const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
1339
+ if (!delegateTjpAddr) { throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 486b486a8c984a52b8d2b22418c20d26)`); }
1340
+
1341
+ // Unregister
1342
+ const finalParent = await service.unregisterDelegate({
1343
+ parentKeystone: evolvedParent,
1344
+ delegateTjpAddr,
1345
+ masterSecret: parentSecret,
1346
+ metaspace: mockMetaspace,
1347
+ space: mockSpace as any,
1348
+ });
1349
+
1350
+ iReckon(sir, finalParent).asTo('final evolved parent exists').isGonnaBeTruthy();
1351
+ const info = finalParent.data?.delegates?.[delegateTjpAddr];
1352
+ iReckon(sir, info).asTo('delegate info removed').isGonnaBeUndefined();
1353
+ });
1354
+
1355
+ await ifWe(sir, 'throws if parent does not have a manage pool', async () => {
1356
+ // Create a parent WITHOUT a manage pool
1357
+ const salt = (await getUUID()).substring(0, 16);
1358
+ const defaultConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt });
1359
+ const restrictedParent = await service.genesis({
1360
+ masterSecret: parentSecret,
1361
+ configs: [defaultConfig],
1362
+ metaspace: mockMetaspace,
1363
+ space: mockSpace as any,
1364
+ });
1365
+
1366
+ let errorCaught = false;
1367
+ try {
1368
+ await service.registerDelegate({
1369
+ parentKeystone: restrictedParent,
1370
+ delegateKeystone,
1371
+ masterSecret: parentSecret,
1372
+ metaspace: mockMetaspace,
1373
+ space: mockSpace as any,
1374
+ allowedVerbs: [KEYSTONE_VERB_SYNC],
1375
+ });
1376
+ } catch (e: any) {
1377
+ errorCaught = true;
1378
+ iReckon(sir, e.message).includes('Manage pool is required');
1379
+ }
1380
+ iReckon(sir, errorCaught).asTo('delegation rejected').isGonnaBeTrue();
1381
+ });
1382
+ });
1383
+
1384
+ await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
1385
+ const service = new KeystoneService_V1();
1386
+ const parentSecret = "ParentMasterSecret_SuiteG";
1387
+ const delegateSecret = "DelegateMasterSecret_SuiteG";
1388
+
1389
+ let mockSpace: MockIbGibSpace;
1390
+ let mockMetaspace: any;
1391
+
1392
+ let parentKeystone: KeystoneIbGib_V1;
1393
+ let delegateKeystone: KeystoneIbGib_V1;
1394
+
1395
+ firstOfEach(sir, async () => {
1396
+ mockSpace = new MockIbGibSpace();
1397
+ mockMetaspace = new MockMetaspaceService(mockSpace);
1398
+
1399
+ const salt1 = (await getUUID()).substring(0, 16);
1400
+ const salt2 = (await getUUID()).substring(0, 16);
1401
+
1402
+ const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: salt2 });
1403
+ manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE];
1404
+
1405
+ const configs = [
1406
+ createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt1 }),
1407
+ manageConfig
1408
+ ];
1409
+
1410
+ parentKeystone = await service.genesis({
1411
+ masterSecret: parentSecret,
1412
+ configs,
1413
+ metaspace: mockMetaspace,
1414
+ space: mockSpace as any,
1415
+ });
1416
+
1417
+ delegateKeystone = await service.genesis({
1418
+ masterSecret: delegateSecret,
1419
+ configs: [createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt1 })],
1420
+ metaspace: mockMetaspace,
1421
+ space: mockSpace as any,
1422
+ });
1423
+ });
1424
+
1425
+ await ifWe(sir, 'validateKeystoneIb returns true for valid keystone ib and false for others', async () => {
1426
+ iReckon(sir, await validateKeystoneIb({ ib: 'keystone' })).isGonnaBeTrue();
1427
+ iReckon(sir, await validateKeystoneIb({ ib: 'keystone user info' })).isGonnaBeTrue();
1428
+ iReckon(sir, await validateKeystoneIb({ ib: 'invalidAtom' })).isGonnaBeFalse();
1429
+ iReckon(sir, await validateKeystoneIb({ ib: 'not_keystone sub_atom' })).isGonnaBeFalse();
1430
+ });
1431
+
1432
+ await ifWe(sir, 'validateGenesisKeystone fails validation if ib is invalid', async () => {
1433
+ const invalidGenesis = {
1434
+ ...parentKeystone,
1435
+ ib: 'not_a_keystone^' + parentKeystone.gib,
1436
+ };
1437
+ const errors = await validateGenesisKeystone({ keystoneIbGib: invalidGenesis });
1438
+ iReckon(sir, errors.length > 0).isGonnaBeTrue();
1439
+ iReckon(sir, errors[0]).includes('invalid keystone ib');
1440
+ });
1441
+
1442
+ await ifWe(sir, 'validateKeystoneTransition passes for valid delegate registration', async () => {
1443
+ const evolvedParent = await service.registerDelegate({
1444
+ parentKeystone,
1445
+ delegateKeystone,
1446
+ masterSecret: parentSecret,
1447
+ metaspace: mockMetaspace,
1448
+ space: mockSpace as any,
1449
+ allowedVerbs: [KEYSTONE_VERB_SYNC],
1450
+ });
1451
+
1452
+ const errors = await validateKeystoneTransition({
1453
+ currentIbGib: evolvedParent,
1454
+ prevIbGib: parentKeystone,
1455
+ });
1456
+ iReckon(sir, errors.length).willEqual(0);
1457
+ });
1458
+
1459
+ await ifWe(sir, 'validateKeystoneTransition fails if delegates map is mutated without manage proof', async () => {
1460
+ const evolvedParent = await service.registerDelegate({
1461
+ parentKeystone,
1462
+ delegateKeystone,
1463
+ masterSecret: parentSecret,
1464
+ metaspace: mockMetaspace,
1465
+ space: mockSpace as any,
1466
+ allowedVerbs: [KEYSTONE_VERB_SYNC],
1467
+ });
1468
+
1469
+ const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
1470
+
1471
+ // Remove the manage verb proof by converting it to sign
1472
+ for (const proof of tamperedParent.data.proofs) {
1473
+ if (proof.claim) {
1474
+ proof.claim.verb = 'sign';
1475
+ }
1476
+ }
1477
+
1478
+ const errors = await validateKeystoneTransition({
1479
+ currentIbGib: tamperedParent,
1480
+ prevIbGib: parentKeystone,
1481
+ });
1482
+ iReckon(sir, errors.length > 0).isGonnaBeTrue();
1483
+ iReckon(sir, errors.some(e => e.includes('Policy Violation: Delegates map was mutated'))).isGonnaBeTrue();
1484
+ });
1485
+
1486
+ await ifWe(sir, 'validateKeystoneTransition fails if delegate key does not match delegateTjpAddr', async () => {
1487
+ const evolvedParent = await service.registerDelegate({
1488
+ parentKeystone,
1489
+ delegateKeystone,
1490
+ masterSecret: parentSecret,
1491
+ metaspace: mockMetaspace,
1492
+ space: mockSpace as any,
1493
+ allowedVerbs: [KEYSTONE_VERB_SYNC],
1494
+ });
1495
+
1496
+ const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
1497
+ const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
1498
+ if (!delegateTjpAddr) { throw new Error(`delegateTjpAddr falsy?`); }
1499
+
1500
+ const info = tamperedParent.data.delegates![delegateTjpAddr];
1501
+ delete tamperedParent.data.delegates![delegateTjpAddr];
1502
+ tamperedParent.data.delegates!['mismatched_key'] = info;
1503
+
1504
+ const errors = await validateKeystoneTransition({
1505
+ currentIbGib: tamperedParent,
1506
+ prevIbGib: parentKeystone,
1507
+ });
1508
+ iReckon(sir, errors.length > 0).isGonnaBeTrue();
1509
+ iReckon(sir, errors.some(e => e.includes('does not match delegateTjpAddr'))).isGonnaBeTrue();
1510
+ });
1511
+
1512
+ await ifWe(sir, 'validateKeystoneTransition fails if delegate addresses have invalid keystone ib', async () => {
1513
+ const evolvedParent = await service.registerDelegate({
1514
+ parentKeystone,
1515
+ delegateKeystone,
1516
+ masterSecret: parentSecret,
1517
+ metaspace: mockMetaspace,
1518
+ space: mockSpace as any,
1519
+ allowedVerbs: [KEYSTONE_VERB_SYNC],
1520
+ });
1521
+
1522
+ const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
1523
+ const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
1524
+ if (!delegateTjpAddr) { throw new Error(`delegateTjpAddr falsy?`); }
1525
+
1526
+ tamperedParent.data.delegates![delegateTjpAddr].delegateAddr = 'invalid_ib^12345';
1527
+
1528
+ const errors = await validateKeystoneTransition({
1529
+ currentIbGib: tamperedParent,
1530
+ prevIbGib: parentKeystone,
1531
+ });
1532
+ iReckon(sir, errors.length > 0).isGonnaBeTrue();
1533
+ iReckon(sir, errors.some(e => e.includes('not a valid keystone address'))).isGonnaBeTrue();
1534
+ });
1535
+
1536
+ await ifWe(sir, 'validateKeystoneTransition fails if thisAddr is not in the parent timeline history', async () => {
1537
+ const evolvedParent = await service.registerDelegate({
1538
+ parentKeystone,
1539
+ delegateKeystone,
1540
+ masterSecret: parentSecret,
1541
+ metaspace: mockMetaspace,
1542
+ space: mockSpace as any,
1543
+ allowedVerbs: [KEYSTONE_VERB_SYNC],
1544
+ });
1545
+
1546
+ const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
1547
+ const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
1548
+ if (!delegateTjpAddr) { throw new Error(`delegateTjpAddr falsy?`); }
1549
+
1550
+ tamperedParent.data.delegates![delegateTjpAddr].thisAddr = 'keystone^randomgib';
1551
+
1552
+ const errors = await validateKeystoneTransition({
1553
+ currentIbGib: tamperedParent,
1554
+ prevIbGib: parentKeystone,
1555
+ });
1556
+ iReckon(sir, errors.length > 0).isGonnaBeTrue();
1557
+ iReckon(sir, errors.some(e => e.includes('not a valid historical address in the parent keystone timeline'))).isGonnaBeTrue();
1558
+ });
1163
1559
  });