@ibgib/core-gib 0.1.54 → 0.1.57

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 (190) hide show
  1. package/CHANGELOG.md +11 -3
  2. package/README.md +12 -15
  3. package/dist/keystone/aggregate-details.respec.d.mts +2 -0
  4. package/dist/keystone/aggregate-details.respec.d.mts.map +1 -0
  5. package/dist/keystone/aggregate-details.respec.mjs +118 -0
  6. package/dist/keystone/aggregate-details.respec.mjs.map +1 -0
  7. package/dist/keystone/keystone-constants.d.mts +5 -0
  8. package/dist/keystone/keystone-constants.d.mts.map +1 -1
  9. package/dist/keystone/keystone-constants.mjs +6 -1
  10. package/dist/keystone/keystone-constants.mjs.map +1 -1
  11. package/dist/keystone/keystone-helpers.d.mts +7 -1
  12. package/dist/keystone/keystone-helpers.d.mts.map +1 -1
  13. package/dist/keystone/keystone-helpers.mjs +5 -2
  14. package/dist/keystone/keystone-helpers.mjs.map +1 -1
  15. package/dist/keystone/keystone-service-v1.d.mts +51 -0
  16. package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
  17. package/dist/keystone/keystone-service-v1.mjs +176 -9
  18. package/dist/keystone/keystone-service-v1.mjs.map +1 -1
  19. package/dist/keystone/keystone-service-v1.respec.mjs +40 -98
  20. package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
  21. package/dist/keystone/keystone-types.d.mts +5 -0
  22. package/dist/keystone/keystone-types.d.mts.map +1 -1
  23. package/dist/sync/graft-info/graft-info-helpers.respec.mjs +8 -8
  24. package/dist/sync/graft-info/graft-info-helpers.respec.mjs.map +1 -1
  25. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs +26 -30
  26. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs.map +1 -1
  27. package/dist/sync/sync-conflict-basic-divergence.respec.mjs +7 -8
  28. package/dist/sync/sync-conflict-basic-divergence.respec.mjs.map +1 -1
  29. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs +10 -11
  30. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs.map +1 -1
  31. package/dist/sync/sync-conflict-text-merge.respec.mjs +30 -33
  32. package/dist/sync/sync-conflict-text-merge.respec.mjs.map +1 -1
  33. package/dist/sync/sync-constants.d.mts +1 -56
  34. package/dist/sync/sync-constants.d.mts.map +1 -1
  35. package/dist/sync/sync-constants.mjs +2 -59
  36. package/dist/sync/sync-constants.mjs.map +1 -1
  37. package/dist/sync/sync-helpers.d.mts +0 -1
  38. package/dist/sync/sync-helpers.d.mts.map +1 -1
  39. package/dist/sync/sync-helpers.mjs +1 -6
  40. package/dist/sync/sync-helpers.mjs.map +1 -1
  41. package/dist/sync/sync-innerspace-constants.respec.mjs +5 -9
  42. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
  43. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +6 -7
  44. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  45. package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs +22 -22
  46. package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs.map +1 -1
  47. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +8 -9
  48. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
  49. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +6 -7
  50. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  51. package/dist/sync/sync-innerspace-partial-update.respec.mjs +7 -8
  52. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
  53. package/dist/sync/sync-innerspace.respec.mjs +8 -11
  54. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  55. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-http-node-adapter.mjs +10 -5
  56. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-http-node-adapter.mjs.map +1 -1
  57. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-types.d.mts +3 -1
  58. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-types.d.mts.map +1 -1
  59. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts +4 -2
  60. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts.map +1 -1
  61. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs +37 -6
  62. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs.map +1 -1
  63. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-types.d.mts +3 -1
  64. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-types.d.mts.map +1 -1
  65. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts +4 -2
  66. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts.map +1 -1
  67. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs +37 -0
  68. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs.map +1 -1
  69. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts +7 -1
  70. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts.map +1 -1
  71. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +10 -2
  72. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  73. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +50 -38
  74. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  75. package/dist/sync/sync-peer/sync-peer-types.d.mts +21 -5
  76. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  77. package/dist/sync/sync-peer/sync-peer-v1.d.mts +30 -5
  78. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  79. package/dist/sync/sync-peer/sync-peer-v1.mjs +41 -25
  80. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  81. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +2 -11
  82. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  83. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +7 -51
  84. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  85. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +1 -47
  86. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
  87. package/dist/sync/sync-saga-coordinator.d.mts +6 -58
  88. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  89. package/dist/sync/sync-saga-coordinator.mjs +23 -425
  90. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  91. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +2 -2
  92. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  93. package/dist/sync/sync-types.d.mts +2 -19
  94. package/dist/sync/sync-types.d.mts.map +1 -1
  95. package/dist/sync/sync-types.mjs.map +1 -1
  96. package/dist/test/mock-space.mjs +1 -1
  97. package/dist/test/mock-space.mjs.map +1 -1
  98. package/dist/test-helpers.d.mts +0 -6
  99. package/dist/test-helpers.d.mts.map +1 -1
  100. package/dist/test-helpers.mjs +2 -25
  101. package/dist/test-helpers.mjs.map +1 -1
  102. package/package.json +10 -34
  103. package/src/keystone/aggregate-details.respec.mts +137 -0
  104. package/src/keystone/docs/architecture.md +16 -0
  105. package/src/keystone/keystone-constants.mts +6 -1
  106. package/src/keystone/keystone-helpers.mts +9 -1
  107. package/src/keystone/keystone-service-v1.mts +216 -8
  108. package/src/keystone/keystone-service-v1.respec.mts +39 -103
  109. package/src/keystone/keystone-types.mts +6 -0
  110. package/src/sync/README.md +2 -87
  111. package/src/sync/docs/architecture.md +26 -5
  112. package/src/sync/docs/security.md +176 -0
  113. package/src/sync/graft-info/graft-info-helpers.respec.mts +7 -7
  114. package/src/sync/sync-conflict-adv-multitimelines.respec.mts +25 -29
  115. package/src/sync/sync-conflict-basic-divergence.respec.mts +6 -7
  116. package/src/sync/sync-conflict-basic-multitimelines.respec.mts +9 -10
  117. package/src/sync/sync-conflict-text-merge.respec.mts +29 -32
  118. package/src/sync/sync-constants.mts +2 -62
  119. package/src/sync/sync-helpers.mts +1 -8
  120. package/src/sync/sync-id-testlog.txt +421 -0
  121. package/src/sync/sync-innerspace-constants.respec.mts +4 -8
  122. package/src/sync/sync-innerspace-deep-updates.respec.mts +5 -6
  123. package/src/sync/sync-innerspace-dest-ahead-withid.respec.mts +23 -25
  124. package/src/sync/sync-innerspace-dest-ahead.respec.mts +7 -8
  125. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +5 -6
  126. package/src/sync/sync-innerspace-partial-update.respec.mts +6 -7
  127. package/src/sync/sync-innerspace.respec.mts +7 -10
  128. package/src/sync/sync-peer/sync-peer-http-receiver/sync-http-node-adapter.mts +5 -5
  129. package/src/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-types.mts +5 -1
  130. package/src/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mts +30 -9
  131. package/src/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-types.mts +3 -1
  132. package/src/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mts +29 -2
  133. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +7 -1
  134. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +51 -38
  135. package/src/sync/sync-peer/sync-peer-types.mts +23 -6
  136. package/src/sync/sync-peer/sync-peer-v1.mts +68 -28
  137. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +6 -66
  138. package/src/sync/sync-saga-context/sync-saga-context-types.mts +1 -48
  139. package/src/sync/sync-saga-coordinator.mts +12 -552
  140. package/src/sync/sync-saga-message/sync-saga-message-types.mts +2 -3
  141. package/src/sync/sync-types.mts +2 -22
  142. package/src/sync/unused-identity-backup.mts.md +311 -0
  143. package/src/test/mock-space.mts +1 -1
  144. package/src/test-helpers.mts +1 -26
  145. package/test_output.log +0 -0
  146. package/test_output_utf8.txt +398 -0
  147. package/.vscode/core-gib-snippets.code-snippets +0 -293
  148. package/.vscode/launch.json +0 -40
  149. package/.vscode/settings.json +0 -58
  150. package/.vscode/tasks.json +0 -37
  151. package/dist/sync/sync-peer/sync-peer-http.respec.d.mts +0 -2
  152. package/dist/sync/sync-peer/sync-peer-http.respec.d.mts.map +0 -1
  153. package/dist/sync/sync-peer/sync-peer-http.respec.mjs +0 -340
  154. package/dist/sync/sync-peer/sync-peer-http.respec.mjs.map +0 -1
  155. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-helper.d.mts +0 -42
  156. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-helper.d.mts.map +0 -1
  157. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-helper.mjs +0 -312
  158. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-helper.mjs.map +0 -1
  159. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-types.d.mts +0 -84
  160. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-types.d.mts.map +0 -1
  161. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-types.mjs +0 -65
  162. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-types.mjs.map +0 -1
  163. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-v1.d.mts +0 -73
  164. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-v1.d.mts.map +0 -1
  165. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-v1.mjs +0 -667
  166. package/dist/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-v1.mjs.map +0 -1
  167. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.d.mts +0 -2
  168. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.d.mts.map +0 -1
  169. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.mjs +0 -67
  170. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.mjs.map +0 -1
  171. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.d.mts +0 -2
  172. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.d.mts.map +0 -1
  173. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.mjs +0 -67
  174. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.mjs.map +0 -1
  175. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.d.mts +0 -2
  176. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.d.mts.map +0 -1
  177. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.mjs +0 -68
  178. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.mjs.map +0 -1
  179. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.d.mts +0 -2
  180. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.d.mts.map +0 -1
  181. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.mjs +0 -69
  182. package/dist/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.mjs.map +0 -1
  183. package/src/sync/sync-peer/sync-peer-http.respec.mts +0 -396
  184. package/src/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-helper.mts +0 -298
  185. package/src/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-types.mts +0 -150
  186. package/src/witness/space/filesystem-space/node-filesystem-space/node-filesystem-space-v1.mts +0 -666
  187. package/src/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_createAndInit.node-filesystem-space-v1.respec.mts +0 -87
  188. package/src/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_persistTransformResult.node-filesystem-space-v1.respec.mts +0 -88
  189. package/src/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_putGetDelete.node-filesystem-space-v1.respec.mts +0 -88
  190. package/src/witness/space/filesystem-space/node-filesystem-space/respec/testSpace_registerNewIbGib_GetLatest.node-filesystem-space-v1.respec.mts +0 -90
