@ibgib/core-gib 0.1.63 → 0.1.64

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 (30) hide show
  1. package/dist/keystone/keystone-constants.d.mts +1 -0
  2. package/dist/keystone/keystone-constants.d.mts.map +1 -1
  3. package/dist/keystone/keystone-constants.mjs +1 -0
  4. package/dist/keystone/keystone-constants.mjs.map +1 -1
  5. package/dist/keystone/keystone-helpers.d.mts +19 -1
  6. package/dist/keystone/keystone-helpers.d.mts.map +1 -1
  7. package/dist/keystone/keystone-helpers.mjs +191 -16
  8. package/dist/keystone/keystone-helpers.mjs.map +1 -1
  9. package/dist/keystone/keystone-service-v1.d.mts +82 -1
  10. package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
  11. package/dist/keystone/keystone-service-v1.mjs +321 -27
  12. package/dist/keystone/keystone-service-v1.mjs.map +1 -1
  13. package/dist/keystone/keystone-service-v1.respec.mjs +474 -47
  14. package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
  15. package/dist/keystone/keystone-types.d.mts +26 -2
  16. package/dist/keystone/keystone-types.d.mts.map +1 -1
  17. package/dist/keystone/keystone-types.mjs +13 -0
  18. package/dist/keystone/keystone-types.mjs.map +1 -1
  19. package/dist/keystone/policy/profiles/profile-domain.json +0 -15
  20. package/dist/keystone/policy/schemas/keystone.high.schema.json +1 -1
  21. package/dist/keystone/policy/schemas/keystone.medium.schema.json +1 -1
  22. package/package.json +2 -2
  23. package/src/keystone/keystone-constants.mts +1 -0
  24. package/src/keystone/keystone-helpers.mts +223 -15
  25. package/src/keystone/keystone-service-v1.mts +387 -28
  26. package/src/keystone/keystone-service-v1.respec.mts +1990 -1500
  27. package/src/keystone/keystone-types.mts +43 -2
  28. package/src/keystone/policy/profiles/profile-domain.json +1 -16
  29. package/src/keystone/policy/schemas/keystone.high.schema.json +1 -1
  30. package/src/keystone/policy/schemas/keystone.medium.schema.json +1 -1
@@ -1,4 +1,4 @@
1
- import { extractErrorMsg, mergeMapsOrArrays_Naive } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
1
+ import { extractErrorMsg, mergeMapsOrArrays_Naive, getUUID } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
2
2
  import { getIbGibAddr, getIbAndGib } from '@ibgib/ts-gib/dist/helper.mjs';
3
3
  import { IbGibAddr } from '@ibgib/ts-gib/dist/types.mjs';
4
4
  import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
@@ -7,18 +7,24 @@ import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
7
7
  import {
8
8
  KeystoneData_V1, KeystoneIbGib_V1, KeystonePoolConfig, KeystoneClaim,
9
9
  KeystoneChallengePool, KeystoneReplenishStrategy, KeystoneRevocationInfo,
10
- DelegateKeystoneStatus,
10
+ DelegateKeystoneStatus, KeystoneClaimType,
11
+ ClaimDetails_AddPool, ClaimDetails_ReplacePool, ClaimDetails_ChangePassword
11
12
  } from './keystone-types.mjs';
12
13
  import { KeystoneStrategyFactory } from './strategy/keystone-strategy-factory.mjs';
14
+ import { createStandardPoolConfig } from './keystone-config-builder.mjs';
13
15
  import {
14
16
  POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE,
15
- KEYSTONE_CHECKPOINT_FREQUENCY, POOL_ID_MANAGE, KEYSTONE_VERB_SYNC
17
+ KEYSTONE_CHECKPOINT_FREQUENCY, POOL_ID_MANAGE, KEYSTONE_VERB_SYNC,
18
+ POOL_ID_CUSTODIAN_MANAGE,
19
+ KEYSTONE_CONFIG_DEFAULT_SIZE, KEYSTONE_CONFIG_DEFAULT_SEQUENTIAL,
20
+ KEYSTONE_CONFIG_DEFAULT_RANDOM, KEYSTONE_CONFIG_DEFAULT_BINDING
16
21
  } from './keystone-constants.mjs';
