@ibgib/core-gib 0.1.60 → 0.1.62

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 (181) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/common/other/graph-helper.d.mts.map +1 -1
  3. package/dist/common/other/graph-helper.mjs +7 -6
  4. package/dist/common/other/graph-helper.mjs.map +1 -1
  5. package/dist/keystone/keystone-config-builder.d.mts +1 -2
  6. package/dist/keystone/keystone-config-builder.d.mts.map +1 -1
  7. package/dist/keystone/keystone-config-builder.mjs +4 -19
  8. package/dist/keystone/keystone-config-builder.mjs.map +1 -1
  9. package/dist/keystone/keystone-constants.d.mts +2 -1
  10. package/dist/keystone/keystone-constants.d.mts.map +1 -1
  11. package/dist/keystone/keystone-constants.mjs +2 -1
  12. package/dist/keystone/keystone-constants.mjs.map +1 -1
  13. package/dist/keystone/keystone-helpers.d.mts +28 -16
  14. package/dist/keystone/keystone-helpers.d.mts.map +1 -1
  15. package/dist/keystone/keystone-helpers.mjs +155 -76
  16. package/dist/keystone/keystone-helpers.mjs.map +1 -1
  17. package/dist/keystone/keystone-policy-types.d.mts +6 -4
  18. package/dist/keystone/keystone-policy-types.d.mts.map +1 -1
  19. package/dist/keystone/keystone-service-v1.d.mts +38 -2
  20. package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
  21. package/dist/keystone/keystone-service-v1.mjs +177 -11
  22. package/dist/keystone/keystone-service-v1.mjs.map +1 -1
  23. package/dist/keystone/keystone-service-v1.respec.mjs +527 -41
  24. package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
  25. package/dist/keystone/keystone-types.d.mts +53 -11
  26. package/dist/keystone/keystone-types.d.mts.map +1 -1
  27. package/dist/keystone/policy/keystone-profile-builder.d.mts +25 -0
  28. package/dist/keystone/policy/keystone-profile-builder.d.mts.map +1 -0
  29. package/dist/keystone/policy/keystone-profile-builder.mjs +117 -0
  30. package/dist/keystone/policy/keystone-profile-builder.mjs.map +1 -0
  31. package/dist/keystone/policy/profiles/profile-high.json +84 -0
  32. package/dist/keystone/policy/profiles/profile-low.json +84 -0
  33. package/dist/keystone/policy/profiles/profile-medium.json +84 -0
  34. package/dist/keystone/policy/profiles/profile-session.json +84 -0
  35. package/dist/keystone/policy/profiles/profile-test.json +84 -0
  36. package/dist/keystone/policy/schemas/connect-pool.high.schema.json +23 -0
  37. package/dist/keystone/policy/schemas/connect-pool.low.schema.json +23 -0
  38. package/dist/keystone/policy/schemas/connect-pool.medium.schema.json +23 -0
  39. package/dist/keystone/policy/schemas/keystone.high.schema.json +26 -0
  40. package/dist/keystone/policy/schemas/keystone.low.schema.json +26 -0
  41. package/dist/keystone/policy/schemas/keystone.medium.schema.json +26 -0
  42. package/dist/keystone/policy/schemas/manage-pool.high.schema.json +23 -0
  43. package/dist/keystone/policy/schemas/manage-pool.low.schema.json +23 -0
  44. package/dist/keystone/policy/schemas/manage-pool.medium.schema.json +23 -0
  45. package/dist/keystone/policy/schemas/revoke-pool.high.schema.json +23 -0
  46. package/dist/keystone/policy/schemas/revoke-pool.low.schema.json +23 -0
  47. package/dist/keystone/policy/schemas/revoke-pool.medium.schema.json +23 -0
  48. package/dist/keystone/policy/schemas/sign-pool.high.schema.json +23 -0
  49. package/dist/keystone/policy/schemas/sign-pool.low.schema.json +23 -0
  50. package/dist/keystone/policy/schemas/sign-pool.medium.schema.json +23 -0
  51. package/dist/keystone/policy/schemas/sync-pool.high.schema.json +23 -0
  52. package/dist/keystone/policy/schemas/sync-pool.low.schema.json +23 -0
  53. package/dist/keystone/policy/schemas/sync-pool.medium.schema.json +23 -0
  54. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.d.mts +8 -0
  55. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.d.mts.map +1 -0
  56. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs +582 -0
  57. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs.map +1 -0
  58. package/dist/sync/sync-conflict-basic-divergence.withid.respec.d.mts +7 -0
  59. package/dist/sync/sync-conflict-basic-divergence.withid.respec.d.mts.map +1 -0
  60. package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs +274 -0
  61. package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs.map +1 -0
  62. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.d.mts +8 -0
  63. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.d.mts.map +1 -0
  64. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs +322 -0
  65. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs.map +1 -0
  66. package/dist/sync/sync-conflict-text-merge.withid.respec.d.mts +7 -0
  67. package/dist/sync/sync-conflict-text-merge.withid.respec.d.mts.map +1 -0
  68. package/dist/sync/sync-conflict-text-merge.withid.respec.mjs +307 -0
  69. package/dist/sync/sync-conflict-text-merge.withid.respec.mjs.map +1 -0
  70. package/dist/sync/sync-innerspace-constants.withid.respec.d.mts +8 -0
  71. package/dist/sync/sync-innerspace-constants.withid.respec.d.mts.map +1 -0
  72. package/dist/sync/sync-innerspace-constants.withid.respec.mjs +233 -0
  73. package/dist/sync/sync-innerspace-constants.withid.respec.mjs.map +1 -0
  74. package/dist/sync/sync-innerspace-deep-updates.withid.respec.d.mts +7 -0
  75. package/dist/sync/sync-innerspace-deep-updates.withid.respec.d.mts.map +1 -0
  76. package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs +233 -0
  77. package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs.map +1 -0
  78. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.d.mts +7 -0
  79. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.d.mts.map +1 -0
  80. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs +225 -0
  81. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs.map +1 -0
  82. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  83. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.d.mts +7 -0
  84. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.d.mts.map +1 -0
  85. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs +235 -0
  86. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs.map +1 -0
  87. package/dist/sync/sync-innerspace-partial-update.withid.respec.d.mts +7 -0
  88. package/dist/sync/sync-innerspace-partial-update.withid.respec.d.mts.map +1 -0
  89. package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs +211 -0
  90. package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs.map +1 -0
  91. package/dist/sync/sync-innerspace.withid.respec.d.mts +8 -0
  92. package/dist/sync/sync-innerspace.withid.respec.d.mts.map +1 -0
  93. package/dist/sync/sync-innerspace.withid.respec.mjs +260 -0
  94. package/dist/sync/sync-innerspace.withid.respec.mjs.map +1 -0
  95. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts +1 -0
  96. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts.map +1 -1
  97. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs +13 -4
  98. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs.map +1 -1
  99. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +1 -1
  100. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -1
  101. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +5 -3
  102. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
  103. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  104. package/dist/sync/sync-saga-coordinator.mjs +165 -145
  105. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  106. package/dist/sync/sync-withid.pingpong.respec.mjs +8 -8
  107. package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -1
  108. package/dist/timeline/timeline-api.d.mts.map +1 -1
  109. package/dist/timeline/timeline-api.mjs +1 -14
  110. package/dist/timeline/timeline-api.mjs.map +1 -1
  111. package/dist/witness/space/reconciliation-space/reconciliation-space-base.d.mts.map +1 -1
  112. package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs +0 -40
  113. package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs.map +1 -1
  114. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.d.mts.map +1 -1
  115. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs +6 -6
  116. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs.map +1 -1
  117. package/ibgib-foundations.md +2 -1
  118. package/package.json +1 -1
  119. package/src/common/other/graph-helper.mts +7 -6
  120. package/src/keystone/README.md +34 -17
  121. package/src/keystone/docs/CRYPTANALYSIS.md +185 -0
  122. package/src/keystone/docs/architecture.md +45 -3
  123. package/src/keystone/docs/delegation.md +57 -0
  124. package/src/keystone/docs/profiles.md +124 -0
  125. package/src/keystone/keystone-config-builder.mts +3 -21
  126. package/src/keystone/keystone-constants.mts +2 -1
  127. package/src/keystone/keystone-helpers.mts +204 -81
  128. package/src/keystone/keystone-policy-types.mts +6 -4
  129. package/src/keystone/keystone-service-v1.mts +223 -11
  130. package/src/keystone/keystone-service-v1.respec.mts +596 -39
  131. package/src/keystone/keystone-types.mts +55 -9
  132. package/src/keystone/policy/IMPLEMENTATION.md +60 -0
  133. package/src/keystone/policy/README.md +94 -0
  134. package/src/keystone/policy/keystone-profile-builder.mts +131 -0
  135. package/src/keystone/policy/profiles/profile-high.json +84 -0
  136. package/src/keystone/policy/profiles/profile-low.json +84 -0
  137. package/src/keystone/policy/profiles/profile-medium.json +84 -0
  138. package/src/keystone/policy/profiles/profile-session.json +84 -0
  139. package/src/keystone/policy/profiles/profile-test.json +84 -0
  140. package/src/keystone/policy/schemas/connect-pool.high.schema.json +23 -0
  141. package/src/keystone/policy/schemas/connect-pool.low.schema.json +23 -0
  142. package/src/keystone/policy/schemas/connect-pool.medium.schema.json +23 -0
  143. package/src/keystone/policy/schemas/keystone.high.schema.json +26 -0
  144. package/src/keystone/policy/schemas/keystone.low.schema.json +26 -0
  145. package/src/keystone/policy/schemas/keystone.medium.schema.json +26 -0
  146. package/src/keystone/policy/schemas/manage-pool.high.schema.json +23 -0
  147. package/src/keystone/policy/schemas/manage-pool.low.schema.json +23 -0
  148. package/src/keystone/policy/schemas/manage-pool.medium.schema.json +23 -0
  149. package/src/keystone/policy/schemas/revoke-pool.high.schema.json +23 -0
  150. package/src/keystone/policy/schemas/revoke-pool.low.schema.json +23 -0
  151. package/src/keystone/policy/schemas/revoke-pool.medium.schema.json +23 -0
  152. package/src/keystone/policy/schemas/sign-pool.high.schema.json +23 -0
  153. package/src/keystone/policy/schemas/sign-pool.low.schema.json +23 -0
  154. package/src/keystone/policy/schemas/sign-pool.medium.schema.json +23 -0
  155. package/src/keystone/policy/schemas/sync-pool.high.schema.json +23 -0
  156. package/src/keystone/policy/schemas/sync-pool.low.schema.json +23 -0
  157. package/src/keystone/policy/schemas/sync-pool.medium.schema.json +23 -0
  158. package/src/sync/docs/security.md +54 -21
  159. package/src/sync/sync-conflict-adv-multitimelines.withid.respec.mts +706 -0
  160. package/src/sync/sync-conflict-basic-divergence.withid.respec.mts +321 -0
  161. package/src/sync/sync-conflict-basic-multitimelines.withid.respec.mts +374 -0
  162. package/src/sync/sync-conflict-text-merge.withid.respec.mts +347 -0
  163. package/src/sync/sync-innerspace-constants.withid.respec.mts +264 -0
  164. package/src/sync/sync-innerspace-deep-updates.withid.respec.mts +272 -0
  165. package/src/sync/sync-innerspace-dest-ahead.withid.respec.mts +266 -0
  166. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +0 -1
  167. package/src/sync/sync-innerspace-multiple-timelines.withid.respec.mts +273 -0
  168. package/src/sync/sync-innerspace-partial-update.withid.respec.mts +249 -0
  169. package/src/sync/sync-innerspace.withid.respec.mts +265 -0
  170. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mts +12 -4
  171. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +5 -3
  172. package/src/sync/sync-saga-coordinator.mts +159 -143
  173. package/src/sync/sync-withid.pingpong.respec.mts +8 -8
  174. package/src/timeline/timeline-api.mts +1 -15
  175. package/src/timeline/timeline-api.respec.mts +3 -3
  176. package/src/witness/space/reconciliation-space/reconciliation-space-base.mts +0 -46
  177. package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts +6 -5
  178. package/tsconfig.json +6 -1
  179. package/src/keystone/keystone-policy.schema.json +0 -51
  180. package/src/witness/space/reconciliation-space/reconciliation-space-base.mts.OLD.md +0 -884
  181. package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts.OLD.md +0 -125