@@ -2,7 +2,7 @@ import {
2
2
  respecfully, iReckon, ifWe, firstOfAll, firstOfEach, lastOfAll, lastOfEach, respecfullyDear, ifWeMight
3
3
  } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
4
4
  const maam = `[${import.meta.url}]`, sir = maam;
5
- import { clone, hash } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
5
+ import { clone, hash, getUUID } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
6
6
  import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
7
7
  import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
8
8
 
@@ -17,79 +17,6 @@ import { addToBindingMap } from './keystone-helpers.mjs';
17
17
  const logalot = GLOBAL_LOG_A_LOT;
18
18
 
19
19
 
20
- // /**
21
- // * not sure where to put this, but we probably will want to reuse this in the
22
- // * future (assuming it works)
23
- // * @returns metaspace service reference
24
- // */
25
- // async function getNewInitializedInMemoryMetaspaceForTesting({
26
- // defaultSpaceName,
27
- // }: {
28
- // defaultSpaceName: string,
29
- // }): Promise<MetaspaceService> {
30
- // const lc = `[${getNewInitializedInMemoryMetaspaceForTesting.name}]`;
31
- // try {
32
- // if (logalot) { console.log(`${lc} starting... (I: 766d7596addcb73f4820586469233b25)`); }
33
-
34
- // let metaspace = new Metaspace_Innerspace(/*cacheSvc*/undefined);
35
- // if (logalot) { console.log(`${lc} creating metaspace complete. initializing... (I: 61b74d62e8832c9fa853e4b8c4c2d825)`); }
36
- // getGibInfo()
37
-
38
- // await metaspace.initialize({
39
- // spaceName: defaultSpaceName,
40
- // /**
41
- // * passing in undefined will use the defaults. probably will need to
42
- // * adjust this for testing purposes, but let's see what happens with
43
- // * this first.
44
- // */
45
- // metaspaceFactory: {
46
- // fnDtoToSpace: async () => {
47
- // if (!currentSpace) { currentSpace = new IbGibTestSpace(); }
48
- // return currentSpace;
49
- // },
50
- // fnZeroSpaceFactory: () => {
51
- // if (!currentZeroSpace) { currentZeroSpace = new IbGibTestSpace(); }
52
- // return currentZeroSpace;
53
- // },
54
- // fnDefaultLocalSpaceFactory: async () => {
55
- // if (!currentSpace) { currentSpace = new IbGibTestSpace(); }
56
- // return currentSpace;
57
- // },
58
-
59
- // // export type DtoToSpaceFunction = (spaceDto: IbGib_V1) => Promise<IbGibSpaceAny>;
60
- // // export type ZeroSpaceFactoryFunction = () => IbGibSpaceAny;
61
- // // export type LocalSpaceFactoryFunction = (opts: CreateLocalSpaceOptions) => Promise<IbGibSpaceAny | undefined>;
62
- // },
63
- // getFnAlert: () => { return async ({ title, msg }) => console.log(title, msg) },
64
- // getFnPrompt: () => {
65
- // return async ({ title, msg }) => {
66
- // // if this is needed, we might set up some way for testing
67
- // // to prepare either a queue of prompts or some kind of map or getter
68
- // // and put it on the metaspace itself
69
- // throw new Error(`not implemented (E: c7ef688a02f8cb74487260f9274ac825)`);
70
- // // promptForText({ title, msg, confirm: false });
71
- // }
72
- // },
73
- // getFnPromptPassword: () => {
74
- // return async () => {
75
- // // similar to getFnPrompt, if we need a _different_
76
- // // password, we might set up some way for testing to prepare
77
- // // either a queue of passwords or some kind of map or getter
78
- // // and put it on the metaspace itself
79
- // return 'password';
80
- // // promptForSecret({ confirm: true })
81
- // }
82
- // },
83
- // });
84
- // return metaspace;
85
- // } catch (error) {
86
- // console.error(`${lc} ${extractErrorMsg(error)}`);
87
- // throw error;
88
- // } finally {
89
- // if (logalot) { console.log(`${lc} complete.`); }
90
- // }
91
- // }
92
-
93
20
  /**
94
21
  * A simple in-memory map acting as a Space.
95
22
  * Pure Storage. No Indexing logic.
@@ -115,8 +42,8 @@ class MockIbGibSpace {
115
42
  const addrs = arg.data.ibGibAddrs || [];
116
43
  const ibGibs: IbGib_V1[] = [];
117
44
  for (const addr of addrs) {
118
- const ig = await this.get({ addr });
119
- if (ig) ibGibs.push(ig);
45
+ const x = await this.get({ addr });
46
+ if (x) { ibGibs.push(x); }
120
47
  }
121
48
  return { ibGibs };
122
49
  }
@@ -190,13 +117,14 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
190
117
 
191
118
  // Setup generic variables
192
119
  const masterSecret = "TestSecret_12345";
193
- const salt = "TestPool";
120
+ const poolId = "TestPool";
194
121
  let config: KeystonePoolConfig_HashV1;
195
122
 
196
123
  firstOfAll(sir, async () => {
197
124
  // Use our standard builder to get a valid config object
125
+ const salt = (await getUUID()).substring(0, 16);
198
126
  config = createStandardPoolConfig({
199
- id: salt,
127
+ id: poolId,
200
128
  salt,
201
129
  }) as KeystonePoolConfig_HashV1;
202
130
  });
@@ -243,7 +171,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
243
171
  const challengeId = "a3ff7843552870fc28bef2b"; // arbitrary random challengeId
244
172
 
245
173
  // 1. Generate Solution
246
- const solution = await strategy.generateSolution({ poolSecret, poolId: salt, challengeId });
174
+ const solution = await strategy.generateSolution({ poolSecret, poolId: config.id, challengeId });
247
175
  iReckon(sir, solution.value).asTo('solution value exists').isGonnaBeTruthy();
248
176
  iReckon(sir, solution.challengeId).asTo('id matches').willEqual(challengeId);
249
177
 
@@ -262,7 +190,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
262
190
  const challengeId = "8c994f3ed598f150e25513"; // arbitrary random challengeId
263
191
 
264
192
  // Generate real pair
265
- const solution = await strategy.generateSolution({ poolSecret, poolId: salt, challengeId });
193
+ const solution = await strategy.generateSolution({ poolSecret, poolId: config.id, challengeId });
266
194
  const challenge = await strategy.generateChallenge({ solution });
267
195
 
268
196
  // Tamper with solution value
@@ -278,11 +206,11 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
278
206
 
279
207
  // Generate pair A
280
208
  const challengeId_A = "416c38cfd6ee63dbf8d4e5ef36"; // arbitrary random challengeId
281
- const solutionA = await strategy.generateSolution({ poolSecret, poolId: salt, challengeId: challengeId_A });
209
+ const solutionA = await strategy.generateSolution({ poolSecret, poolId: config.id, challengeId: challengeId_A });
282
210
 
283
211
  // Generate pair B
284
212
  const challengeId_B = "c487ef6b7878fae798c3"; // arbitrary random challengeId
285
- const solutionB = await strategy.generateSolution({ poolSecret, poolId: salt, challengeId: challengeId_B });
213
+ const solutionB = await strategy.generateSolution({ poolSecret, poolId: config.id, challengeId: challengeId_B });
286
214
  const challengeB = await strategy.generateChallenge({ solution: solutionB });
287
215
 
288
216
  // Check A against B
@@ -314,9 +242,10 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
314
242
 
315
243
  await respecfully(sir, 'Genesis', async () => {
316
244
  await ifWe(sir, 'creates a valid genesis frame and persists it', async () => {
245
+ const salt = (await getUUID()).substring(0, 16);
317
246
  const config = createStandardPoolConfig({
318
247
  id: POOL_ID_DEFAULT,
319
- salt: POOL_ID_DEFAULT,
248
+ salt,
320
249
  });
321
250
 
322
251
  genesisKeystone = await service.genesis({
@@ -406,9 +335,10 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
406
335
  mockMetaspace = new MockMetaspaceService(mockSpace);
407
336
 
408
337
  // Setup Alice's Identity
338
+ const salt = (await getUUID()).substring(0, 16);
409
339
  const config = createStandardPoolConfig({
410
340
  id: POOL_ID_DEFAULT,
411
- salt: POOL_ID_DEFAULT,
341
+ salt,
412
342
  targetBinding: 0,
413
343
  size: 10,
414
344
  });
@@ -509,13 +439,15 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
509
439
  mockMetaspace = new MockMetaspaceService(mockSpace);
510
440
 
511
441
  // Setup Identity WITH a Revocation Pool
442
+ const stdSalt = (await getUUID()).substring(0, 16);
443
+ const revokeSalt = (await getUUID()).substring(0, 16);
512
444
  const stdConfig = createStandardPoolConfig({
513
445
  id: POOL_ID_DEFAULT,
514
- salt: POOL_ID_DEFAULT,
446
+ salt: stdSalt,
515
447
  });
516
448
  const revokeConfig = createRevocationPoolConfig({
517
449
  id: POOL_ID_REVOKE,
518
- salt: POOL_ID_REVOKE,
450
+ salt: revokeSalt,
519
451
  }); // Special Config
520
452
 
521
453
  genesisKeystone = await service.genesis({
@@ -588,7 +520,8 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
588
520
 
589
521
  // Helper to generate a "Foreign" pool (e.g. from Bob)
590
522
  const createForeignPool = async (id: string, verbs: string[] = []): Promise<KeystoneChallengePool> => {
591
- const config = createStandardPoolConfig({ id, salt: id });
523
+ const salt = (await getUUID()).substring(0, 16);
524
+ const config = createStandardPoolConfig({ id, salt });
592
525
  config.allowedVerbs = verbs;
593
526
 
594
527
  // We use the factory manually here to simulate Bob doing this offline
@@ -625,7 +558,8 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
625
558
  mockMetaspace = new MockMetaspaceService(mockSpace);
626
559
 
627
560
  // Alice Genesis: Standard pool (allows all verbs, including 'manage')
628
- const config = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: POOL_ID_DEFAULT });
561
+ const salt = (await getUUID()).substring(0, 16);
562
+ const config = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt });
629
563
  aliceKeystone = await service.genesis({
630
564
  masterSecret: aliceSecret,
631
565
  configs: [config],
@@ -746,7 +680,8 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
746
680
 
747
681
  // Helper to simulate Bob creating a pool "offline" to give to Alice
748
682
  const createForeignPool = async (id: string, verbs: string[] = []): Promise<KeystoneChallengePool> => {
749
- const config = createStandardPoolConfig({ id, salt: id });
683
+ const salt = (await getUUID()).substring(0, 16);
684
+ const config = createStandardPoolConfig({ id, salt });
750
685
  config.allowedVerbs = verbs;
751
686
 
752
687
  // We use the factory manually here to simulate Bob doing this on his own machine
@@ -783,9 +718,10 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
783
718
  mockMetaspace = new MockMetaspaceService(mockSpace);
784
719
 
785
720
  // Alice Genesis: Standard pool (allows all verbs, including 'manage')
721
+ const salt = (await getUUID()).substring(0, 16);
786
722
  const config = createStandardPoolConfig({
787
723
  id: POOL_ID_DEFAULT,
788
- salt: POOL_ID_DEFAULT,
724
+ salt,
789
725
  });
790
726
  aliceKeystone = await service.genesis({
791
727
  masterSecret: aliceSecret,
@@ -899,27 +835,27 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
899
835
 
900
836
  const service = new KeystoneService_V1();
901
837
  const aliceSecret = "Alice_Deep_Inspect";
902
- const salt = "granularity_pool";
838
+ const poolId = "granularity_pool";
903
839
 
904
840
  let mockSpace: MockIbGibSpace;
905
841
  let mockMetaspace: any;
906
842
  let genesisKeystone: KeystoneIbGib_V1;
907
-
908
843
  let signedKeystone: KeystoneIbGib_V1;
909
-
910
- // We use a specific hybrid config to test exact selection logic
911
- const hybridConfig = createStandardPoolConfig({
912
- id: salt,
913
- salt,
914
- // 2 FIFO + 2 Random = 4 Total per sign
915
- sequential: 2, random: 2, targetBinding: 0,
916
- size: 20, // Small enough to track, large enough to be random
917
- }) as KeystonePoolConfig_HashV1;
844
+ let hybridConfig: KeystonePoolConfig_HashV1;
918
845
 
919
846
  firstOfAll(sir, async () => {
920
847
  mockSpace = new MockIbGibSpace();
921
848
  mockMetaspace = new MockMetaspaceService(mockSpace);
922
849
 
850
+ const salt = (await getUUID()).substring(0, 16);
851
+ hybridConfig = createStandardPoolConfig({
852
+ id: poolId,
853
+ salt,
854
+ // 2 FIFO + 2 Random = 4 Total per sign
855
+ sequential: 2, random: 2, targetBinding: 0,
856
+ size: 20, // Small enough to track, large enough to be random
857
+ }) as KeystonePoolConfig_HashV1;
858
+
923
859
  genesisKeystone = await service.genesis({
924
860
  masterSecret: aliceSecret,
925
861
  configs: [hybridConfig],
@@ -949,7 +885,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
949
885
 
950
886
  await ifWe(sir, 'verifies the math manually (White-box Crypto Check)', async () => {
951
887
  const proof = signedKeystone.data!.proofs[0];
952
- const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === salt)!;
888
+ const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === poolId)!;
953
889
 
954
890
  // We iterate every solution in the proof and MANUALLY verify the hash relationship
955
891
  // bypassing the Service's validation logic to ensure the raw math holds up.
@@ -978,7 +914,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
978
914
 
979
915
  await ifWe(sir, 'verifies FIFO logic (Deterministic Selection)', async () => {
980
916
  const proof = signedKeystone.data!.proofs[0];
981
- const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === salt)!;
917
+ const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === poolId)!;
982
918
 
983
919
  // The first N keys in the pool should be the FIFO targets.
984
920
  // Assumption: Object.keys returns insertion order (Standard in modern JS engines)
@@ -349,6 +349,12 @@ export interface KeystoneData_V1 extends IbGibData_V1 {
349
349
  * to the next frame's data during evolution.
350
350
  */