17
22
  import {
18
23
  createKeystoneIbGibImpl, evolvePersistAndRegisterKeystone,
19
24
  generateOpaqueChallengeId, resolveTargetPool, selectChallengeIds,
20
25
  solveAndReplenish, validateKeystoneTransition,
21
- validateGenesisKeystone, validateKeystoneGraph
26
+ validateGenesisKeystone, validateKeystoneGraph,
27
+ generatePoolChallenges, rotatePoolChallenges
22
28
  } from './keystone-helpers.mjs';
23
29
  import { getLatestAddrs } from '../witness/space/space-helper.mjs';
24
30
  import { IbGibSpaceAny } from '../witness/space/space-base-v1.mjs';
@@ -45,12 +51,14 @@ export class KeystoneService_V1 {
45
51
  configs,
46
52
  metaspace,
47
53
  space,
54
+ isPrimary,
48
55
  }: {
49
56
  masterSecret: string;
50
57
  frameDetails?: any;
51
58
  configs: KeystonePoolConfig[];
52
59
  metaspace: MetaspaceService;
53
60
  space: IbGibSpaceAny;
61
+ isPrimary?: boolean;
54
62
  }): Promise<KeystoneIbGib_V1> {
55
63
  const lc = `${this.lc}[${this.genesis.name}]`;
56
64
  try {
@@ -59,24 +67,10 @@ export class KeystoneService_V1 {
59
67
  const challengePools: KeystoneChallengePool[] = [];
60
68
 
61
69
  for (const config of configs) {
62
- const strategy = KeystoneStrategyFactory.create({ config });
63
- const poolSecret = await strategy.derivePoolSecret({ masterSecret });
64
- const challenges: { [id: string]: any } = {};
65
-
66
- const targetSize = config.behavior.size;
67
- const timestamp = Date.now().toString();
68
-
69
- for (let i = 0; i < targetSize; i++) {
70
- const challengeId = await generateOpaqueChallengeId({
71
- salt: config.salt, timestamp, index: i
72
- });
73
-
74
- const solution = await strategy.generateSolution({
75
- poolSecret, poolId: config.id, challengeId,
76
- });
77
- const challenge = await strategy.generateChallenge({ solution });
78
- challenges[challengeId] = challenge;
79
- }
70
+ const challenges = await generatePoolChallenges({
71
+ config,
72
+ masterSecret
73
+ });
80
74
 
81
75
  challengePools.push({
82
76
  id: config.id,
@@ -88,7 +82,12 @@ export class KeystoneService_V1 {
88
82
  if (challengePools.length === 0) { throw new Error(`No challenge pools created. (E: 38e538530996940e1f16a8b199995825)`); }
89
83
 
90
84
  const data: KeystoneData_V1 = { challengePools, proofs: [] };
91
- if (frameDetails) { data.frameDetails = frameDetails; }
85
+ if (frameDetails || isPrimary !== undefined) {
86
+ data.frameDetails = { ...(frameDetails || {}) };
87
+ if (isPrimary !== undefined) {
88
+ data.frameDetails.isPrimary = isPrimary;
89
+ }
90
+ }
92
91
  const keystoneIbGib = await createKeystoneIbGibImpl({ data, metaspace, space });
93
92
  return keystoneIbGib;
94
93
  } catch (error) {
@@ -585,10 +584,10 @@ export class KeystoneService_V1 {
585
584
 
586
585
  const prevData = latestKeystone.data!;
587
586
 
588
- // 1. Identify Authority (Resolve Revoke Pool)
587
+ // 1. Identify Authority (Resolve Revoke Pool by verb)
589
588
  const pool = resolveTargetPool({
590
589
  pools: prevData.challengePools,
591
- poolId: POOL_ID_REVOKE // Explicitly require the special revoke pool
590
+ verb: KEYSTONE_VERB_REVOKE
592
591
  });
593
592
 
594
593
  // 2. Construct Claim
@@ -720,13 +719,15 @@ export class KeystoneService_V1 {
720
719
 
721
720
  if (logalot) { console.log(`${lc} Authorized via pool: ${adminPool.id}`); }
722
721
 
723
- // 2. Construct the Management Claim
722
+ // 2. Construct the Management Claim with typed details
724
723
  const target = getIbGibAddr({ ibGib: latestKeystone });
725
724
  const claim: Partial<KeystoneClaim> = {
726
725
  verb: KEYSTONE_VERB_MANAGE,
727
726
  target, // I am managing myself
728
- // Scope creates a cryptographic commitment to WHICH pools are being added
729
- scope: JSON.stringify({ add: newPools.map(p => p.id) })
727
+ details: {
728
+ type: KeystoneClaimType.add_pool,
729
+ info: { add: newPools.map(p => p.id) } as ClaimDetails_AddPool
730
+ }
730
731
  };
731
732
 
732
733
  // 3. Calculate Costs
@@ -854,4 +855,362 @@ export class KeystoneService_V1 {
854
855
  throw error;
855
856
  }
856
857
  }
858
+
859
+ /**
860
+ * Retrieves common aggregated info for a keystone timeline.
861
+ */
862
+ async getKeystoneCommonInfo({
863
+ addr,
864
+ ibGib,
865
+ metaspace,
866
+ space,
867
+ }: {
868
+ addr?: string;
869
+ ibGib?: KeystoneIbGib_V1;
870
+ metaspace: MetaspaceService;
871
+ space: IbGibSpaceAny;
872
+ }): Promise<{
873
+ name: string;
874
+ description: string;
875
+ isPrimary: boolean;
876
+ aggregateDetails: any;
877
+ latestFrameDetails: any;
878
+ n: number;
879
+ timestamp?: string;
880
+ timestampMs?: number;
881
+ } | null> {
882
+ const lc = `${this.lc}[getKeystoneCommonInfo]`;
883
+ try {
884
+ let targetAddr = addr;
885
+ let targetIbGib: any = ibGib;
886
+ if (!targetAddr) {
887
+ if (!targetIbGib) { return null; }
888
+ targetAddr = getIbGibAddr({ ibGib: targetIbGib });
889
+ }
890
+ if (!targetIbGib) {
891
+ const { ib, gib } = getIbAndGib({ ibGibAddr: targetAddr });
892
+ targetIbGib = { ib, gib };
893
+ }
894
+
895
+ const resGetLatest = await getLatestAddrs({
896
+ ibGibs: [targetIbGib],
897
+ space,
898
+ });
899
+
900
+ if (!resGetLatest.data?.latestAddrsMap) { return null; }
901
+ const latestAddr = resGetLatest.data.latestAddrsMap[targetAddr] || Object.values(resGetLatest.data.latestAddrsMap)[0];
902
+ if (!latestAddr) { return null; }
903
+
904
+ const resGet = await metaspace.get({ addr: latestAddr, space });
905
+ const latestKeystone = resGet.ibGibs?.at(0) as KeystoneIbGib_V1;
906
+ if (!latestKeystone || !latestKeystone.data) { return null; }
907
+
908
+ const aggregateDetails = await this.getAggregateDetails({
909
+ latestKeystone,
910
+ metaspace,
911
+ space
912
+ });
913
+
914
+ const name = aggregateDetails?.username || aggregateDetails?.profile?.name || aggregateDetails?.name || '';
915
+ const description = aggregateDetails?.description || aggregateDetails?.profile?.description || '';
916
+ const isPrimary = !!aggregateDetails?.isPrimary || !!aggregateDetails?.profile?.isPrimary;
917
+ const latestFrameDetails = latestKeystone.data.frameDetails || {};
918
+ const n = latestKeystone.data.n ?? 0;
919
+ const timestamp = latestFrameDetails?.timestamp || aggregateDetails?.timestamp || '';
920
+ const timestampMs = latestFrameDetails?.timestampMs || aggregateDetails?.timestampMs || undefined;
921
+
922
+ return {
923
+ name,
924
+ description,
925
+ isPrimary,
926
+ aggregateDetails,
927
+ latestFrameDetails,
928
+ n,
929
+ timestamp,
930
+ timestampMs
931
+ };
932
+ } catch (error) {
933
+ console.error(`${lc} ${extractErrorMsg(error)}`);
934
+ return null;
935
+ }
936
+ }
937
+
938
+ /**
939
+ * Replaces a challenge pool (such as manage or custodian-manage) with a new pool,
940
+ * authorizing the transition via the specified signing pool.
941
+ *
942
+ * @param latestKeystone The current tip frame of the keystone timeline.
943
+ * - Visibility: Public (Public Merkle frame, safe for logs/ledger).
944
+ * - Generator: Client/Server (Constructed locally and synced).
945
+ *
946
+ * @param newPool The fully constructed new pool to place in the next frame.
947
+ * - Visibility: Public (Public configuration and public challenges, safe for ledger).
948
+ * - Generator: Client/Server (Generated dynamically by the party proposing the update).
949
+ *
950
+ * @param signingSecret The master secret or derived KDF secret of the pool authorizing the change.
951
+ * - Visibility: Private (Confidential secret credential. Must never go over the wire).
952
+ * - Generator: Client/Server (Known only to the signing party).
953
+ *
954
+ * @param signingPoolId The ID of the pool used to authorize the transition (defaults to 'manage').
955
+ * - Visibility: Public (Non-sensitive configuration string).
956
+ * - Generator: Client/Server (From pool configuration).
957
+ */
958
+ async replacePool({
959
+ latestKeystone,
960
+ newPool,
961
+ signingSecret,
962
+ signingPoolId = POOL_ID_MANAGE,
963
+ frameDetails,
964
+ metaspace,
965
+ space,
966
+ }: {
967
+ latestKeystone: KeystoneIbGib_V1;
968
+ newPool: KeystoneChallengePool;
969
+ signingSecret: string;
970
+ signingPoolId?: string;
971
+ frameDetails?: any;
972
+ metaspace: MetaspaceService;
973
+ space: IbGibSpaceAny;
974
+ }): Promise<KeystoneIbGib_V1> {
975
+ const lc = `${this.lc}[${this.replacePool.name}]`;
976
+ try {
977
+ if (logalot) { console.log(`${lc} starting... replacing pool: ${newPool.id}`); }
978
+
979
+ if (!latestKeystone.data) { throw new Error(`(UNEXPECTED) latestKeystone.data falsy? (E: 1334c8faed128166a999d428c7805b25)`); }
980
+ const prevData = latestKeystone.data;
981
+
982
+ if (prevData.revocationInfo) { throw new Error(`Keystone has been revoked. Cannot replace pool. (E: 2599f8f51c78d722252ddb2894fdbe25)`); }
983
+
984
+ // 1. Identify Authority (Resolve Signing Pool)
985
+ const prevPools = prevData.challengePools;
986
+ const signingPool = prevPools.find(p => p.id === signingPoolId);
987
+ if (!signingPool) {
988
+ throw new Error(`Signing pool not found: ${signingPoolId} (E: 3599f8f51c78d722252ddb2894fdbe25)`);
989
+ }
990
+
991
+ // 2. Construct the Management Claim with typed details
992
+ const target = getIbGibAddr({ ibGib: latestKeystone });
993
+ const claim: Partial<KeystoneClaim> = {
994
+ verb: KEYSTONE_VERB_MANAGE,
995
+ target,
996
+ details: {
997
+ type: KeystoneClaimType.replace_pool,
998
+ info: { replace: newPool.id } as ClaimDetails_ReplacePool
999
+ }
1000
+ };
1001
+
1002
+ // 3. Calculate Costs
1003
+ const idsToSolve = await selectChallengeIds({
1004
+ pool: signingPool,
1005
+ targetAddr: target
1006
+ });
1007
+
1008
+ // 4. Pay the Cost (Solve & Replenish)
1009
+ const { proof, nextPools } = await solveAndReplenish({
1010
+ targetPoolId: signingPoolId,
1011
+ prevPools,
1012
+ masterSecret: signingSecret,
1013
+ challengeIds: idsToSolve,
1014
+ claim,
1015
+ });
1016
+
1017
+ // 5. Replace target pool in nextPools
1018
+ const targetIdx = nextPools.findIndex(p => p.id === newPool.id);
1019
+ if (targetIdx === -1) {
1020
+ throw new Error(`Target pool to replace '${newPool.id}' not found in keystone challenge pools.`);
1021
+ }
1022
+ nextPools[targetIdx] = newPool;
1023
+
1024
+ // 6. Construct New Data
1025
+ const n = (prevData.n ?? 0) + 1;
1026
+ let checkpointDetails: any | undefined;
1027
+ if (n % KEYSTONE_CHECKPOINT_FREQUENCY === 0) {
1028
+ const currentFrameView: KeystoneIbGib_V1 = {
1029
+ ...latestKeystone,
1030
+ data: { ...prevData, frameDetails }
1031
+ };
1032
+ checkpointDetails = await this.getAggregateDetails({
1033
+ latestKeystone: currentFrameView,
1034
+ metaspace,
1035
+ space
1036
+ });
1037
+ }
1038
+
1039
+ const newData: KeystoneData_V1 = {
1040
+ challengePools: nextPools,
1041
+ proofs: [proof],
1042
+ frameDetails,
1043
+ checkpointDetails,
1044
+ ...(prevData.delegates ? { delegates: prevData.delegates } : {}),
1045
+ };
1046
+
1047
+ // 7. Commit
1048
+ const newKeystone = await evolvePersistAndRegisterKeystone({
1049
+ prevIbGib: latestKeystone,
1050
+ newData,
1051
+ metaspace,
1052
+ space
1053
+ });
1054
+
1055
+ return newKeystone;
1056
+ } catch (error) {
1057
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1058
+ throw error;
1059
+ } finally {
1060
+ if (logalot) { console.log(`${lc} complete.`); }
1061
+ }
1062
+ }
1063
+
1064
+ /**
1065
+ * Evolve the keystone by rotating the primary user manage pool (`manage`) to a new master secret (passphrase).
1066
+ * This creates a new frame signed by the specified `signingPoolId` using `signingSecret`.
1067
+ *
1068
+ * Supports recovery (signing via `custodian-manage` using the custodian secret) and normal rotation
1069
+ * (signing via `manage` using the old password).
1070
+ *
1071
+ * @param latestKeystone The current tip frame of the keystone.
1072
+ * - Visibility: Public (Public Merkle frame, safe for ledger).
1073
+ * - Generator: Client/Server (Constructed locally and synced).
1074
+ *
1075
+ * @param newMasterSecret The new passphrase/master secret to set for the user's primary pools.
1076
+ * - Visibility: Private (Confidential secret credential. Must never go over the wire).
1077
+ * - Generator: Client (From user input in settings).
1078
+ *
1079
+ * @param signingSecret The secret of the pool authorizing this change (either old passphrase or derived custodian secret).
1080
+ * - Visibility: Private (Confidential secret. Must never go over the wire).
1081
+ * - Generator: Client/Server (Known only to the signing party).
1082
+ *
1083
+ * @param signingPoolId The ID of the pool used to authorize the rotation (defaults to 'manage').
1084
+ * - Visibility: Public (Non-sensitive ID string).
1085
+ * - Generator: Client/Server (From pool configuration).
1086
+ */
1087
+ async changePassword({
1088
+ latestKeystone,
1089
+ newMasterSecret,
1090
+ signingSecret,
1091
+ signingPoolId = POOL_ID_MANAGE,
1092
+ poolIds,
1093
+ frameDetails,
1094
+ metaspace,
1095
+ space,
1096
+ }: {
1097
+ latestKeystone: KeystoneIbGib_V1;
1098
+ newMasterSecret: string;
1099
+ signingSecret: string;
1100
+ signingPoolId?: string;
1101
+ poolIds?: string[];
1102
+ frameDetails?: any;
1103
+ metaspace: MetaspaceService;
1104
+ space: IbGibSpaceAny;
1105
+ }): Promise<KeystoneIbGib_V1> {
1106
+ const lc = `${this.lc}[${this.changePassword.name}]`;
1107
+ try {
1108
+ if (logalot) { console.log(`${lc} starting...`); }
1109
+
1110
+ if (!latestKeystone.data) { throw new Error(`latestKeystone.data falsy`); }
1111
+ if (latestKeystone.data.revocationInfo) { throw new Error(`Keystone has been revoked. Cannot change password. (E: 8599f8f51c78d722252ddb2894fdbe25)`); }
1112
+
1113
+ const prevPools = latestKeystone.data.challengePools;
1114
+ const signingPool = prevPools.find(p => p.id === signingPoolId);
1115
+ if (!signingPool) {
1116
+ throw new Error(`Signing pool not found: ${signingPoolId} (E: 3599f8f51c78d722252ddb2894fdbe25)`);
1117
+ }
1118
+
1119
+ // 1. Resolve target pools to rotate
1120
+ let targets: KeystoneChallengePool[] = [];
1121
+ if (poolIds && poolIds.length > 0) {
1122
+ for (const id of poolIds) {
1123
+ const found = prevPools.find(p => p.id === id);
1124
+ if (!found) {
1125
+ throw new Error(`Target pool to rotate not found: ${id} (E: 4599f8f51c78d722252ddb2894fdbe25)`);
1126
+ }
1127
+ targets.push(found);
1128
+ }
1129
+ } else {
1130
+ targets = prevPools.filter(p => !p.isForeign);
1131
+ if (targets.length === 0) {
1132
+ throw new Error(`No non-foreign pools found to rotate. (E: 5599f8f51c78d722252ddb2894fdbe25)`);
1133
+ }
1134
+ }
1135
+
1136
+ // 2. Rotate the target pools
1137
+ const rotatedPools: KeystoneChallengePool[] = [];
1138
+ for (const targetPool of targets) {
1139
+ const rotated = await rotatePoolChallenges({
1140
+ prevPool: targetPool,
1141
+ newMasterSecret
1142
+ });
1143
+ rotatedPools.push(rotated);
1144
+ }
1145
+
1146
+ // 3. Construct Management Claim with typed details
1147
+ const target = getIbGibAddr({ ibGib: latestKeystone });
1148
+ const claim: Partial<KeystoneClaim> = {
1149
+ verb: KEYSTONE_VERB_MANAGE,
1150
+ target,
1151
+ details: {
1152
+ type: KeystoneClaimType.change_password,
1153
+ info: { change: rotatedPools.map(p => p.id) } as ClaimDetails_ChangePassword
1154
+ }
1155
+ };
1156
+
1157
+ // 4. Calculate Costs
1158
+ const idsToSolve = await selectChallengeIds({
1159
+ pool: signingPool,
1160
+ targetAddr: target
1161
+ });
1162
+
1163
+ // 5. Pay the Cost (Solve & Replenish)
1164
+ const { proof, nextPools } = await solveAndReplenish({
1165
+ targetPoolId: signingPoolId,
1166
+ prevPools,
1167
+ masterSecret: signingSecret,
1168
+ challengeIds: idsToSolve,
1169
+ claim
1170
+ });
1171
+
1172
+ // 6. Swap the rotated pools in nextPools
1173
+ for (const rotated of rotatedPools) {
1174
+ const idx = nextPools.findIndex(p => p.id === rotated.id);
1175
+ if (idx === -1) {
1176
+ throw new Error(`Rotated pool ${rotated.id} not found in next pools list. (E: 6599f8f51c78d722252ddb2894fdbe25)`);
1177
+ }
1178
+ nextPools[idx] = rotated;
1179
+ }
1180
+
1181
+ // 7. Construct New Data
1182
+ const n = (latestKeystone.data.n ?? 0) + 1;
1183
+ let checkpointDetails: any | undefined;
1184
+ if (n % KEYSTONE_CHECKPOINT_FREQUENCY === 0) {
1185
+ const currentFrameView: KeystoneIbGib_V1 = {
1186
+ ...latestKeystone,
1187
+ data: { ...latestKeystone.data, challengePools: nextPools }
1188
+ };
1189
+ checkpointDetails = await this.getAggregateDetails({
1190
+ latestKeystone: currentFrameView,
1191
+ metaspace,
1192
+ space
1193
+ });
1194
+ }
1195
+
1196
+ const newData: KeystoneData_V1 = {
1197
+ challengePools: nextPools,
1198
+ proofs: [proof],
1199
+ frameDetails,
1200
+ checkpointDetails,
1201
+ ...(latestKeystone.data.delegates ? { delegates: latestKeystone.data.delegates } : {}),
1202
+ };
1203
+
1204
+ // 8. Commit
1205
+ return await evolvePersistAndRegisterKeystone({
1206
+ prevIbGib: latestKeystone,
1207
+ newData,
1208
+ metaspace,
1209
+ space
1210
+ });
1211
+ } catch (error) {
1212
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1213
+ throw error;
1214
+ }
1215
+ }
857
1216
  }