@ibgib/node-gib 0.0.14 → 0.0.17

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 (218) hide show
  1. package/data/identity_index/email/hashes/32/13/49/e64f32730f01a61ada211f29eaf0a210fc2386b668ad04d7e8a9b16766.json +4 -0
  2. package/dist/email/email-helpers.d.mts.map +1 -1
  3. package/dist/email/email-helpers.mjs.map +1 -1
  4. package/dist/email/index.d.mts +3 -0
  5. package/dist/email/index.d.mts.map +1 -1
  6. package/dist/email/index.mjs +3 -0
  7. package/dist/email/index.mjs.map +1 -1
  8. package/dist/email/verify/email-verify-constants.d.mts +3 -0
  9. package/dist/email/verify/email-verify-constants.d.mts.map +1 -0
  10. package/dist/email/verify/email-verify-constants.mjs +3 -0
  11. package/dist/email/verify/email-verify-constants.mjs.map +1 -0
  12. package/dist/email/verify/email-verify-helpers.d.mts +107 -0
  13. package/dist/email/verify/email-verify-helpers.d.mts.map +1 -0
  14. package/dist/email/verify/email-verify-helpers.mjs +357 -0
  15. package/dist/email/verify/email-verify-helpers.mjs.map +1 -0
  16. package/dist/email/verify/email-verify-types.d.mts +41 -0
  17. package/dist/email/verify/email-verify-types.d.mts.map +1 -0
  18. package/dist/email/verify/email-verify-types.mjs +2 -0
  19. package/dist/email/verify/email-verify-types.mjs.map +1 -0
  20. package/dist/identity/sso/custodian-auth-helpers.d.mts +45 -0
  21. package/dist/identity/sso/custodian-auth-helpers.d.mts.map +1 -0
  22. package/dist/identity/sso/custodian-auth-helpers.mjs +108 -0
  23. package/dist/identity/sso/custodian-auth-helpers.mjs.map +1 -0
  24. package/dist/identity/sso/sso-config-helper.d.mts +7 -0
  25. package/dist/identity/sso/sso-config-helper.d.mts.map +1 -0
  26. package/dist/identity/sso/sso-config-helper.mjs +42 -0
  27. package/dist/identity/sso/sso-config-helper.mjs.map +1 -0
  28. package/dist/identity/sso/sso-custodian-service.d.mts +139 -0
  29. package/dist/identity/sso/sso-custodian-service.d.mts.map +1 -0
  30. package/dist/identity/sso/sso-custodian-service.mjs +471 -0
  31. package/dist/identity/sso/sso-custodian-service.mjs.map +1 -0
  32. package/dist/identity/sso/sso-types.d.mts +71 -0
  33. package/dist/identity/sso/sso-types.d.mts.map +1 -0
  34. package/dist/identity/sso/sso-types.mjs +7 -0
  35. package/dist/identity/sso/sso-types.mjs.map +1 -0
  36. package/dist/index.d.mts +5 -1
  37. package/dist/index.d.mts.map +1 -1
  38. package/dist/index.mjs +5 -1
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/serve-gib/handlers/api/email/email-outbound.handler.d.mts.map +1 -1
  41. package/dist/serve-gib/handlers/api/email/email-outbound.handler.mjs +9 -2
  42. package/dist/serve-gib/handlers/api/email/email-outbound.handler.mjs.map +1 -1
  43. package/dist/serve-gib/handlers/api/email/email-verification-code.handler.d.mts +29 -0
  44. package/dist/serve-gib/handlers/api/email/email-verification-code.handler.d.mts.map +1 -0
  45. package/dist/serve-gib/handlers/api/email/email-verification-code.handler.mjs +149 -0
  46. package/dist/serve-gib/handlers/api/email/email-verification-code.handler.mjs.map +1 -0
  47. package/dist/serve-gib/handlers/api/email/email-verify-code-onetime.handler.d.mts +24 -0
  48. package/dist/serve-gib/handlers/api/email/email-verify-code-onetime.handler.d.mts.map +1 -0
  49. package/dist/serve-gib/handlers/api/email/email-verify-code-onetime.handler.mjs +212 -0
  50. package/dist/serve-gib/handlers/api/email/email-verify-code-onetime.handler.mjs.map +1 -0
  51. package/dist/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.d.mts +37 -0
  52. package/dist/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.d.mts.map +1 -0
  53. package/dist/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.mjs +192 -0
  54. package/dist/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.mjs.map +1 -0
  55. package/dist/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.d.mts +33 -0
  56. package/dist/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.d.mts.map +1 -0
  57. package/dist/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.mjs +173 -0
  58. package/dist/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.mjs.map +1 -0
  59. package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.d.mts.map +1 -1
  60. package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.mjs +2 -3
  61. package/dist/serve-gib/handlers/api/keystone/keystone-check-email.handler.mjs.map +1 -1
  62. package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.d.mts.map +1 -1
  63. package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.mjs +38 -3
  64. package/dist/serve-gib/handlers/api/keystone/keystone-evolve.handler.mjs.map +1 -1
  65. package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.d.mts.map +1 -1
  66. package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.mjs +54 -24
  67. package/dist/serve-gib/handlers/api/keystone/keystone-genesis.handler.mjs.map +1 -1
  68. package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.d.mts.map +1 -1
  69. package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.mjs +13 -13
  70. package/dist/serve-gib/handlers/api/keystone/keystone-lookup.handler.mjs.map +1 -1
  71. package/dist/serve-gib/handlers/api/keystone/keystone-post.handler.mjs +1 -1
  72. package/dist/serve-gib/handlers/api/keystone/keystone-post.handler.mjs.map +1 -1
  73. package/dist/serve-gib/handlers/api/keystone/sso-config.handler.mjs +3 -3
  74. package/dist/serve-gib/handlers/api/keystone/sso-config.handler.mjs.map +1 -1
  75. package/dist/serve-gib/handlers/api/keystone/sso-link.handler.d.mts.map +1 -1
  76. package/dist/serve-gib/handlers/api/keystone/sso-link.handler.mjs +28 -15
  77. package/dist/serve-gib/handlers/api/keystone/sso-link.handler.mjs.map +1 -1
  78. package/dist/serve-gib/handlers/api/keystone/sso-login.handler.d.mts +29 -15
  79. package/dist/serve-gib/handlers/api/keystone/sso-login.handler.d.mts.map +1 -1
  80. package/dist/serve-gib/handlers/api/keystone/sso-login.handler.mjs +105 -108
  81. package/dist/serve-gib/handlers/api/keystone/sso-login.handler.mjs.map +1 -1
  82. package/dist/serve-gib/identity-index/identity-index-constants.d.mts +3 -2
  83. package/dist/serve-gib/identity-index/identity-index-constants.d.mts.map +1 -1
  84. package/dist/serve-gib/identity-index/identity-index-constants.mjs +4 -2
  85. package/dist/serve-gib/identity-index/identity-index-constants.mjs.map +1 -1
  86. package/dist/serve-gib/identity-index/identity-index-helpers.d.mts.map +1 -1
  87. package/dist/serve-gib/identity-index/identity-index-helpers.mjs +28 -11
  88. package/dist/serve-gib/identity-index/identity-index-helpers.mjs.map +1 -1
  89. package/dist/serve-gib/identity-index/identity-index-service.d.mts +20 -3
  90. package/dist/serve-gib/identity-index/identity-index-service.d.mts.map +1 -1
  91. package/dist/serve-gib/identity-index/identity-index-service.mjs +134 -18
  92. package/dist/serve-gib/identity-index/identity-index-service.mjs.map +1 -1
  93. package/dist/serve-gib/identity-index/identity-index-types.d.mts +4 -2
  94. package/dist/serve-gib/identity-index/identity-index-types.d.mts.map +1 -1
  95. package/dist/serve-gib/index.d.mts +4 -0
  96. package/dist/serve-gib/index.d.mts.map +1 -1
  97. package/dist/serve-gib/index.mjs +12 -0
  98. package/dist/serve-gib/index.mjs.map +1 -1
  99. package/dist/serve-gib/path-constants.d.mts +17 -0
  100. package/dist/serve-gib/path-constants.d.mts.map +1 -1
  101. package/dist/serve-gib/path-constants.mjs +17 -0
  102. package/dist/serve-gib/path-constants.mjs.map +1 -1
  103. package/e2e/identity-custodian-lifecycle.e2e.md +93 -0
  104. package/e2e/identity-sso-lifecycle.e2e.md +132 -0
  105. package/package.json +5 -5
  106. package/src/email/email-helpers.mts +0 -1
  107. package/src/email/index.mts +3 -0
  108. package/src/email/verify/email-verify-constants.mts +3 -0
  109. package/src/email/verify/email-verify-helpers.mts +466 -0
  110. package/src/email/verify/email-verify-types.mts +47 -0
  111. package/src/identity/sso/custodian-auth-helpers.mts +146 -0
  112. package/src/identity/sso/custodian-auth-helpers.respec.mts +97 -0
  113. package/src/identity/sso/sso-config-helper.mts +52 -0
  114. package/src/identity/sso/sso-custodian-service.mts +566 -0
  115. package/src/identity/sso/sso-custodian-service.respec.mts +297 -0
  116. package/src/identity/sso/sso-types.mts +80 -0
  117. package/src/index.mts +14 -0
  118. package/src/serve-gib/handlers/api/email/email-outbound.handler.mts +8 -2
  119. package/src/serve-gib/handlers/api/email/email-verification-code.handler.mts +163 -0
  120. package/src/serve-gib/handlers/api/email/email-verify-code-onetime.handler.mts +231 -0
  121. package/src/serve-gib/handlers/api/email/email-verify.respec.mts +183 -0
  122. package/src/serve-gib/handlers/api/identity/keystone-custodian-genesis.handler.mts +233 -0
  123. package/src/serve-gib/handlers/api/identity/keystone-custodian-genesis.respec.mts +93 -0
  124. package/src/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler.mts +210 -0
  125. package/src/serve-gib/handlers/api/identity/keystone-custodian-register-delegate.respec.mts +112 -0
  126. package/src/serve-gib/handlers/api/keystone/keystone-check-email.handler.mts +2 -4
  127. package/src/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +42 -3
  128. package/src/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +46 -23
  129. package/src/serve-gib/handlers/api/keystone/keystone-lookup.handler.mts +11 -15
  130. package/src/serve-gib/handlers/api/keystone/keystone-post.handler.mts +1 -1
  131. package/src/serve-gib/handlers/api/keystone/sso-config.handler.mts +3 -3
  132. package/src/serve-gib/handlers/api/keystone/sso-link.handler.mts +29 -17
  133. package/src/serve-gib/handlers/api/keystone/sso-login.handler.mts +132 -125
  134. package/src/serve-gib/identity-index/identity-index-constants.mts +5 -2
  135. package/src/serve-gib/identity-index/identity-index-helpers.mts +26 -12
  136. package/src/serve-gib/identity-index/identity-index-service.mts +133 -16
  137. package/src/serve-gib/identity-index/identity-index-service.respec.mts +1 -1
  138. package/src/serve-gib/identity-index/identity-index-types.mts +4 -2
  139. package/src/serve-gib/index.mts +12 -0
  140. package/src/serve-gib/path-constants.mts +20 -0
  141. package/tsconfig.json +2 -2
  142. package/tsconfig.test.json +1 -1
  143. package/dist/assumptions.respec.d.mts +0 -2
  144. package/dist/assumptions.respec.d.mts.map +0 -1
  145. package/dist/assumptions.respec.mjs +0 -41
  146. package/dist/assumptions.respec.mjs.map +0 -1
  147. package/dist/email/email-service.respec.d.mts +0 -2
  148. package/dist/email/email-service.respec.d.mts.map +0 -1
  149. package/dist/email/email-service.respec.mjs +0 -115
  150. package/dist/email/email-service.respec.mjs.map +0 -1
  151. package/dist/serve-gib/bootstrap-helper.respec.d.mts +0 -2
  152. package/dist/serve-gib/bootstrap-helper.respec.d.mts.map +0 -1
  153. package/dist/serve-gib/bootstrap-helper.respec.mjs +0 -85
  154. package/dist/serve-gib/bootstrap-helper.respec.mjs.map +0 -1
  155. package/dist/serve-gib/handlers/api/health.respec.d.mts +0 -2
  156. package/dist/serve-gib/handlers/api/health.respec.d.mts.map +0 -1
  157. package/dist/serve-gib/handlers/api/health.respec.mjs +0 -47
  158. package/dist/serve-gib/handlers/api/health.respec.mjs.map +0 -1
  159. package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.d.mts +0 -2
  160. package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.d.mts.map +0 -1
  161. package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.mjs +0 -98
  162. package/dist/serve-gib/handlers/api/keystone/keystone-get.respec.mjs.map +0 -1
  163. package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.d.mts +0 -2
  164. package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.d.mts.map +0 -1
  165. package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.mjs +0 -131
  166. package/dist/serve-gib/handlers/api/keystone/keystone-post.respec.mjs.map +0 -1
  167. package/dist/serve-gib/identity-index/identity-index-service.respec.d.mts +0 -2
  168. package/dist/serve-gib/identity-index/identity-index-service.respec.d.mts.map +0 -1
  169. package/dist/serve-gib/identity-index/identity-index-service.respec.mjs +0 -109
  170. package/dist/serve-gib/identity-index/identity-index-service.respec.mjs.map +0 -1
  171. package/dist/serve-gib/keystone-schema-helpers.respec.d.mts +0 -2
  172. package/dist/serve-gib/keystone-schema-helpers.respec.d.mts.map +0 -1
  173. package/dist/serve-gib/keystone-schema-helpers.respec.mjs +0 -68
  174. package/dist/serve-gib/keystone-schema-helpers.respec.mjs.map +0 -1
  175. package/dist/serve-gib/path-helper.respec.d.mts +0 -2
  176. package/dist/serve-gib/path-helper.respec.d.mts.map +0 -1
  177. package/dist/serve-gib/path-helper.respec.mjs +0 -83
  178. package/dist/serve-gib/path-helper.respec.mjs.map +0 -1
  179. package/dist/serve-gib/serve-gib.respec.d.mts +0 -2
  180. package/dist/serve-gib/serve-gib.respec.d.mts.map +0 -1
  181. package/dist/serve-gib/serve-gib.respec.mjs +0 -85
  182. package/dist/serve-gib/serve-gib.respec.mjs.map +0 -1
  183. package/dist/spec-helper.node.respec.d.mts +0 -13
  184. package/dist/spec-helper.node.respec.d.mts.map +0 -1
  185. package/dist/spec-helper.node.respec.mjs +0 -50
  186. package/dist/spec-helper.node.respec.mjs.map +0 -1
  187. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_createAndInit.node-indexed-filesystem-space-v1.respec.d.mts +0 -2
  188. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_createAndInit.node-indexed-filesystem-space-v1.respec.d.mts.map +0 -1
  189. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_createAndInit.node-indexed-filesystem-space-v1.respec.mjs +0 -52
  190. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_createAndInit.node-indexed-filesystem-space-v1.respec.mjs.map +0 -1
  191. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_persistTransformResult.node-indexed-filesystem-space-v1.respec.d.mts +0 -2
  192. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_persistTransformResult.node-indexed-filesystem-space-v1.respec.d.mts.map +0 -1
  193. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_persistTransformResult.node-indexed-filesystem-space-v1.respec.mjs +0 -53
  194. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_persistTransformResult.node-indexed-filesystem-space-v1.respec.mjs.map +0 -1
  195. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_putGetDelete.node-indexed-filesystem-space-v1.respec.d.mts +0 -2
  196. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_putGetDelete.node-indexed-filesystem-space-v1.respec.d.mts.map +0 -1
  197. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_putGetDelete.node-indexed-filesystem-space-v1.respec.mjs +0 -59
  198. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_putGetDelete.node-indexed-filesystem-space-v1.respec.mjs.map +0 -1
  199. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-indexed-filesystem-space-v1.respec.d.mts +0 -2
  200. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-indexed-filesystem-space-v1.respec.d.mts.map +0 -1
  201. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-indexed-filesystem-space-v1.respec.mjs +0 -55
  202. package/dist/witness/space/node-filesystem-space/node-indexed-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-indexed-filesystem-space-v1.respec.mjs.map +0 -1
  203. package/dist/witness/space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.d.mts +0 -2
  204. package/dist/witness/space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.d.mts.map +0 -1
  205. package/dist/witness/space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.mjs +0 -67
  206. package/dist/witness/space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.mjs.map +0 -1
  207. package/dist/witness/space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.d.mts +0 -2
  208. package/dist/witness/space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.d.mts.map +0 -1
  209. package/dist/witness/space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.mjs +0 -67
  210. package/dist/witness/space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.mjs.map +0 -1
  211. package/dist/witness/space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.d.mts +0 -2
  212. package/dist/witness/space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.d.mts.map +0 -1
  213. package/dist/witness/space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.mjs +0 -68
  214. package/dist/witness/space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.mjs.map +0 -1
  215. package/dist/witness/space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.d.mts +0 -2
  216. package/dist/witness/space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.d.mts.map +0 -1
  217. package/dist/witness/space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.mjs +0 -69
  218. package/dist/witness/space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.mjs.map +0 -1