351
351
  frameDetails?: any;
352
+
353
+ /**
354
+ * Aggregated state of the Keystone identity up to this frame.
355
+ * Acts as a snapshot to avoid walking the entire timeline.
356
+ */
357
+ checkpointDetails?: any;
352
358
  }
353
359
 
354
360
  export interface KeystoneRel8ns_V1 extends IbGibRel8ns_V1 {
@@ -20,7 +20,6 @@ Init -> Ack -> Delta(s) -> Conflict/Commit via "ping pong" exchange.
20
20
 
21
21
  1. **Init**: Sender initiates sync.
22
22
  * Creates the `SyncSagaIbGib_V1` with an `Init` msg frame containing knowledge about domain ibgibs to sync.
23
- * Optionally creates session keystone based off of Sender's primary keystone.
24
23
  * Wraps the saga ibgib in a `SyncSagaContextIbGib_V1` and passes to the `SyncPeer_V1`.
25
24
  2. **Ack**: Receiver gets incoming `SyncSagaContextIbGib_V1` and passes to its `SyncSagaCoordinator`.
26
25
  * Handles the `Init` frame, comparing knowledge with Receiver's own status of each timeline/stone in domain ibgibs.
@@ -34,44 +33,11 @@ Init -> Ack -> Delta(s) -> Conflict/Commit via "ping pong" exchange.
34
33
  * `SyncPeer_V1.payloadIbGibsDomainReceived$` is an observable for the actual domain payload ibgibs transmitted.
35
34
  * So the a node sends domain payloads out in bulk to the `SyncPeer_V1`, receives the return saga ibgib with a manifest, but may have to wait for the observable to complete to actually process the ibgibs.
36
35
  * `Delta` frame can include `requests` (e.g., asking for missing conflict history).
37
- * Once no deltas required, `Delta` frame sets `proposeCommit` flag. If the other has nothing to add, the session transitions to `Commit`.
36
+ * Once no deltas required, `Delta` frame sets `proposeCommit` flag. If the other has nothing to add, the saga transitions to `Commit`.
38
37
  4. **Commit**: When receive `Delta` frame with `proposeCommit`, should trigger the receiver of this to start its commit. If that is successful, it should return a `Commit` frame, which signals the commit was successful on their end. When this `Commit` frame is received, this end should not perform its own commit.
39
38
  * For local-only syncs, the `SyncPeer_V1` implementation is responsible for moving verified `ibGibs` from the temporary saga space to the durable/persistent domain space.
40
39
  * From each endpoint's POV, the cleanup should remove the temp spaces used, in addition to any other required transaction cleanup (like closing any connections).
41
40
 
42
- ### Session Identity (`useSessionIdentity`)
43
-
44
- The sync protocol can optionally generate an ephemeral Keystone Identity for the duration of the saga.
45
-
46
- **Purpose**: To allow secure, signed exchanges even if the Node doesn't have a long-lived identity, or to isolate the sync session security from master identity.
47
-
48
- **Current Status**:
49
- * ✅ Session keystone creation implemented ([getSessionIdentity()](file:./sync-saga-coordinator.mts#L328-L362))
50
- * ✅ Keystone passed via Init message ([initData.identity](file:./sync-saga-message/sync-saga-message-types.mts#L75))
51
- * ⚠️ **Signature generation/verification: NOT yet implemented**
52
- * ⚠️ **Per-frame proof creation: NOT yet implemented**
53
-
54
- **Usage**:
55
- ```typescript
56
- const { done } = await coordinator.sync({
57
- peer,
58
- domainIbGibs: [timeline],
59
- localSpace,
60
- metaspace,
61
- useSessionIdentity: true, // Default, but currently non-functional
62
- });
63
- ```
64
-
65
- > [!CAUTION]
66
- > **🚧 Session identity is NOT working yet.** Tests explicitly set `useSessionIdentity: false` to avoid failures. See [sync-innerspace-dest-ahead-withid.respec.mts](file:./sync-innerspace-dest-ahead-withid.respec.mts) for the failing test case.
67
-
68
- **Authentication/Authorization Hooks**:
69
-
70
- Validation occurs in [SyncPeer_V1.witness()](file:./sync-peer/sync-peer-v1.mts#L129-L219):
71
- 1. `validateContextAndSagaFrame()` - Intrinsic validation
72
- 2. `authenticateContext()` - Verify identity (stub)
73
- 3. `authorizeContext()` - Check permissions (stub)
74
-
75
41
  ### Conflict Resolution via Grafting
76
42
 
77
43
  > [!IMPORTANT]
@@ -110,58 +76,6 @@ See [graft-info-helpers.mts](file:./graft-info/graft-info-helpers.mts) for imple
110
76
 
111
77
  **Why**: Ensures atomic sync - either all data validates and commits, or none does.
112
78
 
113
-
114
- ## 🔒 Security Considerations
115
-
116
- > [!WARNING]
117
- > Session identity is **currently non-functional**. The mitigations below describe the intended security model.
118
-
119
- ### Attack Vectors & Mitigations
120
-
121
- | Attack Vector | Description | Mitigation |
122
- |---------------|-------------|------------|
123
- | **MITM Replay** | Attacker replays captured saga frames | • Frame signatures include timestamp<br>• Freshness checks reject old frames<br>• Challenge depletion prevents reuse<br>• sagaId deduplication |
124
- | **Session Fixation** | Attacker pre-generates session keystone | • Session secret derived via KDF (strategy in keystone metadata)<br>• `sessionSecret = KDF(masterSecret, sagaId, strategy)` |
125
- | **Identity Spoofing** | Impersonation via fake session keystone | • Session keystone includes `derivedFrom` master address<br>• Receiver validates master keystone is known/trusted |
126
- | **Challenge Grinding** | Brute-force challenge solutions for target binding | • Hash pre-imaging computationally infeasible<br>• Stochastic selection adds randomness |
127
- | **DoS (Pool Exhaustion)** | Excessive syncs deplete challenge pools | • Top-up replenishment refills challenges<br>• Rate limiting per identity<br>• Pool separation for critical ops |
128
- | **Saga Hijacking** | Mid-flight frame injection | • All frames signed with session keystone<br>• `past` rel8n creates tamper-evident chain |
129
-
130
- ### Cryptographic Primitives
131
-
132
- * **Hash Function**: SHA-256 (content addressing, challenge generation)
133
- * **Proof System**: Hash-reveal protocol (`hash-reveal-v1`)
134
- * **Binding**: Target address → hex bucket → challenge selection
135
- * **Entropy**: Stochastic challenge selection (anti-grinding)
136
-
137
- ### Trust Model
138
-
139
- **Session Keystones**:
140
- - Ephemeral identity per sync saga
141
- - Derived from master keystone + `sagaId`
142
- - Validates via proof-of-work (solving challenges)
143
- - Post-hoc audit trail: master can prove session authorship
144
-
145
- **Propagation**:
146
- - Sync does NOT rely on global PKI or certificate authorities
147
- - Trust propagates through sync sessions (Alice syncs with Bob → Bob witnesses Alice's keystone)
148
- - Revocation propagates same way (Alice revokes → syncs revocation to Bob → Bob sees revoked timeline)
149
-
150
- **Threat Boundaries**:
151
- - ✅ Protects against: MITM replay, impersonation, frame tampering
152
- - ⚠️ Does NOT protect against: Compromised endpoint (live memory access during active session)
153
- - ⚠️ Master secrets: Raw secrets should NOT stored; keystones master secrets to keystones should be derived secrets via key stretching passwords. raw secret files of course can drive these passwords and must be protected
154
- - ⚠️ Transport security: Use TLS for network layer encryption (ibgib protocol provides authentication/integrity, not confidentiality)
155
-
156
- ### Best Practices
157
-
158
- 1. **Master Secret Protection**: Store master secrets in secure enclaves/keychains
159
- 2. **Session Lifetime**: Limit saga duration, revoke sessions post-completion
160
- 3. **Rate Limiting**: Implement per-identity sync rate limits
161
- 4. **Freshness**: Reject frames older than 60 seconds
162
- 5. **Audit Logs**: Persist saga timelines for post-hoc validation (saga timelines are ibgibs, so integrity is built-in via content addressing)
163
- 6. **TLS**: Always use TLS for network transport (defense-in-depth)
164
-
165
79
  ## 📚 Documentation
166
80
  * **[Architecture](./docs/architecture.md)**: Detailed design of the Saga Protocol, State Machine, and Smart Diff logic.
167
81
  * **[Testing & Verification](./docs/testing.md)**: Test strategy, verification matrix, and comprehensive testing guidelines.
@@ -174,3 +88,4 @@ See [graft-info-helpers.mts](file:./graft-info/graft-info-helpers.mts) for imple
174
88
 
175
89
  ## Verification
176
90
  We rely on rigorous In-Memory Simulation (`InnerSpace`) to verify complex graph scenarios. See **[Verification](./docs/verification.md)** for the full status.
91
+
@@ -21,7 +21,7 @@ The `SyncSagaCoordinator` drives a Finite State Machine (FSM) via the `handleSag
21
21
  ### 2.1 The Ping-Pong Flow
22
22
 
23
23
  1. **Start (Initiator)**:
24
- * Initiator (Alice) calls `startSaga`.
24
+ * Initiator (Alice) calls `sync`.
25
25
  * Generates `Init` frame containing her `KnowledgeMap` and `Mode` (Push/Pull/Sync).
26
26
  * Sends `Init` to Bob.
27
27
 
@@ -42,7 +42,7 @@ The `SyncSagaCoordinator` drives a Finite State Machine (FSM) via the `handleSag
42
42
 
43
43
  4. **Convergence (`handleDeltaFrame`)**:
44
44
  * Bob receives `Delta`.
45
- * **Verifies**: Checks content-address hashes (`ib` vs `gib`). ⚠️ Keystone signature verification pending.
45
+ * **Verifies**: Checks content-address hashes.
46
46
  * **Merges**: Puts data into his local space.
47
47
  * Bob sends `Commit` frame.
48
48
 
@@ -55,7 +55,7 @@ The `SyncSagaCoordinator` drives a Finite State Machine (FSM) via the `handleSag
55
55
  ### 3.1 SyncSagaMessage
56
56
  All frames are `SyncIbGib`s carrying specific data payloads.
57
57
 
58
- * `sagaId`: Unique UUID for the session.
58
+ * `sagaId`: Unique UUID for the duration of this exchange.
59
59
  * `stage`: `init` | `ack` | `delta` | `commit` | `conflict`.
60
60
  * `knowledgeMap`: `{ [tjpAddr]: [tipAddr, ...ancestors] }`.
61
61
 
@@ -65,5 +65,26 @@ The protocol supports two types of data:
65
65
  * **Stones (Constants)**: Immutable data without a history (e.g., Code lists, Configs). Syncing involves simple existence checks.
66
66
 
67
67
  ## 4. Security & Identity
68
- * **Session Keystone**: Each Saga can optionally generate an ephemeral identity to sign frames, linked to a master identity. ⚠️ **Currently non-functional** - see [README Session Identity section](file:../README.md#session-identity-usesessionidentity) for status.
69
- * **Trust Chain**: Each frame points to the previous one (`past` rel8n), creating a hash-linked chain of custody.
68
+
69
+ See [Security](./security.md) documentation.
70
+
71
+ > [!WARNING]
72
+ > ## 5. Sync Storage & Persistence Rules (Critical Security)
73
+ >
74
+ > Knowing **when** to put **what** ibgib frames into **which** space is a high-priority security concern. The sync algorithm must balance maintaining a robust audit trail with protecting durable spaces from unauthorized pollution. Always follow these rules:
75
+ >
76
+ > ### Rule 1: Saga/Control Audit Trail (Immediate Persistence)
77
+ > We need an audit trail of the sync saga itself (including context and authentication info).
78
+ > * The initial session keystone (or any valid evolution performed locally) and saga control ibgibs are trusted because *we* just generated them.
79
+ > * Persist these immediately in the **local durable space** (and possibly the local temporary space if needed for transit) so they are available for validation and auditing.
80
+ >
81
+ > ### Rule 2: Domain IbGibs (Deferred Persistence)
82
+ > We do NOT persist *any* domain ibgibs (e.g., user payloads) into the durable space before the `commit` phase of the sync process.
83
+ > * Domain ibgibs must be routed to the **tempSpace** during the `delta` or `ack` phases.
84
+ > * The `commit` phase acts as the transaction boundary. By waiting for the commit, we avoid polluting durable spaces on error, preventing the need for complex rollbacks.
85
+ >
86
+ > ### Rule 3: Validate First, Store Second
87
+ > We never chance storing intrinsically invalid ibgibs.
88
+ > * **First step:** Intrinsically validate ibgibs (hashes, basic structure).
89
+ > * **Second step:** Perform AuthN/AuthZ checks (using the session keystone).
90
+ > * **Final step:** Once verified, saga "control" ibgibs (which only contain "soft" links to domain ibgibs) can be safely persisted to durable spaces.
@@ -0,0 +1,176 @@
1
+ # Ibgib Sync Security
2
+
3
+ We use ibgib's unique Merkle-DAG-based protocol and innovative "keystone" construct (see keystone [README.md](../../keystone/README.md) and other [docs](../../keystone/docs/)) to maximize security while minimizing surface area (code).
4
+
5
+ ## Core Concepts
6
+
7
+ * **Domain Keystone (I)**: The primary, long-lived identity keystone representing the data owner (e.g., Alice). This dictates high-level access and is registered with a domain provider (e.g., Space-Gib as an initial reference implementation).
8
+ * **Session Keystone (S)**: An ephemeral, short-lived keystone generated locally by the Sender explicitly for a single sync session.
9
+ * **Target Domain (X)**: The specific ibgib timeline or payload that is the subject of the sync.
10
+ * **Sovereign Broker (Receiver)**: The domain provider acts as a passive, high-integrity validator. It does not generate or sign sync frames with its own session identity; it relies entirely on the Sender's cryptographic proofs to authorize actions. While we may treat the provider as "honest" practically, it is ultimately a sovereign entity over its data, and its "honesty" is driven by long-term self-interest.
11
+
12
+ ## Development Nuances & Implementation TODOs
13
+
14
+ This section serves as a working scratchpad to capture specific code-level nuances, terminology standardization, and pending tasks before jumping into implementation.
15
+
16
+ ### ✅ Agreed: Secret Naming — `senderSecret` & `sessionSecret`
17
+
18
+ * **`senderSecret`**: The master secret corresponding to `senderIdentity`. Drives the KDF to produce the session secret. Replaces the old `nonSessionSecret` / `identitySecret` / `domainSecret` names.
19
+ * **`sessionSecret`**: Deterministically derived via `KDF(senderSecret, sagaId)`. Ephemeral and saga-specific. Used only within `establishSessionIdentity` to create the session keystone genesis.
20
+ * *Rationale*: `sender` pairs naturally with `senderIdentity`; `session` pairs naturally with `sessionIdentity`.
21
+
22
+ ### ✅ Agreed: Identity Naming — `senderIdentity` & `sessionIdentity`
23
+
24
+ * **`senderIdentity`**: The long-lived Domain Keystone (I) representing the Sender (Alice). Optional — a sync can run with just a `domainSecret` and no named identity.
25
+ * Using `senderIdentity` (not `domainIdentity` or `primaryIdentity`) because "domain" becomes ambiguous when Bob eventually has his own domain identity in a future symmetric model.
26
+ * **`sessionIdentity`**: The ephemeral keystone (S) generated per-saga. Used consistently as both:
27
+ * A **param/property name** (e.g., `peer.sessionIdentity`, `createSessionIdentity()`).
28
+ * A **rel8n name** on both the sync saga ibgib and the context ibgib (e.g., `syncSagaIbGib.rel8ns.sessionIdentity`, `contextIbGib.rel8ns.sessionIdentity`).
29
+
30
+ ### ✅ Agreed: Identity Hard-linking Strategy
31
+
32
+ * **Sync Saga Frame** (`ibgib.data`, soft-link):
33
+ * `data` should record identity details as soft references (addresses in `ibgib.data`, not hard-linked `ibgib.rel8ns`):
34
+ * `senderIdentity` TJP addr.
35
+ * The exact `senderIdentity` frame addr that signed/authorized the session (i.e., the new frame post-`establishSessionIdentity`).
36
+ * `sessionIdentity` TJP addr.
37
+ * No hard-link (`rel8ns`) to `sessionIdentity` from the sync saga ibgib.
38
+ * **Context IbGib** (`ibgib.rel8ns`, hard-link):
39
+ * `rel8ns.sessionIdentity` hard-links to the **previous** session keystone frame (the frame *before* the current signing step). This is the "point forward, sign backward" pattern: the signed (evolved) keystone's claim targets *this context's addr*, so the context can only reference the *prior* frame.
40
+ * The `signedSessionKeystone` property on the context carries the **newly evolved** frame (the current turn's signature).
41
+ * **No transition pool**: The prior symmetric "transition pool" (for the receiver's signing turn) is eliminated. Only the Sender signs.
42
+ * **Sovereign Broker response**: The Receiver includes the *same, unevolved* `sessionIdentity` addr in its response context's `rel8ns.sessionIdentity` — it does not evolve the keystone.
43
+
44
+ ### ✅ Agreed: Session Keystone Pool Configuration
45
+
46
+ The session keystone has **two dedicated pools**, each with a matching `poolId` and `verb`:
47
+
48
+ * **`connect` pool** (`poolId: "connect"`, `verb: "connect"`):
49
+ * Used exclusively during `peer.connect()` — the in-band WebSocket challenge/response handshake.
50
+ * The receiver issues challenges from this pool; the sender solves them to prove possession of S.
51
+ * **`sync` pool** (`poolId: "sync"`, `verb: "sync"`):
52
+ * Used to sign each outgoing context frame during the sync ping-pong (Init, Ack, Delta, Commit turns).
53
+ * Pool separation is intentional — the `connect` handshake and per-turn signing are distinct operations with distinct lifetimes.
54
+ * [ ] Add `"connect"` and `"sync"` verbs to `keystone-constants.mts`.
55
+ * [ ] Create standard pool config factories for both in [`keystone-config-builder.mts`](../../keystone/keystone-config-builder.mts).
56
+
57
+ Note: The `senderIdentity` Domain Keystone also uses the `"sync"` verb in its claim when it signs/authorizes the session keystone genesis during `establishSessionIdentity`.
58
+
59
+ ### ✅ Agreed: `establishSessionIdentity` — Pre-Connect Phase
60
+
61
+ A new `establishSessionIdentity` method on the sync peer base class is the **mandatory first step** before `connect`. It encapsulates:
62
+
63
+ 1. **Generate session keystone**: `sessionIdentity` genesis (S^Stjp) is created locally from `KDF(senderSecret, sagaId)`. The genesis includes metadata about `senderIdentity` TJP and the target domain.
64
+ 2. **Sign `senderIdentity`**: The Sender signs their own `senderIdentity` keystone with a `sync` claim targeting `S^Stjp`. The result (`newSenderIdentity`) is the evolved sender frame that proves delegation. The name `newSenderIdentity` is **only** used within `establishSessionIdentity` and its helper — after this method returns, it becomes the active `senderIdentity`.
65
+ 3. **Post to domain provider**: Both `newSenderIdentity` and `sessionIdentity` genesis are transmitted to the Receiver via a pre-connect API call (cf. `putEvolveKeystone` in `dev-tools.mts`).
66
+ 4. **Receiver validates**: The Receiver independently loads the **latest known tip** of `I^Itjp` from its own registry (never trusting `newSenderIdentity.rel8ns.past`). It validates the evolution and, if authorized, stores both keystones in the domain.
67
+ 5. **After this point**: `senderIdentity` IS the new frame. The session keystone takes over authorization for all subsequent sync turns.
68
+
69
+ ### ✅ Agreed: Per-Turn Transmission Protocol
70
+
71
+ * **Sender → Receiver (each turn)**: Only the **current evolved frame of `sessionIdentity`** (S) is transmitted. `senderIdentity` is never re-transmitted after `establishSessionIdentity`.
72
+ * **Receiver validation (each turn)**: The Receiver independently loads the latest `senderIdentity` tip from its domain registry, finds the `S^Stjp` addr from the `sync` claim within it, loads the known session keystone graph, and validates the incoming evolved S frame against it.
73
+ * This is possible on every turn because `establishSessionIdentity` guarantees the domain and `I^Itjp` already exist on the Receiver.
74
+ * **Receiver → Sender (each turn)**: The Receiver's response context echoes the same (unevolved) `sessionIdentity` addr — it never produces a new keystone evolution.
75
+
76
+ ### 🔲 Pending: `authenticateContext` Placement
77
+
78
+ The receiver-side validation that runs on each incoming context has two steps:
79
+ 1. **Transition validity**: Replay the session keystone evolution — verify `data.n` is sequential, challenge solutions are valid, pool not exhausted, etc.
80
+ 2. **Target binding**: Verify that the incoming `signedSessionIdentity`'s proof actually targets *this* context's exact address (`proof.claim.target === contextAddr`).
81
+
82
+ **Open**: Where does this code live?
83
+ * **On the sync peer** (e.g., as `peer.authenticateContext(...)`): the peer already knows its local durable space, which is needed to load the latest `senderIdentity` tip. This makes the tip-lookup natural.
84
+ * **As a pure helper function** (`sync-peer-helpers.mts`): would need the durable space passed as a parameter (plus whatever else the tip-lookup requires).
85
+
86
+ The peer placement is currently preferred since it co-locates the space knowledge with the validation logic. Finalize during implementation.
87
+
88
+ ### ✅ Agreed: `KeystoneService_V1` — No Injection Needed
89
+
90
+ `KeystoneService_V1` is stateless. It can be `new`-ed on demand wherever validation or signing is needed. There is no need to pass it as a constructor parameter or inject it into `authenticateContext` or any related helper. Just instantiate inline.
91
+
92
+ ### ✅ Agreed: Context Property — `signedSessionIdentity`
93
+
94
+ The context ibgib carries the newly evolved `sessionIdentity` frame in a property named **`signedSessionIdentity`** (replacing the old `signedSessionKeystone`).
95
+
96
+ Context ibgib now has a clean, consistent pair:
97
+ * `rel8ns.sessionIdentity` → addr of the **previous** frame (before signing this turn)
98
+ * `signedSessionIdentity` → the **newly evolved** frame (current turn's cryptographic proof)
99
+
100
+
101
+
102
+ ## High Level Authorization Flow
103
+
104
+ 1. [ ] **`establishSessionIdentity`** *(new pre-connect phase)*:
105
+ [ ] * Sender locally generates `sessionIdentity` (S) and signs `senderIdentity` (I &rarr; I1) with a `sync` claim targeting `S^Stjp`.
106
+ [ ] * Both keystones are posted to the domain provider. Receiver validates the evolution against its own known tip of `I^Itjp` and stores them if authorized.
107
+ 2. [ ] **`peer.connect()`** *(transport handshake)*:
108
+ [ ] * Sender opens the transport channel (e.g., WebSocket).
109
+ [ ] * Receiver issues challenges from S's **`connect` pool**.
110
+ [ ] * Sender solves and returns proof-of-possession. Session is authorized.
111
+ 3. [ ] **Asymmetric Sync (Ping-Pong)**:
112
+ [ ] * Sender signs all outgoing contexts (Init, Ack, Delta, Commit) using S's **`sync` pool**, evolving S each turn.
113
+ [ ] * Only the latest evolved S frame is transmitted per turn — never `senderIdentity`.
114
+ [ ] * Receiver validates by independently loading `I^Itjp`, tracing to `S^Stjp`, and verifying the incoming frame.
115
+ [ ] * Receiver responds with unsigned contexts (same S addr echoed, no new evolution).
116
+
117
+
118
+ ## Attack Vectors & Mitigations
119
+
120
+ | Attack Vector | Description | Mitigation |
121
+ |---------------|-------------|------------|
122
+ | **MITM Replay** | Attacker replays captured saga frames | • Frame signatures include timestamp<br>• Freshness checks reject old frames<br>• Challenge depletion prevents reuse<br>• sagaId deduplication |
123
+ | **Session Fixation** | Attacker pre-generates session keystone | • Session secret derived via KDF (strategy in keystone metadata)<br>• `sessionSecret = KDF(masterSecret, sagaId, strategy)` |
124
+ | **Identity Spoofing** | Impersonation via fake session keystone | • Session keystone includes `derivedFrom` master address<br>• Receiver validates master keystone is known/trusted |
125
+ | **Challenge Grinding** | Brute-force challenge solutions for target binding | • Hash pre-imaging computationally infeasible<br>• Stochastic selection adds randomness |
126
+ | **DoS (Pool Exhaustion)** | Excessive syncs deplete challenge pools | • Top-up replenishment refills challenges<br>• Rate limiting per identity<br>• Pool separation for critical ops |
127
+ | **Saga Hijacking** | Mid-flight frame injection | • All frames signed with session keystone<br>• `past` rel8n creates tamper-evident chain |
128
+
129
+ ## Trust Model
130
+
131
+ **Asymmetric Identity (Domain-Based Authorization)**:
132
+ - **Sender Signs**: The Sender is responsible for cryptographically signing all requests using their ephemeral Session Keystone.
133
+ - **Receiver Validates**: The Receiver acts strictly as an "Honest Broker". It validates the Sender's proofs and domain boundaries but does not maintain or sign a symmetric session identity.
134
+ - **Delegated Authority**: Trust is explicitly delegated from a Domain Keystone (I) to a Session Keystone (S) via a `sync` claim evolution.
135
+
136
+ **Session Keystones**:
137
+ - Ephemeral identity per sync saga.
138
+ - Secret derived via `KDF(senderSecret, sagaId)`.
139
+ - Contains two isolated pools: `connect` (for transport handshake) and `sync` (for per-turn frame signing).
140
+ - Validates via proof-of-work (solving challenges from the appropriate pool).
141
+
142
+ **Propagation**:
143
+ - Sync does NOT rely on global PKI or certificate authorities.
144
+ - Trust propagates through sync sessions (Alice syncs with Bob &rarr; Bob witnesses Alice's keystone).
145
+ - Revocation propagates same way (Alice revokes → syncs revocation to Bob → Bob sees revoked timeline)
146
+
147
+ **Threat Boundaries**:
148
+ - ✅ Protects against: MITM replay, impersonation, frame tampering
149
+ - ⚠️ Does NOT protect against: Compromised endpoint (live memory access during active session)
150
+ - ⚠️ Master secrets: Raw secrets should NOT stored; keystones master secrets to keystones should be derived secrets via key stretching passwords. raw secret files of course can drive these passwords and must be protected
151
+ - ⚠️ Transport security: Use TLS for network layer encryption (ibgib protocol provides authentication/integrity, not confidentiality)
152
+
153
+ ## Best Practices
154
+
155
+ 1. **Master Secret Protection**: Store master secrets in secure enclaves/keychains
156
+ 2. **Session Lifetime**: Limit saga duration, revoke sessions post-completion
157
+ 3. **Rate Limiting**: Implement per-identity sync rate limits
158
+ 4. **Freshness**: Reject frames older than 60 seconds
159
+ 5. **Audit Logs**: Persist saga timelines for post-hoc validation (saga timelines are ibgibs, so integrity is built-in via content addressing)
160
+ 6. **TLS**: Always use TLS for network transport (defense-in-depth)
161
+
162
+ ## ❓ Open Questions
163
+
164
+ ### Payload Scope Validation: Can the Protocol Police What ibGibs Are Transmitted?
165
+
166
+ **Question**: During a sync session authorized by session substone S (itself authorized by domain keystone I with `claim.target = S^Stjp` and sync subject `X^X10.Xtjp`), should the server validate that all Delta payload ibgibs are genuinely related to X's timeline? Or is it sufficient that they arrive under a valid substone?
167
+
168
+ **Context**: The substone's `frameDetails` records both the parent identity `I^Itjp` and the sync subject `X^X10.Xtjp`. In theory the server could walk the dependency graph of each incoming Delta payload and confirm every ibgib is either:
169
+ - A control ibgib (sync saga, context, substone frames), or
170
+ - Part of the dependency graph rooted at some frame of X's timeline (`X^Xtjp`)
171
+
172
+ **Problem**: This check may not be enforceable at the protocol layer. A client who controls the keystone (has the master secret) could always evolve X's timeline to hard-link any arbitrary ibgib, and that ibgib would then pass the "is in X's dependency graph" check. So the constraint can be trivially bypassed by the legitimate key-holder, making it only useful against *illegitimate* actors — who are already blocked by the keystone proof requirement.
173
+
174
+ **Current position**: This is likely a **higher-layer business rule** rather than a sync protocol concern. The sync protocol's job is to ensure the session is cryptographically authorized (via keystone proofs). What is stored under an authorized session is the domain owner's responsibility. Enforcement of content policies (e.g., "only ibgibs of type X are allowed") belongs in the server's `authorizeContext` hook, not in the core sync coordinator.
175
+
176
+ **Tracked in**: `space-gib.sync-walkthrough.md`