@@ -8,7 +8,7 @@ import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
8
8
  import { getGib } from "@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs";
9
9
 
10
10
  import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
11
- import { KEYSTONE_ATOM, KEYSTONE_HASH_MAX_ROUNDS, KEYSTONE_POOL_ID_REGEXP, KEYSTONE_SALT_REGEXP } from "./keystone-constants.mjs";
11
+ import { KEYSTONE_ATOM, KEYSTONE_HASH_MAX_ROUNDS, KEYSTONE_POOL_ID_REGEXP, KEYSTONE_SALT_REGEXP, KEYSTONE_USERNAME_REGEXP, KEYSTONE_DESCRIPTION_REGEXP, KEYSTONE_VERB_MANAGE } from "./keystone-constants.mjs";
12
12
  import {
13
13
  KeystoneData_V1, KeystoneIbGib_V1, KeystoneIb_V1, KeystoneChallengePool,
14
14
  DeterministicResult, KeystoneProof, KeystonePoolConfig,
@@ -17,6 +17,7 @@ import {
17
17
  KeystoneChallengeType,
18
18
  KEYSTONE_CHALLENGE_TYPE_VALID_VALUES,
19
19
  KeystonePoolConfig_HashV1,
20
+ DelegateKeystoneInfo,
20
21
  } from "./keystone-types.mjs";
21
22
  import { MetaspaceService } from "../witness/space/metaspace/metaspace-types.mjs";
22
23
  import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
@@ -90,12 +91,28 @@ export async function parseKeystoneIb({
90
91
  }
91
92
  }
92
93
 
94
+ /**
95
+ * Helper to validate if an ib string is a well-formed keystone ib.
96
+ */
97
+ export async function validateKeystoneIb({
98
+ ib,
99
+ }: {
100
+ ib: Ib,
101
+ }): Promise<boolean> {
102
+ try {
103
+ await parseKeystoneIb({ ib });
104
+ return true;
105
+ } catch {
106
+ return false;
107
+ }
108
+ }
109
+
93
110
  /**
94
111
  * The Policy Engine.
95
112
  * Calculates exactly which challenges MUST be consumed based on config and demands.
96
113
  * Enforces STRICT DISTINCTNESS (No double-dipping).
97
114
  */
98
- export function getDeterministicRequirements({
115
+ export async function getDeterministicRequirements({
99
116
  pool,
100
117
  requiredChallengeIds,
101
118
  targetAddr
@@ -103,7 +120,7 @@ export function getDeterministicRequirements({
103
120
  pool: KeystoneChallengePool;
104
121
  requiredChallengeIds?: string[];
105
122
  targetAddr?: string;
106
- }): DeterministicResult {
123
+ }): Promise<DeterministicResult> {
107
124
  const behavior = pool.config.behavior;
108
125
  const mandatory = new Set<string>();
109
126
 
@@ -131,31 +148,34 @@ export function getDeterministicRequirements({
131
148
  }
132
149
 
133
150
  // ---------------------------------------------------------
134
- // 2. Target Binding (Explicit Buckets)
151
+ // 2. Target Binding (Drawing Without Replacement)
135
152
  // ---------------------------------------------------------
136
- if (behavior.targetBindingChars > 0 && targetAddr) {
137
- const { gib } = getIbAndGib({ ibGibAddr: targetAddr });
138
- if (gib) {
139
- // Get required hex prefixes (e.g. 'a', 'b', 'c', '1')
140
- const prefixes = gib !== GIB ?
141
- gib.substring(0, behavior.targetBindingChars).toLowerCase() :
142
- 'abc'; // arbitrary for primitive binding target ibgib
143
-
144
- for (const char of prefixes) {
145
- // Look in the Explicit Bucket
146
- const bucket = pool.bindingMap[char] || [];
147
-
148
- // Find the first ID in this bucket that is still in 'available'
149
- const match = bucket.find(id => available.includes(id));
150
-
151
- if (!match) {
152
- throw new Error(`Entropy Exhaustion. Cannot satisfy binding for char '${char}'. (E: 341b95dc3a58be3e083d1d9c4a0c4925)`);
153
- }
153
+ if (behavior.targetBindingCount > 0 && targetAddr) {
154
+ // Sort remaining available IDs alphabetically to ensure stable ordering across platforms
155
+ let sortedAvailable = [...available].sort();
154
156
 
155
- // Strict: Consume it.
156
- mandatory.add(match);
157
- available = available.filter(id => id !== match);
157
+ // Start hashing path from targetAddr
158
+ let currentHash = await hash({ s: targetAddr });
159
+
160
+ for (let i = 0; i < behavior.targetBindingCount; i++) {
161
+ if (sortedAvailable.length === 0) {
162
+ throw new Error(`Entropy Exhaustion. Cannot satisfy target binding. Sorted available pool is empty. (E: 341b95dc3a58be3e083d1d9c4a0c4925)`);
158
163
  }
164
+
165
+ // Parse the entire hash via BigInt to eliminate modulo bias
166
+ const hashBigInt = BigInt("0x" + currentHash);
167
+ const index = Number(hashBigInt % BigInt(sortedAvailable.length));
168
+ const selectedId = sortedAvailable[index];
169
+
170
+ // Consume selected challenge ID
171
+ mandatory.add(selectedId);
172
+ sortedAvailable.splice(index, 1);
173
+
174
+ // Filter out of insertion-ordered available list (for FIFO)
175
+ available = available.filter(id => id !== selectedId);
176
+
177
+ // Hash recursively for the next draw
178
+ currentHash = await hash({ s: currentHash });
159
179
  }
160
180
  }
161
181
 
@@ -179,42 +199,7 @@ export function getDeterministicRequirements({
179
199
  return { mandatoryIds: mandatory, availableIds: available };
180
200
  }
181
201
 
182
- /**
183
- * Helper to update the Binding Map when adding new Challenge IDs.
184
- * Uses "Implicit Bucketing" (ID start char) but can be extended for full coverage.
185
- */
186
- export function addToBindingMap(
187
- map: { [char: string]: string[] },
188
- challengeId: string
189
- ): void {
190
- const firstChar = challengeId.charAt(0).toLowerCase();
191
- // Validate it is hex
192
- if (/[0-9a-f]/.test(firstChar)) {
193
- if (!map[firstChar]) { map[firstChar] = []; }
194
- map[firstChar].push(challengeId);
195
-
196
- // OPTIONAL: Implement Full Coverage Strategy here?
197
- // e.g. map[challengeId[1]].push(challengeId) ...
198
- // For V1, we stick to Native/Implicit bucket (Index 0).
199
- } else {
200
- throw new Error(`invalid challengeId (${challengeId}). Must start with a hex character. (E: c96ed8460de89e28c801370a0f07f826)`);
201
- }
202
- }
203
202
 
204
- /**
205
- * Helper to clean up Binding Map when removing IDs.
206
- */
207
- export function removeFromBindingMap(
208
- map: { [char: string]: string[] },
209
- challengeId: string
210
- ): void {
211
- // Since we don't know exactly which buckets an ID is in (if we did multi-bucket),
212
- // we strictly should scan all. For V1 Native, we check first char.
213
- // SAFE IMPLEMENTATION: Scan all buckets.
214
- for (const key of Object.keys(map)) {
215
- map[key] = map[key].filter(id => id !== challengeId);
216
- }
217
- }
218
203
 
219
204
  /**
220
205
  * Selects the specific pool to use for an operation based on ID, filter criteria, or verb authorization.
@@ -297,7 +282,7 @@ export function resolveTargetPool({
297
282
  *
298
283
  * @returns Array of unique challenge IDs.
299
284
  */
300
- export function selectChallengeIds({
285
+ export async function selectChallengeIds({
301
286
  pool,
302
287
  targetAddr,
303
288
  requiredChallengeIds,
@@ -311,11 +296,11 @@ export function selectChallengeIds({
311
296
  * Explicit demands from a verifier.
312
297
  */
313
298
  requiredChallengeIds?: string[];
314
- }): string[] {
299
+ }): Promise<string[]> {
315
300
  const lc = `[selectChallengeIds]`;
316
301
  try {
317
302
  // 1. Get Deterministic Requirements
318
- const { mandatoryIds, availableIds } = getDeterministicRequirements({
303
+ const { mandatoryIds, availableIds } = await getDeterministicRequirements({
319
304
  pool,
320
305
  requiredChallengeIds,
321
306
  targetAddr
@@ -398,11 +383,6 @@ export async function applyReplenishmentStrategy({
398
383
 
399
384
  const strategyType = config.behavior.replenish;
400
385
 
401
- // Clean up Binding Map for consumed IDs
402
- consumedIds.forEach(id => {
403
- if (pool.bindingMap) { removeFromBindingMap(pool.bindingMap, id); }
404
- });
405
-
406
386
  if (strategyType === KeystoneReplenishStrategy.topUp) {
407
387
  // Remove consumed
408
388
  consumedIds.forEach(id => delete pool.challenges[id]);
@@ -417,14 +397,9 @@ export async function applyReplenishmentStrategy({
417
397
  poolSecret, poolId: pool.id, challengeId: newId
418
398
  });
419
399
  pool.challenges[newId] = await strategy.generateChallenge({ solution });
420
-
421
- // Update Binding Map
422
- if (!pool.bindingMap) { pool.bindingMap = {}; }
423
- addToBindingMap(pool.bindingMap, newId);
424
400
  }
425
401
  } else if (strategyType === KeystoneReplenishStrategy.replaceAll) {
426
402
  pool.challenges = {};
427
- pool.bindingMap = {};
428
403
 
429
404
  for (let i = 0; i < config.behavior.size; i++) {
430
405
  const newId = await generateOpaqueChallengeId({
@@ -434,13 +409,11 @@ export async function applyReplenishmentStrategy({
434
409
  poolSecret, poolId: pool.id, challengeId: newId
435
410
  });
436
411
  pool.challenges[newId] = await strategy.generateChallenge({ solution });
437
- addToBindingMap(pool.bindingMap, newId);
438
412
  }
439
413
  } else if (strategyType === KeystoneReplenishStrategy.consume) {
440
414
  consumedIds.forEach(id => delete pool.challenges[id]);
441
415
  } else if (strategyType === KeystoneReplenishStrategy.deleteAll) {
442
416
  pool.challenges = {};
443
- pool.bindingMap = {};
444
417
  } else {
445
418
  throw new Error(`Unknown replenish strategy: ${strategyType}. Valid list: ${pretty(KEYSTONE_REPLENISH_STRATEGY_VALID_VALUES)} (E: 0acf56f1e1486240080e11e8046d0825)`);
446
419
  }
@@ -599,10 +572,14 @@ export async function validateChallengePool({ pool }: {
599
572
  }
600
573
 
601
574
  if (pool.config.behavior) {
602
- const { size } = pool.config.behavior;
575
+ const { size, selectSequentially, selectRandomly, targetBindingCount } = pool.config.behavior;
603
576
  if (!size || size === 0) {
604
577
  errors.push(`${lc} invalid pool.config.behavior.size (${size}). Must be positive integer. (E: b221e36ec102bdc944552248ce8fe626)`)
605
578
  }
579
+ const totalRequested = (selectSequentially || 0) + (selectRandomly || 0) + (targetBindingCount || 0);
580
+ if (totalRequested >= size) {
581
+ errors.push(`${lc} Total requested challenges (${totalRequested}) cannot equal or exceed pool size (${size}) to prevent full pool exposure and selection exhaustion. (E: 81cb834f826315264bca81c15fca58f1)`);
582
+ }
606
583
  // todo: more behavior checks
607
584
  } else {
608
585
  errors.push(`${lc} pool.config.behavior falsy (E: bede081c066c39732eefe2f92e296326)`)
@@ -632,6 +609,35 @@ export async function validateChallengePool({ pool }: {
632
609
  }
633
610
 
634
611
 
612
+ export function validateKeystoneMetadata({
613
+ data
614
+ }: {
615
+ data: KeystoneData_V1;
616
+ }): string[] {
617
+ const lc = `[validateKeystoneMetadata]`;
618
+ const errors: string[] = [];
619
+
620
+ const checkDetails = (details: any, source: string) => {
621
+ if (!details) return;
622
+ if (details.username !== undefined) {
623
+ if (typeof details.username !== 'string' || !KEYSTONE_USERNAME_REGEXP.test(details.username)) {
624
+ errors.push(`${lc} invalid username in ${source} (${details.username}). Must match ${KEYSTONE_USERNAME_REGEXP}`);
625
+ }
626
+ }
627
+ if (details.description !== undefined) {
628
+ if (typeof details.description !== 'string' || !KEYSTONE_DESCRIPTION_REGEXP.test(details.description)) {
629
+ errors.push(`${lc} invalid description in ${source} (${details.description}). Must match ${KEYSTONE_DESCRIPTION_REGEXP}`);
630
+ }
631
+ }
632
+ };
633
+
634
+ checkDetails(data.frameDetails, 'frameDetails');
635
+ checkDetails(data.checkpointDetails, 'checkpointDetails');
636
+
637
+ return errors;
638
+ }
639
+
640
+
635
641
  export async function validateGenesisKeystone({
636
642
  keystoneIbGib
637
643
  }: {
@@ -645,6 +651,15 @@ export async function validateGenesisKeystone({
645
651
 
646
652
  const { data, rel8ns } = keystoneIbGib;
647
653
 
654
+ const isValidIb = await validateKeystoneIb({ ib: keystoneIbGib.ib });
655
+ if (!isValidIb) {
656
+ errors.push(`${lc} invalid keystone ib (${keystoneIbGib.ib}). (E: ad7a88bc38c290a184dfbd42be48f526)`);
657
+ }
658
+
659
+ // Validate username/description metadata if present
660
+ const metadataErrors = validateKeystoneMetadata({ data });
661
+ metadataErrors.forEach(x => errors.push(x));
662
+
648
663
  // no proofs/solutions yet
649
664
  if (data.proofs && data.proofs.length > 0) {
650
665
  errors.push(`${lc} proofs already exist on genesis keystone. (E: 7a5e15f20918f1bbd8ffb62857dcd526)`);
@@ -677,6 +692,89 @@ export async function validateGenesisKeystone({
677
692
  }
678
693
  }
679
694
 
695
+ /**
696
+ * Helper to determine if two delegates maps differ.
697
+ */
698
+ export function isDelegatesChanged({
699
+ currDelegates,
700
+ prevDelegates,
701
+ }: {
702
+ currDelegates?: Record<string, DelegateKeystoneInfo>,
703
+ prevDelegates?: Record<string, DelegateKeystoneInfo>,
704
+ }): boolean {
705
+ const curr = currDelegates || {};
706
+ const prev = prevDelegates || {};
707
+ const currKeys = Object.keys(curr);
708
+ const prevKeys = Object.keys(prev);
709
+
710
+ if (currKeys.length !== prevKeys.length) { return true; }
711
+
712
+ for (const key of currKeys) {
713
+ const currVal = curr[key];
714
+ const prevVal = prev[key];
715
+ if (!prevVal) { return true; }
716
+ if (currVal.delegateTjpAddr !== prevVal.delegateTjpAddr ||
717
+ currVal.delegateAddr !== prevVal.delegateAddr ||
718
+ currVal.thisAddr !== prevVal.thisAddr) {
719
+ return true;
720
+ }
721
+ }
722
+
723
+ return false;
724
+ }
725
+
726
+ /**
727
+ * Validates the structure and constraints of the delegates map.
728
+ */
729
+ export async function validateDelegates({
730
+ currDelegates,
731
+ prevIbGib,
732
+ lc,
733
+ errors,
734
+ }: {
735
+ currDelegates: Record<string, DelegateKeystoneInfo>,
736
+ prevIbGib: KeystoneIbGib_V1,
737
+ lc: string,
738
+ errors: string[],
739
+ }): Promise<void> {
740
+ const currKeys = Object.keys(currDelegates);
741
+ const prevAddr = getIbGibAddr({ ibGib: prevIbGib });
742
+
743
+ for (const key of currKeys) {
744
+ const info = currDelegates[key];
745
+ if (key !== info.delegateTjpAddr) {
746
+ errors.push(`${lc} Invalid delegate mapping: Key '${key}' does not match delegateTjpAddr '${info.delegateTjpAddr}'. (E: 9c8da8bc38c290a184dfbd42be48f526)`);
747
+ }
748
+
749
+ // Verify addresses are valid keystone addresses
750
+ const [
751
+ parsedDelegateTjp,
752
+ parsedDelegate,
753
+ parsedThis,
754
+ ] = await Promise.all([
755
+ validateKeystoneIb({ ib: info.delegateTjpAddr.split('^')[0] }),
756
+ validateKeystoneIb({ ib: info.delegateAddr.split('^')[0] }),
757
+ validateKeystoneIb({ ib: info.thisAddr.split('^')[0] }),
758
+ ]);
759
+
760
+ if (!parsedDelegateTjp) {
761
+ errors.push(`${lc} Delegate delegateTjpAddr '${info.delegateTjpAddr}' is not a valid keystone address. (E: 10d8a8bc38c290a184dfbd42be48f526)`);
762
+ }
763
+ if (!parsedDelegate) {
764
+ errors.push(`${lc} Delegate delegateAddr '${info.delegateAddr}' is not a valid keystone address. (E: 20d8a8bc38c290a184dfbd42be48f526)`);
765
+ }
766
+ if (!parsedThis) {
767
+ errors.push(`${lc} Delegate thisAddr '${info.thisAddr}' is not a valid keystone address. (E: 30d8a8bc38c290a184dfbd42be48f526)`);
768
+ }
769
+
770
+ // Verify thisAddr is in the parent's past (or is the current parent address)
771
+ const isHistoricalParent = info.thisAddr === prevAddr || (prevIbGib.rel8ns?.past || []).includes(info.thisAddr);
772
+ if (!isHistoricalParent) {
773
+ errors.push(`${lc} Delegate thisAddr '${info.thisAddr}' is not a valid historical address in the parent keystone timeline. (E: 40d8a8bc38c290a184dfbd42be48f526)`);
774
+ }
775
+ }
776
+ }
777
+
680
778
  /**
681
779
  * Validates the transition from Prev -> Curr.
682
780
  * Enforces Cryptography AND Behavioral Policy.
@@ -707,24 +805,48 @@ export async function validateKeystoneTransition({
707
805
  const currData = currentIbGib.data!;
708
806
  const prevData = prevIbGib.data!;
709
807
 
808
+ const isValidIb = await validateKeystoneIb({ ib: currentIbGib.ib });
809
+ if (!isValidIb) {
810
+ errors.push(`${lc} invalid keystone ib (${currentIbGib.ib}). (E: a98dc8bc38c290a184dfbd42be48f526)`);
811
+ }
812
+
813
+ // Validate username/description metadata if present
814
+ const metadataErrors = validateKeystoneMetadata({ data: currData });
815
+ metadataErrors.forEach(x => errors.push(x));
816
+
817
+ // Validate delegates map mutations if present
818
+ const currDelegates = currData.delegates || {};
819
+ const prevDelegates = prevData.delegates || {};
820
+ const delegatesChanged = isDelegatesChanged({ currDelegates, prevDelegates });
821
+
822
+ if (delegatesChanged) {
823
+ const hasManageProof = currData.proofs?.some(proof => proof.claim?.verb === KEYSTONE_VERB_MANAGE);
824
+ if (!hasManageProof) {
825
+ errors.push(`${lc} Policy Violation: Delegates map was mutated but no proof for manage verb ('${KEYSTONE_VERB_MANAGE}') is present. (E: 819fa8cb28c9b60e68994511a5825)`);
826
+ }
827
+ }
828
+
829
+ // Validate each delegate's structure and constraints
830
+ await validateDelegates({ currDelegates, prevIbGib, lc, errors });
831
+
710
832
  // must be sequential data.n values
711
833
  // note: "-2" because we don't want a "valid" sequential data.n if we
712
834
  // have an invalid prev and a valid currDataN of 0
713
835
  const currDataN = currData.n ?? -2;
714
836
  const prevDataN = prevData.n ?? -2;
715
837
  if (currDataN < 0) {
716
- errors.push('invalid keystone.currData.n is undefined or null.');
838
+ errors.push('invalid keystone.currData.n is undefined or null. (E: 9fbe38704b68829d18ea1d0940d0aa26)');
717
839
  }
718
840
  if (prevDataN < 0) {
719
- errors.push('invalid keystone.prevData.n is undefined or null.');
841
+ errors.push('invalid keystone.prevData.n is undefined or null. (E: 6dc5aa9c87017db6080ed4268b07af26)');
720
842
  };
721
843
  if (currDataN !== (prevDataN + 1)) {
722
- errors.push(`keystone data.n values are not sequential. prevData.n: ${prevDataN}, currData.n: ${currDataN}. `);
844
+ errors.push(`keystone data.n values are not sequential. prevData.n: ${prevDataN}, currData.n: ${currDataN}. (E: 5ae12809f2c67216d38d8ba8d6c42826)`);
723
845
  }
724
846
 
725
847
  for (const proof of currData.proofs) {
726
848
  if (proof.solutions.length === 0) {
727
- errors.push(`Proof ${proof.id || 'unknown'} has no solutions.`);
849
+ errors.push(`Proof ${proof.id || 'unknown'} has no solutions. (E: e2b4683ce0ce30f798ec77841e88c826)`);
728
850
  continue;
729
851
  }
730
852
 
@@ -784,7 +906,7 @@ export async function verifyProofAgainstPool({
784
906
  }
785
907
 
786
908
  // 1. Reconstruct Deterministic Requirements
787
- const { mandatoryIds, availableIds } = getDeterministicRequirements({
909
+ const { mandatoryIds, availableIds } = await getDeterministicRequirements({
788
910
  pool,
789
911
  requiredChallengeIds: proof.requiredChallengeIds,
790
912
  targetAddr: proof.claim.target // Not used extensively in V1 logic yet, mainly for logging/context
@@ -863,6 +985,7 @@ export async function evolvePersistAndRegisterKeystone({
863
985
  if (prevData.challengePools) { dataToRemove.challengePools = []; }
864
986
  if (prevData.frameDetails) { dataToRemove.frameDetails = {}; }
865
987
  if (prevData.checkpointDetails) { dataToRemove.checkpointDetails = {}; }
988
+ if (prevData.delegates) { (dataToRemove as any).delegates = 'remove'; }
866
989
  if (Object.keys(dataToRemove).length === 0) { dataToRemove = undefined; }
867
990
 
868
991
  const resMut8 = await mut8({
@@ -6,20 +6,22 @@ export interface KeystoneBehaviorProfileTemplate {
6
6
  replenish: KeystoneReplenishStrategy;
7
7
  selectSequentially: number;
8
8
  selectRandomly: number;
9
- targetBindingChars: number;
9
+ targetBindingCount: number;
10
10
  }
11
11
 
12
12
  export interface KeystonePoolTemplate {
13
13
  id: string;
14
14
  allowedVerbs: string[];
15
- behaviorProfile?: string; // Reference to a behavior profile key
16
- behaviorInline?: KeystoneBehaviorProfileTemplate; // Or specify behavior inline
15
+ behavior: KeystoneBehaviorProfileTemplate;
17
16
  algo: HashAlgorithm;
18
17
  rounds: number;
19
18
  type?: KeystoneChallengeType;
20
19
  }
21
20
 
22
21
  export interface KeystonePolicyConfigTemplate {
23
- behaviorProfiles?: Record<string, KeystoneBehaviorProfileTemplate>;
22
+ metadata?: {
23
+ name?: string;
24
+ securityLevel?: string;
25
+ };
24
26
  pools: Record<string, KeystonePoolTemplate>;
25
27
  }