@@ -0,0 +1,93 @@
1
+ import { existsSync, rmSync } from 'node:fs';
2
+
3
+ import {
4
+ respecfully,
5
+ iReckon,
6
+ ifWe
7
+ } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
8
+
9
+ import { KeystoneCustodianGenesisHandler } from './keystone-custodian-genesis.handler.mjs';
10
+ import { verifyCustodianRequirements } from '../../../../identity/sso/custodian-auth-helpers.mjs';
11
+ import { loadSsoServerConfig } from '../../../../identity/sso/sso-config-helper.mjs';
12
+
13
+ const sir = `[${import.meta.url}]`;
14
+
15
+ await respecfully(sir, 'KeystoneCustodianGenesisHandler', async () => {
16
+ const testDataDir = './test-data-custodian-genesis';
17
+
18
+ if (existsSync(testDataDir)) {
19
+ rmSync(testDataDir, { recursive: true, force: true });
20
+ }
21
+
22
+ const origDataDir = process.env.DATA_DIR;
23
+ process.env.DATA_DIR = testDataDir;
24
+
25
+ const handler = new KeystoneCustodianGenesisHandler();
26
+
27
+ await ifWe(sir, 'reject request with 400 if email is missing or invalid', async () => {
28
+ const reqCtx: any = {
29
+ method: 'POST',
30
+ body: JSON.stringify({ email: 'invalid-email' }),
31
+ dataDir: testDataDir
32
+ };
33
+
34
+ const res = await handler.handleRoute(reqCtx);
35
+ iReckon(sir, res?.status).isGonnaBe(400);
36
+ });
37
+
38
+ await ifWe(sir, 'create new custodian primary identity and return custodianReqs for new email', async () => {
39
+ const reqCtx: any = {
40
+ method: 'POST',
41
+ body: JSON.stringify({ email: 'alice.custodian@example.com', username: 'alice' }),
42
+ dataDir: testDataDir
43
+ };
44
+
45
+ const res = await handler.handleRoute(reqCtx);
46
+ iReckon(sir, res?.status).isGonnaBe(201);
47
+
48
+ const body = JSON.parse(res?.body || '{}');
49
+ iReckon(sir, body.success).isGonnaBeTrue();
50
+ iReckon(sir, body.isNewIdentity).isGonnaBeTrue();
51
+ iReckon(sir, body.primaryTjpAddr).includes('keystone^');
52
+ iReckon(sir, body.primaryKeystone).not.isGonnaBeUndefined();
53
+ iReckon(sir, body.custodianReqs).not.isGonnaBeUndefined();
54
+
55
+ // Verify returned custodianReqs authCode signature
56
+ const serverSecret = loadSsoServerConfig().ssoServerKdfSecret;
57
+ const verifyRes = await verifyCustodianRequirements({
58
+ custodianReqs: body.custodianReqs,
59
+ serverSecret
60
+ });
61
+ iReckon(sir, verifyRes.valid).isGonnaBeTrue();
62
+ });
63
+
64
+ await ifWe(sir, 'retrieve existing primary identity and return fresh custodianReqs for existing email', async () => {
65
+ const reqCtx: any = {
66
+ method: 'POST',
67
+ body: JSON.stringify({ email: 'alice.custodian@example.com' }),
68
+ dataDir: testDataDir
69
+ };
70
+
71
+ const res = await handler.handleRoute(reqCtx);
72
+ iReckon(sir, res?.status).isGonnaBe(200);
73
+
74
+ const body = JSON.parse(res?.body || '{}');
75
+ iReckon(sir, body.success).isGonnaBeTrue();
76
+ iReckon(sir, body.isNewIdentity).isGonnaBeFalse();
77
+ iReckon(sir, body.primaryTjpAddr).includes('keystone^');
78
+ iReckon(sir, body.custodianReqs).not.isGonnaBeUndefined();
79
+
80
+ const serverSecret = loadSsoServerConfig().ssoServerKdfSecret;
81
+ const verifyRes = await verifyCustodianRequirements({
82
+ custodianReqs: body.custodianReqs,
83
+ serverSecret
84
+ });
85
+ iReckon(sir, verifyRes.valid).isGonnaBeTrue();
86
+ });
87
+
88
+ // Cleanup
89
+ process.env.DATA_DIR = origDataDir;
90
+ if (existsSync(testDataDir)) {
91
+ rmSync(testDataDir, { recursive: true, force: true });
92
+ }
93
+ });
@@ -0,0 +1,210 @@
1
+ /**
2
+ * @module serve-gib/handlers/api/identity/keystone-custodian-register-delegate.handler
3
+ *
4
+ * POST /api/identity/custodian/register-delegate
5
+ *
6
+ * Validates a client delegate keystone containing single-turn `custodianReqs`,
7
+ * enforces single-use nonce reservation, verifies cryptographic `authCode`,
8
+ * evolves the server-held primary identity keystone to link the delegate,
9
+ * and returns the updated `evolvedPrimaryKeystone`.
10
+ */
11
+
12
+ import * as fs from 'fs/promises';
13
+ import * as path from 'path';
14
+
15
+ import { extractErrorMsg, hash } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
16
+ import { validateIbGibIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
17
+ import { getDependencyGraph } from '@ibgib/core-gib/dist/common/other/graph-helper.mjs';
18
+ import { FlatIbGibGraph } from '@ibgib/core-gib/dist/common/other/graph-types.mjs';
19
+ import { KeystoneService_V1 } from '@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs';
20
+ import { KeystoneIbGib_V1 } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
21
+ import { KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
22
+
23
+ import { GLOBAL_LOG_A_LOT } from '../../../constants.mjs';
24
+ import { ServeGibHandlerBase } from '../../handler-base.mjs';
25
+ import { RequestContext, ResponseResult, ServeGibHttpMethod } from '../../../types.mjs';
26
+ import { API_PATH_REGEXES } from '../../../path-constants.mjs';
27
+ import { bootstrapDomainMetaspace } from '../../../bootstrap-helper.mjs';
28
+ import { loadSsoServerConfig } from '../../../../identity/sso/sso-config-helper.mjs';
29
+ import { getAuthCustodianIndex, verifyCustodianRequirements } from '../../../../identity/sso/custodian-auth-helpers.mjs';
30
+ import { CustodianRequirements } from '../../../../identity/sso/sso-types.mjs';
31
+
32
+ const logalot = GLOBAL_LOG_A_LOT || true;
33
+
34
+ export interface CustodianRegisterDelegateRequestBody {
35
+ delegateKeystone: any;
36
+ }
37
+
38
+ export interface CustodianRegisterDelegateResponseBody {
39
+ success: boolean;
40
+ primaryTjpAddr: string;
41
+ evolvedPrimaryKeystone: any;
42
+ primaryGraph?: FlatIbGibGraph;
43
+ message?: string;
44
+ }
45
+
46
+ /**
47
+ * POST /api/identity/custodian/register-delegate
48
+ */
49
+ export class KeystoneCustodianRegisterDelegateHandler extends ServeGibHandlerBase<undefined, undefined> {
50
+ protected override lc: string = `[${KeystoneCustodianRegisterDelegateHandler.name}]`;
51
+ protected override method: ServeGibHttpMethod = 'POST';
52
+ protected override regex = API_PATH_REGEXES.KEYSTONE_CUSTODIAN_REGISTER_DELEGATE;
53
+
54
+ protected async handleRouteImpl(
55
+ reqCtx: RequestContext<undefined, undefined>
56
+ ): Promise<ResponseResult | undefined> {
57
+ const lc = `${this.lc}[${this.handleRouteImpl.name}]`;
58
+ let authFilePathToClean: string | undefined;
59
+
60
+ try {
61
+ if (logalot) { console.log(`${lc} starting... (I: a23456789abcdef0123456789c0001)`); }
62
+
63
+ if (reqCtx.method !== 'POST') {
64
+ return this.error(405, 'Method Not Allowed');
65
+ }
66
+
67
+ if (!reqCtx.body) {
68
+ return this.error(400, 'Request body is empty (E: b23456789abcdef0123456789c0002)');
69
+ }
70
+
71
+ let body: CustodianRegisterDelegateRequestBody;
72
+ try {
73
+ body = JSON.parse(reqCtx.body);
74
+ } catch {
75
+ return this.error(400, 'Request body must be valid JSON (E: c23456789abcdef0123456789c0003)');
76
+ }
77
+
78
+ const { delegateKeystone } = body;
79
+ if (!delegateKeystone) {
80
+ return this.error(400, 'Request body must contain delegateKeystone (E: d23456789abcdef0123456789c0004)');
81
+ }
82
+
83
+ // 1. Intrinsic hash validation on delegate keystone
84
+ const intrinsicErrors = await validateIbGibIntrinsically({ ibGib: delegateKeystone });
85
+ if (intrinsicErrors && intrinsicErrors.length > 0) {
86
+ return this.error(422, 'Intrinsic validation failed for delegateKeystone', { errors: intrinsicErrors });
87
+ }
88
+
89
+ // 2. Structural keystone validation
90
+ const keystoneService = new KeystoneService_V1();
91
+ const genesisErrors = await keystoneService.validateGenesisKeystone({ keystoneIbGib: delegateKeystone });
92
+ if (genesisErrors && genesisErrors.length > 0) {
93
+ return this.error(422, 'Delegate keystone structure validation failed', { errors: genesisErrors });
94
+ }
95
+
96
+ // 3. Extract and check custodianReqs from frameDetails
97
+ const frameDetails = delegateKeystone.data?.frameDetails || delegateKeystone.data;
98
+ const custodianReqs: CustodianRequirements | undefined = frameDetails?.custodianReqs;
99
+ if (!custodianReqs) {
100
+ return this.error(400, 'Missing custodianReqs payload in delegate keystone frameDetails (E: e23456789abcdef0123456789c0005)');
101
+ }
102
+
103
+ const delegatePrimaryTjpAddr: string | undefined = frameDetails?.primaryTjpAddr;
104
+ if (delegatePrimaryTjpAddr && delegatePrimaryTjpAddr !== custodianReqs.primaryTjpAddr) {
105
+ return this.error(400, 'Primary TJP address mismatch between delegate frameDetails and custodianReqs (E: f23456789abcdef0123456789c0006)');
106
+ }
107
+
108
+ // 4. Verify cryptographic signature & expiration of custodianReqs
109
+ const ssoConfig = loadSsoServerConfig();
110
+ const serverSecret = ssoConfig.ssoServerKdfSecret;
111
+ const verifyRes = await verifyCustodianRequirements({ custodianReqs, serverSecret });
112
+ if (!verifyRes.valid) {
113
+ return this.error(401, verifyRes.error || 'Invalid custodian requirements payload');
114
+ }
115
+
116
+ // 5. Atomic Single-Turn Nonce Reservation Check (`{ flag: 'wx' }`)
117
+ const authCustodianIndex = await getAuthCustodianIndex({ custodianReqs });
118
+ const dataDir = reqCtx.dataDir || process.env.DATA_DIR || 'data';
119
+ const authDir = path.join(dataDir, 'identity_index', 'auth_custodian');
120
+ await fs.mkdir(authDir, { recursive: true });
121
+
122
+ authFilePathToClean = path.join(authDir, `${authCustodianIndex}.json`);
123
+ try {
124
+ await fs.writeFile(authFilePathToClean, JSON.stringify({
125
+ usedAt: Date.now(),
126
+ primaryTjpAddr: custodianReqs.primaryTjpAddr,
127
+ nonce: custodianReqs.nonce
128
+ }), { flag: 'wx' });
129
+ } catch (fileErr: any) {
130
+ if (fileErr.code === 'EEXIST') {
131
+ return this.error(409, 'Custodian requirements payload has already been used. Nonce single-turn reservation violation.');
132
+ }
133
+ throw fileErr;
134
+ }
135
+
136
+ // 6. Evolve Server-Held Primary Identity Keystone
137
+ const metaspace = await bootstrapDomainMetaspace(custodianReqs.primaryTjpAddr, reqCtx.dataDir);
138
+ const space = await metaspace.getLocalUserSpace({ lock: false });
139
+ if (!space) {
140
+ return this.error(500, `Could not load domain space for primary identity ${custodianReqs.primaryTjpAddr}`);
141
+ }
142
+
143
+ const latestPrimaryAddr = await metaspace.getLatestAddr({ addr: custodianReqs.primaryTjpAddr, space }) ?? custodianReqs.primaryTjpAddr;
144
+ const getRes = await metaspace.get({ addrs: [latestPrimaryAddr], space });
145
+ const parentKeystone = getRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
146
+
147
+ if (!parentKeystone) {
148
+ return this.error(404, `Primary identity keystone not found for address ${custodianReqs.primaryTjpAddr}`);
149
+ }
150
+
151
+ // Derive server's primary custodian secret
152
+ const primaryEmail: string = parentKeystone.data?.emails?.[0]
153
+ || parentKeystone.data?.frameDetails?.emails?.[0]
154
+ || frameDetails?.email
155
+ || '';
156
+
157
+ if (!primaryEmail) {
158
+ return this.error(500, `Could not determine primary email for identity ${custodianReqs.primaryTjpAddr}`);
159
+ }
160
+
161
+ const primarySecret = await hash({ s: `${serverSecret}_custodian_${primaryEmail}` });
162
+
163
+ // Execute primary keystone evolution linking the new delegate
164
+ const evolvedPrimaryKeystone = await keystoneService.registerDelegate({
165
+ parentKeystone,
166
+ delegateKeystone,
167
+ masterSecret: primarySecret,
168
+ metaspace,
169
+ space,
170
+ allowedVerbs: custodianReqs.allowedVerbs || [KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT],
171
+ deviceName: frameDetails?.deviceTag || 'client-delegate'
172
+ });
173
+
174
+ // Persist delegate keystone in domain space
175
+ await metaspace.put({ ibGibs: [delegateKeystone], space });
176
+ await metaspace.registerNewIbGib({ ibGib: delegateKeystone, space });
177
+
178
+ // Extract complete primary keystone dependency graph
179
+ const primaryGraph = await getDependencyGraph({
180
+ ibGib: evolvedPrimaryKeystone,
181
+ space,
182
+ live: false
183
+ });
184
+
185
+ const responseBody: CustodianRegisterDelegateResponseBody = {
186
+ success: true,
187
+ primaryTjpAddr: custodianReqs.primaryTjpAddr,
188
+ evolvedPrimaryKeystone,
189
+ primaryGraph,
190
+ message: 'Custodian delegate keystone registered and primary identity evolved successfully.'
191
+ };
192
+
193
+ return this.ok(responseBody, 200);
194
+ } catch (error) {
195
+ const emsg = extractErrorMsg(error);
196
+ console.error(`${lc} ${emsg}`);
197
+ return this.error(500, emsg);
198
+ } finally {
199
+ // Clean up single-turn reservation file after processing
200
+ if (authFilePathToClean) {
201
+ try {
202
+ await fs.unlink(authFilePathToClean);
203
+ } catch {
204
+ // Ignore unlinking errors if already cleaned up
205
+ }
206
+ }
207
+ if (logalot) { console.log(`${lc} complete.`); }
208
+ }
209
+ }
210
+ }
@@ -0,0 +1,112 @@
1
+ import { existsSync, rmSync } from 'node:fs';
2
+
3
+ import {
4
+ respecfully,
5
+ iReckon,
6
+ ifWe
7
+ } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
8
+
9
+ import { KeystoneCustodianGenesisHandler } from './keystone-custodian-genesis.handler.mjs';
10
+ import { KeystoneCustodianRegisterDelegateHandler } from './keystone-custodian-register-delegate.handler.mjs';
11
+ import { KeystoneProfileBuilder } from '@ibgib/core-gib/dist/keystone/policy/keystone-profile-builder.mjs';
12
+ import { KeystoneService_V1 } from '@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs';
13
+ import { deriveLocalCustodianDelegateSecret } from '@ibgib/web-gib/dist/identity/custodian-delegate-helper.mjs';
14
+ import { bootstrapDomainMetaspace } from '../../../bootstrap-helper.mjs';
15
+
16
+ const sir = `[${import.meta.url}]`;
17
+
18
+ await respecfully(sir, 'KeystoneCustodianRegisterDelegateHandler', async () => {
19
+ const testDataDir = './test-data-custodian-register';
20
+
21
+ if (existsSync(testDataDir)) {
22
+ rmSync(testDataDir, { recursive: true, force: true });
23
+ }
24
+
25
+ const origDataDir = process.env.DATA_DIR;
26
+ process.env.DATA_DIR = testDataDir;
27
+
28
+ const genesisHandler = new KeystoneCustodianGenesisHandler();
29
+ const registerHandler = new KeystoneCustodianRegisterDelegateHandler();
30
+
31
+ await ifWe(sir, 'reject request with 400 if delegateKeystone or custodianReqs is missing', async () => {
32
+ const reqCtx: any = {
33
+ method: 'POST',
34
+ body: JSON.stringify({}),
35
+ dataDir: testDataDir
36
+ };
37
+
38
+ const res = await registerHandler.handleRoute(reqCtx);
39
+ iReckon(sir, res?.status).isGonnaBe(400);
40
+ });
41
+
42
+ await ifWe(sir, 'successfully register delegate keystone and evolve primary identity keystone', async () => {
43
+ // Step 1: Genesis via custodian genesis handler
44
+ const genesisReqCtx: any = {
45
+ method: 'POST',
46
+ body: JSON.stringify({ email: 'bob.custodian@example.com', username: 'bob' }),
47
+ dataDir: testDataDir
48
+ };
49
+
50
+ const genesisRes = await genesisHandler.handleRoute(genesisReqCtx);
51
+ iReckon(sir, genesisRes?.status).isGonnaBe(201);
52
+
53
+ const genesisBody = JSON.parse(genesisRes?.body || '{}');
54
+ const primaryTjpAddr = genesisBody.primaryTjpAddr;
55
+ const custodianReqs = genesisBody.custodianReqs;
56
+
57
+ iReckon(sir, primaryTjpAddr).not.isGonnaBeUndefined();
58
+ iReckon(sir, custodianReqs).not.isGonnaBeUndefined();
59
+
60
+ // Step 2: Build client delegate keystone containing custodianReqs
61
+ const delegateSecret = await deriveLocalCustodianDelegateSecret({ primaryTjpAddr });
62
+ const delegateBuilder = KeystoneProfileBuilder.buildKeystone('sync')
63
+ .withUsername('bob-sync-phone')
64
+ .withDescription('Sync Delegate for bob on phone')
65
+ .withDetails({
66
+ client: 'space-gib-web',
67
+ role: 'sync-delegate',
68
+ deviceTag: 'phone',
69
+ primaryTjpAddr,
70
+ custodianReqs,
71
+ });
72
+
73
+ const configs = await delegateBuilder.compileConfigs();
74
+ const metaspace = await bootstrapDomainMetaspace(primaryTjpAddr, testDataDir);
75
+ const space = await metaspace.getLocalUserSpace({ lock: false });
76
+
77
+ const keystoneService = new KeystoneService_V1();
78
+ const delegateKeystone = await keystoneService.genesis({
79
+ masterSecret: delegateSecret,
80
+ configs,
81
+ metaspace,
82
+ space: space!,
83
+ frameDetails: delegateBuilder.getFrameDetails(),
84
+ isPrimary: false
85
+ });
86
+
87
+ // Step 3: Register delegate keystone with server
88
+ const registerReqCtx: any = {
89
+ method: 'POST',
90
+ body: JSON.stringify({ delegateKeystone }),
91
+ dataDir: testDataDir
92
+ };
93
+
94
+ const registerRes = await registerHandler.handleRoute(registerReqCtx);
95
+ iReckon(sir, registerRes?.status).isGonnaBe(200);
96
+
97
+ const registerBody = JSON.parse(registerRes?.body || '{}');
98
+ iReckon(sir, registerBody.success).isGonnaBeTrue();
99
+ iReckon(sir, registerBody.primaryTjpAddr).isGonnaBe(primaryTjpAddr);
100
+ iReckon(sir, registerBody.evolvedPrimaryKeystone).not.isGonnaBeUndefined();
101
+
102
+ // Verify primary keystone frame n=1 has been evolved
103
+ const evolvedPrimary = registerBody.evolvedPrimaryKeystone;
104
+ iReckon(sir, evolvedPrimary.data?.n).isGonnaBe(1);
105
+ });
106
+
107
+ // Cleanup
108
+ process.env.DATA_DIR = origDataDir;
109
+ if (existsSync(testDataDir)) {
110
+ rmSync(testDataDir, { recursive: true, force: true });
111
+ }
112
+ });
@@ -6,10 +6,8 @@
6
6
  * Checks whether an email address is already registered in the server identity index.
7
7
  */
8
8
 
9
- import * as path from 'node:path';
10
9
  import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
11
- import { IdentityIndexService } from '../../../identity-index/identity-index-service.mjs';
12
- import { getIdentityEmailHash } from '../../../identity-index/identity-index-helpers.mjs';
10
+ import { getIdentityIndexService } from '../../../identity-index/identity-index-service.mjs';
13
11
  import { ServeGibHandlerBase } from '../../handler-base.mjs';
14
12
  import { RequestContext, ResponseResult, ServeGibHttpMethod } from '../../../types.mjs';
15
13
  import { API_PATH_REGEXES } from '../../../path-constants.mjs';
@@ -36,7 +34,7 @@ export class KeystoneCheckEmailHandler extends ServeGibHandlerBase<undefined, {
36
34
  return this.error(400, 'Query param email is required');
37
35
  }
38
36
 
39
- const indexService = new IdentityIndexService({ baseDir: path.join(reqCtx.dataDir, 'index') });
37
+ const indexService = getIdentityIndexService();
40
38
  const record = await indexService.getEmailIndex({ email });
41
39
 
42
40
  if (record) {
@@ -15,15 +15,17 @@ import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
15
15
  import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
16
16
  import { validateAndRegisterEvolveKeystone } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs';
17
17
  import { KeystoneIbGib_V1 } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
18
- import { loadSsoServerConfig } from '@ibgib/web-gib/dist/identity/sso/sso-config-helper.mjs';
19
- import { SsoCustodianService } from '@ibgib/web-gib/dist/identity/sso/sso-custodian-service.mjs';
18
+ import { loadSsoServerConfig } from '../../../../identity/sso/sso-config-helper.mjs';
19
+ import { SsoCustodianService } from '../../../../identity/sso/sso-custodian-service.mjs';
20
20
 
21
21
  import { GLOBAL_LOG_A_LOT } from '../../../constants.mjs';
22
+ import { getIdentityIndexService } from '../../../identity-index/identity-index-service.mjs';
23
+ import { savePendingStagingKeystone, dispatchVerificationCode } from '../../../../email/verify/email-verify-helpers.mjs';
22
24
  import { ServeGibHandlerWithMetaspaceBase } from '../../handler-base.mjs';
23
25
  import { API_PATH_REGEXES } from '../../../path-constants.mjs';
24
26
  import { ParamsWithDomain, RequestContext, ResponseResult } from '../../../types.mjs';
25
27
 
26
- const logalot = GLOBAL_LOG_A_LOT || true;
28
+ const logalot = GLOBAL_LOG_A_LOT;
27
29
 
28
30
  interface EvolveParams extends ParamsWithDomain {
29
31
  domainAddr: string;
@@ -97,6 +99,43 @@ export class KeystoneEvolveHandler extends ServeGibHandlerWithMetaspaceBase<Evol
97
99
  return this.error(500, 'No local user space found (E: a1144e5d6cd9641fb82d6b359f131126)');
98
100
  }
99
101
 
102
+ // Check for any new unverified email addresses attached to the evolved keystone
103
+ const incomingEmails: string[] = keystoneIbGib.data?.emails ?? keystoneIbGib.data?.frameDetails?.emails ?? [];
104
+ if (incomingEmails.length > 0) {
105
+ const unverifiedEmails: string[] = [];
106
+ for (const email of incomingEmails) {
107
+ const record = await getIdentityIndexService().lookupIdentityByEmail({ email });
108
+ if (!record || record.primaryTjpAddr !== domainAddr) {
109
+ unverifiedEmails.push(email);
110
+ }
111
+ }
112
+
113
+ if (unverifiedEmails.length > 0) {
114
+ console.log(`${lc} Evolved keystone contains unverified email(s): ${unverifiedEmails.join(', ')}. Staging evolution in quarantine area.`);
115
+ const saveRes = await savePendingStagingKeystone({
116
+ keystone: keystoneIbGib,
117
+ emails: unverifiedEmails,
118
+ primaryTjpAddr: domainAddr,
119
+ dataDir: reqCtx.dataDir
120
+ });
121
+
122
+ // Immediately dispatch 6-digit verification code email for first unverified email
123
+ const primaryEmail = unverifiedEmails[0];
124
+ await dispatchVerificationCode({
125
+ email: primaryEmail,
126
+ primaryTjpAddr: domainAddr,
127
+ pendingDir: saveRes.pendingDir,
128
+ });
129
+
130
+ return this.ok({
131
+ success: true,
132
+ pending: true,
133
+ addr: domainAddr,
134
+ message: 'Evolved keystone quarantined in pending staging area. 6-digit verification code sent to your email.'
135
+ }, 202);
136
+ }
137
+ }
138
+
100
139
  // Delegate all intrinsic checks, transition rules, pool restrictions, and registration/persistence to core-gib helper
101
140
  await validateAndRegisterEvolveKeystone({
102
141
  domainAddr,
@@ -33,12 +33,11 @@
33
33
  import { existsSync } from 'node:fs';
34
34
  import * as path from 'node:path';
35
35
 
36
- import { extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
36
+ import { extractErrorMsg, getUUID, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
37
37
  import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
38
38
  import { validateIbGibIntrinsically } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
39
39
  import { getGibInfo } from '@ibgib/ts-gib/dist/V1/transforms/transform-helper.mjs';
40
40
  import { KeystoneService_V1 } from '@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs';
41
- import { IdentityIndexService } from '../../../identity-index/identity-index-service.mjs';
42
41
 
43
42
  import { GLOBAL_LOG_A_LOT } from '../../../constants.mjs';
44
43
  import { ServeGibHandlerBase } from '../../handler-base.mjs';
@@ -46,12 +45,11 @@ import { RequestContext, ResponseResult, ServeGibHttpMethod } from '../../../typ
46
45
  import { API_PATH_REGEXES } from '../../../path-constants.mjs';
47
46
  import { getDomainRootPath } from '../../../bootstrap-helper.mjs';
48
47
  import { bootstrapDomainMetaspace } from '../../../bootstrap-helper.mjs';
48
+ import { savePendingStagingKeystone, dispatchVerificationCode } from '../../../../email/verify/email-verify-helpers.mjs';
49
49
 
50
50
  const logalot = GLOBAL_LOG_A_LOT || true;
51
51
 
52
52
 
53
-
54
-
55
53
  /**
56
54
  * POST /api/keystone/genesis
57
55
  *
@@ -66,8 +64,11 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
66
64
  reqCtx: RequestContext<undefined, undefined>
67
65
  ): Promise<ResponseResult | undefined> {
68
66
  const lc = `${this.lc}[${this.handleRouteImpl.name}]`;
67
+ const timestampUUID = `${lc}${[(await getUUID()).substring(0, 8)]}`;
69
68
  try {
70
- if (logalot) { console.log(`${lc} starting... (I: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c526)`); }
69
+ if (logalot) { console.log(`${lc} starting... (I: 866db89dfcb947c7353a1a8849bb2826)`); }
70
+
71
+ if (logalot) { console.time(timestampUUID); }
71
72
 
72
73
  // ----------------------------------------------------------------
73
74
  // 1. Parse body — expect { keystoneIbGib }
@@ -79,6 +80,8 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
79
80
  return this.error(400, 'Request body must be valid JSON (E: dd8618857568f8fc5e9c62b8ed135826)');
80
81
  }
81
82
 
83
+ if (logalot) { console.timeLog(timestampUUID, 'parse complete') }
84
+
82
85
  const { keystoneIbGib } = parsed;
83
86
  if (!keystoneIbGib) {
84
87
  return this.error(400, 'Body must contain keystoneIbGib (E: 9c8c5d979d7842cd716ba64f69349c26)');
@@ -92,6 +95,8 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
92
95
  return this.error(422, 'Intrinsic keystone validation failed (E: 76d388e198f23a87dd6596f59c36e826)', { errors: intrinsicErrors });
93
96
  }
94
97
 
98
+ if (logalot) { console.timeLog(timestampUUID, 'intrinsic validation complete') }
99
+
95
100
  // ----------------------------------------------------------------
96
101
  // 3. Structural keystone + genesis-frame checks
97
102
  // ----------------------------------------------------------------
@@ -102,11 +107,14 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
102
107
  if (keystoneErrors && keystoneErrors.length > 0) {
103
108
  return this.error(422, `Keystone genesis validation failed. Errors: ${keystoneErrors} (E: 8384285be7a8ab0d41cfd1df2e2aa826)`, { errors: keystoneErrors });
104
109
  }
110
+ if (logalot) { console.timeLog(timestampUUID, 'keystone validation complete') }
105
111
 
106
112
  // ----------------------------------------------------------------
107
113
  // 4. Domain uniqueness & Email Index Registration
108
114
  // ----------------------------------------------------------------
109
115
  const addr = getIbGibAddr({ ibGib: keystoneIbGib });
116
+ const primaryTjpAddr = addr;
117
+ if (logalot) { console.log(`${lc} primaryTjpAddr: ${primaryTjpAddr} (I: c09d68025348261ac8ccec8ebe59d626)`); }
110
118
  const { tjpGib, punctiliarHash } = getGibInfo({ ibGibAddr: addr });
111
119
  const domainGib = tjpGib || punctiliarHash;
112
120
  if (!domainGib) {
@@ -120,31 +128,42 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
120
128
 
121
129
  // Extract email addresses attached to primary identity keystone (if present)
122
130
  const emails: string[] = keystoneIbGib.data?.emails ?? keystoneIbGib.data?.frameDetails?.emails ?? [];
123
- const indexService = new IdentityIndexService({ baseDir: path.join(reqCtx.dataDir, 'index') });
124
- const registeredHashes: string[] = [];
125
131
 
126
132
  if (emails.length > 0) {
127
- try {
128
- for (const email of emails) {
129
- const { emailHash } = await indexService.registerEmailIndex({ email, tjpAddr: addr });
130
- registeredHashes.push(emailHash);
131
- }
132
- } catch (err: any) {
133
- if (err.message.includes('duplicate_identity_409')) {
134
- // Roll back any hashes registered in this transaction before throwing
135
- for (const emailHash of registeredHashes) {
136
- try { await indexService.getEmailIndexByHash({ emailHash }); } catch { /* ignore */ }
137
- }
138
- return this.error(409, `Email/Identity already registered (E: duplicate_identity_409)`);
139
- }
140
- throw err;
141
- }
133
+ // Staging Quarantine: Save keystone & email records to email/pending/
134
+ console.log(`${lc} Staging genesis keystone with unverified email(s): ${emails.join(', ')} (I: c123456789abcdef0123456789abcdef)`);
135
+ const saveRes = await savePendingStagingKeystone({
136
+ keystone: keystoneIbGib,
137
+ emails,
138
+ primaryTjpAddr: addr,
139
+ dataDir: reqCtx.dataDir
140
+ });
141
+
142
+ // Immediately dispatch 6-digit verification code email for primary email
143
+ const primaryEmail = emails[0];
144
+ await dispatchVerificationCode({
145
+ email: primaryEmail,
146
+ primaryTjpAddr: addr,
147
+ pendingDir: saveRes.pendingDir,
148
+ });
149
+
150
+ if (logalot) { console.timeLog(timestampUUID, `quarantine staging complete & code dispatched to ${primaryEmail}`); }
151
+
152
+ // EARLY RETURN 202 Accepted — do NOT bootstrap domain space or write to email/hashes/ until email is verified!
153
+ return this.ok({
154
+ success: true,
155
+ pending: true,
156
+ addr,
157
+ message: 'Genesis keystone quarantined in pending staging area. 6-digit verification code sent to your email.'
158
+ }, 202);
142
159
  }
160
+ if (logalot) { console.timeLog(timestampUUID, 'emails complete') }
143
161
 
144
162
  // ----------------------------------------------------------------
145
163
  // 5. Bootstrap the new domain metaspace (creates folders)
146
164
  // ----------------------------------------------------------------
147
165
  const metaspace = await bootstrapDomainMetaspace(addr, reqCtx.dataDir);
166
+ if (logalot) { console.timeLog(timestampUUID, 'metaspace bootstrap complete') }
148
167
 
149
168
  // ----------------------------------------------------------------
150
169
  // 6. Persist the genesis keystone into the new domain space
@@ -158,6 +177,7 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
158
177
  await metaspace.registerNewIbGib({ ibGib: keystoneIbGib, space });
159
178
 
160
179
  console.log(`${lc} domain created and keystone persisted: ${addr}`);
180
+ if (logalot) { console.timeLog(timestampUUID, 'domain created complete') }
161
181
  // if (logalot) { console.log(`${lc} keystoneIbGib: ${pretty(keystoneIbGib)} (I: d598c6ff7a48997d585b84c19c464826)`); }
162
182
  return this.ok({ success: true, addr }, 201);
163
183
  } catch (error) {
@@ -165,7 +185,10 @@ export class KeystoneGenesisHandler extends ServeGibHandlerBase<undefined, undef
165
185
  console.error(`${lc} ${emsg}`);
166
186
  return this.error(500, emsg);
167
187
  } finally {
168
- if (logalot) { console.log(`${lc} complete.`); }
188
+ if (logalot) {
189
+ console.log(`${lc} complete.`);
190
+ console.timeEnd(timestampUUID);
191
+ }
169
192
  }
170
193
  }
171
194
  }