@ibgib/core-gib 0.1.59 → 0.1.61

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 (206) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/dist/common/other/graph-helper.d.mts.map +1 -1
  3. package/dist/common/other/graph-helper.mjs +7 -6
  4. package/dist/common/other/graph-helper.mjs.map +1 -1
  5. package/dist/keystone/keystone-config-builder.d.mts +1 -2
  6. package/dist/keystone/keystone-config-builder.d.mts.map +1 -1
  7. package/dist/keystone/keystone-config-builder.mjs +4 -19
  8. package/dist/keystone/keystone-config-builder.mjs.map +1 -1
  9. package/dist/keystone/keystone-constants.d.mts +2 -0
  10. package/dist/keystone/keystone-constants.d.mts.map +1 -1
  11. package/dist/keystone/keystone-constants.mjs +2 -0
  12. package/dist/keystone/keystone-constants.mjs.map +1 -1
  13. package/dist/keystone/keystone-helpers.d.mts +5 -15
  14. package/dist/keystone/keystone-helpers.d.mts.map +1 -1
  15. package/dist/keystone/keystone-helpers.mjs +59 -73
  16. package/dist/keystone/keystone-helpers.mjs.map +1 -1
  17. package/dist/keystone/keystone-policy-types.d.mts +6 -4
  18. package/dist/keystone/keystone-policy-types.d.mts.map +1 -1
  19. package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
  20. package/dist/keystone/keystone-service-v1.mjs +4 -8
  21. package/dist/keystone/keystone-service-v1.mjs.map +1 -1
  22. package/dist/keystone/keystone-service-v1.respec.mjs +182 -33
  23. package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
  24. package/dist/keystone/keystone-types.d.mts +4 -11
  25. package/dist/keystone/keystone-types.d.mts.map +1 -1
  26. package/dist/keystone/policy/keystone-profile-builder.d.mts +25 -0
  27. package/dist/keystone/policy/keystone-profile-builder.d.mts.map +1 -0
  28. package/dist/keystone/policy/keystone-profile-builder.mjs +117 -0
  29. package/dist/keystone/policy/keystone-profile-builder.mjs.map +1 -0
  30. package/dist/keystone/policy/profiles/profile-high.json +84 -0
  31. package/dist/keystone/policy/profiles/profile-low.json +84 -0
  32. package/dist/keystone/policy/profiles/profile-medium.json +84 -0
  33. package/dist/keystone/policy/profiles/profile-session.json +84 -0
  34. package/dist/keystone/policy/profiles/profile-test.json +84 -0
  35. package/dist/keystone/policy/schemas/connect-pool.high.schema.json +23 -0
  36. package/dist/keystone/policy/schemas/connect-pool.low.schema.json +23 -0
  37. package/dist/keystone/policy/schemas/connect-pool.medium.schema.json +23 -0
  38. package/dist/keystone/policy/schemas/keystone.high.schema.json +26 -0
  39. package/dist/keystone/policy/schemas/keystone.low.schema.json +26 -0
  40. package/dist/keystone/policy/schemas/keystone.medium.schema.json +26 -0
  41. package/dist/keystone/policy/schemas/manage-pool.high.schema.json +23 -0
  42. package/dist/keystone/policy/schemas/manage-pool.low.schema.json +23 -0
  43. package/dist/keystone/policy/schemas/manage-pool.medium.schema.json +23 -0
  44. package/dist/keystone/policy/schemas/revoke-pool.high.schema.json +23 -0
  45. package/dist/keystone/policy/schemas/revoke-pool.low.schema.json +23 -0
  46. package/dist/keystone/policy/schemas/revoke-pool.medium.schema.json +23 -0
  47. package/dist/keystone/policy/schemas/sign-pool.high.schema.json +23 -0
  48. package/dist/keystone/policy/schemas/sign-pool.low.schema.json +23 -0
  49. package/dist/keystone/policy/schemas/sign-pool.medium.schema.json +23 -0
  50. package/dist/keystone/policy/schemas/sync-pool.high.schema.json +23 -0
  51. package/dist/keystone/policy/schemas/sync-pool.low.schema.json +23 -0
  52. package/dist/keystone/policy/schemas/sync-pool.medium.schema.json +23 -0
  53. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.d.mts +8 -0
  54. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.d.mts.map +1 -0
  55. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs +582 -0
  56. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs.map +1 -0
  57. package/dist/sync/sync-conflict-basic-divergence.withid.respec.d.mts +7 -0
  58. package/dist/sync/sync-conflict-basic-divergence.withid.respec.d.mts.map +1 -0
  59. package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs +274 -0
  60. package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs.map +1 -0
  61. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.d.mts +8 -0
  62. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.d.mts.map +1 -0
  63. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs +322 -0
  64. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs.map +1 -0
  65. package/dist/sync/sync-conflict-text-merge.withid.respec.d.mts +7 -0
  66. package/dist/sync/sync-conflict-text-merge.withid.respec.d.mts.map +1 -0
  67. package/dist/sync/sync-conflict-text-merge.withid.respec.mjs +307 -0
  68. package/dist/sync/sync-conflict-text-merge.withid.respec.mjs.map +1 -0
  69. package/dist/sync/sync-innerspace-constants.withid.respec.d.mts +8 -0
  70. package/dist/sync/sync-innerspace-constants.withid.respec.d.mts.map +1 -0
  71. package/dist/sync/sync-innerspace-constants.withid.respec.mjs +233 -0
  72. package/dist/sync/sync-innerspace-constants.withid.respec.mjs.map +1 -0
  73. package/dist/sync/sync-innerspace-deep-updates.withid.respec.d.mts +7 -0
  74. package/dist/sync/sync-innerspace-deep-updates.withid.respec.d.mts.map +1 -0
  75. package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs +233 -0
  76. package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs.map +1 -0
  77. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.d.mts +7 -0
  78. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.d.mts.map +1 -0
  79. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs +225 -0
  80. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs.map +1 -0
  81. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  82. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.d.mts +7 -0
  83. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.d.mts.map +1 -0
  84. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs +235 -0
  85. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs.map +1 -0
  86. package/dist/sync/sync-innerspace-partial-update.withid.respec.d.mts +7 -0
  87. package/dist/sync/sync-innerspace-partial-update.withid.respec.d.mts.map +1 -0
  88. package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs +211 -0
  89. package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs.map +1 -0
  90. package/dist/sync/sync-innerspace.withid.respec.d.mts +8 -0
  91. package/dist/sync/sync-innerspace.withid.respec.d.mts.map +1 -0
  92. package/dist/sync/sync-innerspace.withid.respec.mjs +260 -0
  93. package/dist/sync/sync-innerspace.withid.respec.mjs.map +1 -0
  94. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +1 -1
  95. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  96. package/dist/sync/sync-peer/sync-peer-types.d.mts +12 -1
  97. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  98. package/dist/sync/sync-peer/sync-peer-v1.d.mts +7 -0
  99. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  100. package/dist/sync/sync-peer/sync-peer-v1.mjs +43 -1
  101. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  102. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts +2 -0
  103. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts.map +1 -1
  104. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs +28 -9
  105. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs.map +1 -1
  106. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +1 -1
  107. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -1
  108. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +5 -3
  109. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
  110. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts +16 -0
  111. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts.map +1 -1
  112. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs +223 -79
  113. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs.map +1 -1
  114. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  115. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +41 -2
  116. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  117. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +4 -0
  118. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
  119. package/dist/sync/sync-saga-coordinator.d.mts +6 -0
  120. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  121. package/dist/sync/sync-saga-coordinator.mjs +213 -137
  122. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  123. package/dist/sync/sync-withid.pingpong.respec.mjs +70 -2
  124. package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -1
  125. package/dist/timeline/timeline-api.d.mts.map +1 -1
  126. package/dist/timeline/timeline-api.mjs +1 -14
  127. package/dist/timeline/timeline-api.mjs.map +1 -1
  128. package/dist/witness/space/reconciliation-space/reconciliation-space-base.d.mts.map +1 -1
  129. package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs +0 -40
  130. package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs.map +1 -1
  131. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.d.mts.map +1 -1
  132. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs +6 -6
  133. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs.map +1 -1
  134. package/ibgib-foundations.md +2 -1
  135. package/package.json +1 -1
  136. package/src/common/other/graph-helper.mts +7 -6
  137. package/src/keystone/README.md +19 -15
  138. package/src/keystone/docs/CRYPTANALYSIS.md +185 -0
  139. package/src/keystone/docs/architecture.md +24 -1
  140. package/src/keystone/docs/profiles.md +124 -0
  141. package/src/keystone/keystone-config-builder.mts +3 -21
  142. package/src/keystone/keystone-constants.mts +2 -0
  143. package/src/keystone/keystone-helpers.mts +74 -77
  144. package/src/keystone/keystone-policy-types.mts +6 -4
  145. package/src/keystone/keystone-service-v1.mts +4 -9
  146. package/src/keystone/keystone-service-v1.respec.mts +214 -32
  147. package/src/keystone/keystone-types.mts +5 -9
  148. package/src/keystone/policy/IMPLEMENTATION.md +60 -0
  149. package/src/keystone/policy/README.md +94 -0
  150. package/src/keystone/policy/keystone-profile-builder.mts +131 -0
  151. package/src/keystone/policy/profiles/profile-high.json +84 -0
  152. package/src/keystone/policy/profiles/profile-low.json +84 -0
  153. package/src/keystone/policy/profiles/profile-medium.json +84 -0
  154. package/src/keystone/policy/profiles/profile-session.json +84 -0
  155. package/src/keystone/policy/profiles/profile-test.json +84 -0
  156. package/src/keystone/policy/schemas/connect-pool.high.schema.json +23 -0
  157. package/src/keystone/policy/schemas/connect-pool.low.schema.json +23 -0
  158. package/src/keystone/policy/schemas/connect-pool.medium.schema.json +23 -0
  159. package/src/keystone/policy/schemas/keystone.high.schema.json +26 -0
  160. package/src/keystone/policy/schemas/keystone.low.schema.json +26 -0
  161. package/src/keystone/policy/schemas/keystone.medium.schema.json +26 -0
  162. package/src/keystone/policy/schemas/manage-pool.high.schema.json +23 -0
  163. package/src/keystone/policy/schemas/manage-pool.low.schema.json +23 -0
  164. package/src/keystone/policy/schemas/manage-pool.medium.schema.json +23 -0
  165. package/src/keystone/policy/schemas/revoke-pool.high.schema.json +23 -0
  166. package/src/keystone/policy/schemas/revoke-pool.low.schema.json +23 -0
  167. package/src/keystone/policy/schemas/revoke-pool.medium.schema.json +23 -0
  168. package/src/keystone/policy/schemas/sign-pool.high.schema.json +23 -0
  169. package/src/keystone/policy/schemas/sign-pool.low.schema.json +23 -0
  170. package/src/keystone/policy/schemas/sign-pool.medium.schema.json +23 -0
  171. package/src/keystone/policy/schemas/sync-pool.high.schema.json +23 -0
  172. package/src/keystone/policy/schemas/sync-pool.low.schema.json +23 -0
  173. package/src/keystone/policy/schemas/sync-pool.medium.schema.json +23 -0
  174. package/src/sync/docs/security-3b.md +92 -0
  175. package/src/sync/docs/security.md +139 -38
  176. package/src/sync/sync-conflict-adv-multitimelines.withid.respec.mts +706 -0
  177. package/src/sync/sync-conflict-basic-divergence.withid.respec.mts +321 -0
  178. package/src/sync/sync-conflict-basic-multitimelines.withid.respec.mts +374 -0
  179. package/src/sync/sync-conflict-text-merge.withid.respec.mts +347 -0
  180. package/src/sync/sync-innerspace-constants.withid.respec.mts +264 -0
  181. package/src/sync/sync-innerspace-deep-updates.withid.respec.mts +272 -0
  182. package/src/sync/sync-innerspace-dest-ahead.withid.respec.mts +266 -0
  183. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +0 -1
  184. package/src/sync/sync-innerspace-multiple-timelines.withid.respec.mts +273 -0
  185. package/src/sync/sync-innerspace-partial-update.withid.respec.mts +249 -0
  186. package/src/sync/sync-innerspace.withid.respec.mts +265 -0
  187. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +1 -1
  188. package/src/sync/sync-peer/sync-peer-types.mts +11 -1
  189. package/src/sync/sync-peer/sync-peer-v1.mts +47 -1
  190. package/src/sync/sync-peer/sync-peer-websocket/README.md +42 -0
  191. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mts +26 -9
  192. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +5 -3
  193. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mts +242 -78
  194. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +46 -4
  195. package/src/sync/sync-saga-context/sync-saga-context-types.mts +5 -0
  196. package/src/sync/sync-saga-coordinator.mts +221 -137
  197. package/src/sync/sync-withid.pingpong.respec.mts +76 -3
  198. package/src/timeline/timeline-api.mts +1 -15
  199. package/src/timeline/timeline-api.respec.mts +3 -3
  200. package/src/witness/space/reconciliation-space/reconciliation-space-base.mts +0 -46
  201. package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts +6 -5
  202. package/tsconfig.json +6 -1
  203. package/src/keystone/keystone-policy.schema.json +0 -51
  204. package/src/sync/docs/ping_pong_plan.md +0 -147
  205. package/src/witness/space/reconciliation-space/reconciliation-space-base.mts.OLD.md +0 -884
  206. package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts.OLD.md +0 -125
@@ -0,0 +1,265 @@
1
+ /**
2
+ * @module sync-innerspace.withid.respec
3
+ *
4
+ * Verifies SyncSagaCoordinator using InnerSpace (in-memory/local) spaces with Identity.
5
+ * This utilizes SyncPeerInnerspace_V1 for local-to-local synchronization simulations.
6
+ */
7
+
8
+ import {
9
+ respecfully, lastOfAll, ifWe, iReckon,
10
+ ifWeMight
11
+ } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
12
+ const maam = `[${import.meta.url}]`, sir = maam;
13
+ import { clone, delay, extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
14
+ import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
15
+ import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
16
+
17
+ import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
18
+ import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
19
+ import { getFromSpace } from '../witness/space/space-helper.mjs';
20
+ import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
21
+ import { getDependencyGraph } from '../common/other/graph-helper.mjs';
22
+ import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
23
+ import { createTimelineRootTestHelper, } from '../test-helpers.mjs';
24
+ import { mut8Timeline, } from '../timeline/timeline-api.mjs';
25
+ import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-space-types.mjs';
26
+ import { toDto } from '../common/other/ibgib-helper.mjs';
27
+ import { fnObs } from '../common/pubsub/observer/observer-helper.mjs';
28
+ import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
29
+ import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
30
+
31
+ import { KeystoneService_V1 } from '../keystone/keystone-service-v1.mjs';
32
+ import {
33
+ KEYSTONE_VERB_SYNC, POOL_ID_SYNC,
34
+ } from '../keystone/keystone-constants.mjs';
35
+ import { createStandardPoolConfig } from '../keystone/keystone-config-builder.mjs';
36
+ import { KeystoneReplenishStrategy } from '../keystone/keystone-types.mjs';
37
+ import { SyncConflictStrategy } from './sync-constants.mjs';
38
+
39
+ const logalot = GLOBAL_LOG_A_LOT;
40
+ const lc = `[sync-innerspace.withid.respec]`;
41
+
42
+ const SENDER_SECRET = 'test-sender-secret-innerspace-withid';
43
+
44
+ const SENDER_IDENTITY_SYNC_POOL_CONFIG = createStandardPoolConfig({
45
+ id: POOL_ID_SYNC,
46
+ salt: 'senderidentitysyncsaltinnerspacewithid',
47
+ verbs: [KEYSTONE_VERB_SYNC],
48
+ size: 200,
49
+ sequential: 1,
50
+ random: 1,
51
+ targetBinding: 2,
52
+ replenishStrategy: KeystoneReplenishStrategy.topUp,
53
+ });
54
+
55
+ await respecfully(sir, `Sync InnerSpaces with Identity`, async () => {
56
+
57
+ let metaspace: Metaspace_Innerspace;
58
+ let sourceSpace: InnerSpace_V1;
59
+ let destSpace: InnerSpace_V1;
60
+
61
+ // #region Round 1 - Push timeline
62
+ await respecfully(sir, `Basic Push Sync with Identity (Source -> Dest)`, async () => {
63
+ // 1. Setup Spaces
64
+ metaspace = new Metaspace_Innerspace(undefined);
65
+ await metaspace.initialize({
66
+ getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
67
+ getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
68
+ getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
69
+ });
70
+ while (!metaspace.initialized) { await delay(10); }
71
+
72
+ const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
73
+ if (!defaultLocalUserSpace) { throw new Error(`(UNEXPECTED) defaultLocalUserSpace falsy? (E: 87da738ca09e8fcabde916298126)`); }
74
+ await defaultLocalUserSpace.initialized;
75
+ if (logalot) { console.log(`defaultLocalUserSpace: ${pretty(toDto({ ibGib: defaultLocalUserSpace }))}`); }
76
+
77
+ sourceSpace = new InnerSpace_V1({
78
+ ...DEFAULT_INNER_SPACE_DATA_V1,
79
+ name: 'source',
80
+ uuid: 'source_uuid',
81
+ description: 'source test space',
82
+ });
83
+ await sourceSpace.initialized;
84
+ if (logalot) { console.log(`sourceSpace: ${pretty(toDto({ ibGib: sourceSpace }))}`); }
85
+
86
+ destSpace = new InnerSpace_V1({
87
+ ...DEFAULT_INNER_SPACE_DATA_V1,
88
+ name: 'dest',
89
+ uuid: 'dest_uuid',
90
+ description: 'dest test space',
91
+ });
92
+ await destSpace.initialized;
93
+ if (logalot) { console.log(`destSpace: ${pretty(toDto({ ibGib: destSpace }))}`); }
94
+
95
+ const keystoneSvc = new KeystoneService_V1();
96
+ const senderIdentity = await keystoneSvc.genesis({
97
+ masterSecret: SENDER_SECRET,
98
+ configs: [SENDER_IDENTITY_SYNC_POOL_CONFIG],
99
+ metaspace,
100
+ space: sourceSpace,
101
+ });
102
+
103
+ // post the senderIdentity to receiver
104
+ await metaspace.put({ ibGib: senderIdentity, space: destSpace });
105
+ await metaspace.registerNewIbGib({ ibGib: senderIdentity, space: destSpace });
106
+
107
+ // #region r1 setup
108
+ // 2. Seed Source Data
109
+ if (logalot) { console.log(`${lc} Creating data in Source...`); }
110
+ const r1_alpha_v0_source = await createTimelineRootTestHelper({
111
+ ib: 'timeline_root',
112
+ data: { type: 'root', },
113
+ space: sourceSpace,
114
+ });
115
+
116
+ const r1_alpha_v1_source = await mut8Timeline({
117
+ timeline: r1_alpha_v0_source,
118
+ mut8Opts: {
119
+ dataToAddOrPatch: { type: 'child', },
120
+ },
121
+ metaspace,
122
+ space: sourceSpace,
123
+ });
124
+ const r1_alpha_v1_source_addr = getIbGibAddr({ ibGib: r1_alpha_v1_source });
125
+ // #endregion r1 setup
126
+
127
+ // 3. Setup Coordinators & Peer
128
+ if (logalot) { console.log(`${lc} Setting up Coordinators...`); }
129
+
130
+ // Sender Coordinator
131
+ const senderCoordinator = new SyncSagaCoordinator();
132
+
133
+ // Receiver Coordinator
134
+ const receiverCoordinator = new SyncSagaCoordinator();
135
+
136
+ // Peer (The "Network")
137
+ const peer = new SyncPeerInnerspace_V1(clone(SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1));
138
+ await peer.initialized;
139
+ await peer.initializeOpts({
140
+ sagaId: '',
141
+ localMetaspace: metaspace,
142
+ localSpace: sourceSpace, // "Client"
143
+ receiverSpace: destSpace, // "Server"
144
+ receiverCoordinator,
145
+ receiverMetaspace: metaspace,
146
+ });
147
+
148
+ // 4. Run Sync
149
+ if (logalot) { console.log(`${lc} Running Sync...`); }
150
+ const resSync = await senderCoordinator.sync({
151
+ peer: peer,
152
+ localSpace: sourceSpace,
153
+ metaspace: metaspace,
154
+ domainIbGibs: [r1_alpha_v1_source], // Sync the child
155
+ senderIdentity,
156
+ fnSenderSecret: async () => SENDER_SECRET,
157
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
158
+ });
159
+
160
+ if (logalot) { console.log(`${lc} resSync: ${pretty(resSync)}`); }
161
+
162
+ const sublc = `${lc}[updates$]`;
163
+ const subscription = await resSync.updates$.subscribe(fnObs({
164
+ next: async (ctxIbGib) => {
165
+ if (logalot) { console.log(`${sublc} next fired`); }
166
+ },
167
+ error: async (e) => {
168
+ if (logalot) { console.log(`${sublc} error fired`); }
169
+ },
170
+ complete: async () => {
171
+ if (logalot) { console.log(`${sublc} complete fired`); }
172
+ },
173
+ }));
174
+
175
+ if (logalot) { console.log(`${lc} awaiting resSync.done`) }
176
+ await resSync.done;
177
+
178
+ // #region r1 verify post
179
+ // 5. Verify Dest
180
+ if (logalot) { console.log(`${lc} Verifying Destination...`); }
181
+ try {
182
+ const getChildInDest = await getFromSpace({ space: destSpace, addr: r1_alpha_v1_source_addr });
183
+
184
+ await ifWe(sir, `verify success getChildInDest`, async () => {
185
+ iReckon(sir, getChildInDest.success).asTo('Child present in Dest').isGonnaBeTrue();
186
+ });
187
+
188
+ await ifWe(sir, `verify getChildInDest.ibGibs`, async () => {
189
+ const firstChild = getChildInDest.ibGibs?.[0];
190
+ if (logalot) { console.log(`${lc} firstChild: ${pretty(firstChild)}`); }
191
+ iReckon(sir, firstChild?.data?.n).asTo('Child content matches').isGonnaBe(2);
192
+ });
193
+
194
+ await ifWe(sir, 'sessionIdentity evolved the expected number of times', async () => {
195
+ const latestS = peer.currentSessionIdentity;
196
+ iReckon(sir, latestS).asTo('latest session identity exists on peer').isGonnaBeTruthy();
197
+ if (latestS) {
198
+ iReckon(sir, latestS.data?.n).asTo('session identity data.n').isGonnaBe(2);
199
+ }
200
+ });
201
+
202
+ await ifWe(sir, 'sender durable space has I (evolved frame)', async () => {
203
+ const latestSenderIdentityAddr = await metaspace.getLatestAddr({
204
+ addr: getIbGibAddr({ ibGib: senderIdentity }),
205
+ space: sourceSpace,
206
+ });
207
+ iReckon(sir, latestSenderIdentityAddr).asTo('latestSenderIdentityAddr exists').isGonnaBeTruthy();
208
+ if (latestSenderIdentityAddr) {
209
+ const resGetSourceI = await getFromSpace({ space: sourceSpace, addr: latestSenderIdentityAddr });
210
+ iReckon(sir, resGetSourceI.success && resGetSourceI.ibGibs?.length === 1).asTo('evolved I exists in sourceSpace').isGonnaBeTrue();
211
+ if (resGetSourceI.success && resGetSourceI.ibGibs?.[0]) {
212
+ const evolvedI = resGetSourceI.ibGibs[0];
213
+ iReckon(sir, evolvedI.data?.n).asTo('evolved I data.n').isGonnaBe(1);
214
+ }
215
+ }
216
+ });
217
+
218
+ await ifWe(sir, 'receiver durable space has I (evolved frame)', async () => {
219
+ const latestSenderIdentityAddr = await metaspace.getLatestAddr({
220
+ addr: getIbGibAddr({ ibGib: senderIdentity }),
221
+ space: sourceSpace,
222
+ });
223
+ iReckon(sir, latestSenderIdentityAddr).asTo('latestSenderIdentityAddr exists').isGonnaBeTruthy();
224
+ if (latestSenderIdentityAddr) {
225
+ const resGetDestI = await getFromSpace({ space: destSpace, addr: latestSenderIdentityAddr });
226
+ iReckon(sir, resGetDestI.success && resGetDestI.ibGibs?.length === 1).asTo('evolved I exists in destSpace').isGonnaBeTrue();
227
+ if (resGetDestI.success && resGetDestI.ibGibs?.[0]) {
228
+ const evolvedI = resGetDestI.ibGibs[0];
229
+ iReckon(sir, evolvedI.data?.n).asTo('evolved I data.n').isGonnaBe(1);
230
+ }
231
+ }
232
+ });
233
+
234
+ await ifWe(sir, 'sender durable space has full S dep graph', async () => {
235
+ const latestS = peer.currentSessionIdentity;
236
+ iReckon(sir, latestS).asTo('latestS exists').isGonnaBeTruthy();
237
+ if (latestS) {
238
+ const depGraphS = await getDependencyGraph({ ibGib: latestS, space: sourceSpace });
239
+ const addrs = Object.keys(depGraphS);
240
+ iReckon(sir, addrs.length > 2).asTo('number of nodes in S graph > 2').isGonnaBeTrue();
241
+ }
242
+ });
243
+
244
+ await ifWe(sir, 'receiver durable space has full S dep graph', async () => {
245
+ const latestS = peer.currentSessionIdentity;
246
+ iReckon(sir, latestS).asTo('latestS exists').isGonnaBeTruthy();
247
+ if (latestS) {
248
+ const depGraphS = await getDependencyGraph({ ibGib: latestS, space: destSpace });
249
+ const addrs = Object.keys(depGraphS);
250
+ iReckon(sir, addrs.length > 2).asTo('number of nodes in S graph in destSpace > 2').isGonnaBeTrue();
251
+ }
252
+ });
253
+
254
+ } catch (error) {
255
+ await ifWe(sir, `doh`, async () => {
256
+ iReckon(sir, true).asTo(`error: ${extractErrorMsg(error)}`).isGonnaBeFalse();
257
+ });
258
+ } finally {
259
+ if (logalot) { console.log(`${lc} Test complete.`); }
260
+ }
261
+ // #endregion r1 verify post
262
+ });
263
+ // #endregion Round 1 - Push timeline
264
+
265
+ });
@@ -325,7 +325,7 @@ export class SyncPeerInnerspace_V1 extends SyncPeer_V1<ConnectSyncPeerInnerspace
325
325
  if (!msgResponse.data) { throw new Error(`(UNEXPECTED) sync saga message ibgib.data falsy? (E: 61ec18743988ad3cbab2072d1dd69826)`); }
326
326
 
327
327
  const responsePayloadIbGibsControl = [
328
- msgResponse, responseCtx.sagaFrame, context
328
+ msgResponse, responseCtx.sagaFrame, responseCtx
329
329
  ].map(x => toDto({ ibGib: x }));
330
330
  // ...put into sender's durable space
331
331
  await putInSpace({
@@ -198,7 +198,17 @@ export interface SyncPeerWitness<TInitializeOpts extends InitializeSyncPeerOpts
198
198
  // getSenderIdentity(): KeystoneIbGib_V1 | undefined;
199
199
 
200
200
  /**
201
- * Evolves the session identity (S_n -> S_n+1) and signs targeting contextAddr.
201
+ * Evolves the session identity (S_n -> S_n+1) and signs targeting
202
+ * contextAddr for the **sync** verb. This is in contrast to signing for
203
+ * the "connect" verb, for which you use {@link signContextConnect}
202
204
  */
203
205
  signContext(opts: { contextAddr: IbGibAddr }): Promise<KeystoneIbGib_V1 | undefined>;
206
+
207
+ /**
208
+ * Evolves the session identity (S_n -> S_n+1) solving the demanded connect
209
+ * challenges. This is NOT for signing session when doing the ping pong of
210
+ * sync protocol. For that, you use {@link signContext}
211
+ */
212
+ signContextConnect(opts: { challengeUuid: string, demandedIds: string[] }): Promise<KeystoneIbGib_V1 | undefined>;
213
+
204
214
  }
@@ -23,7 +23,7 @@ import { getFullSyncSagaHistory, deriveSessionSecret } from '../sync-helpers.mjs
23
23
  import { SessionGenesisFrameDetails, SyncSagaFrameDependencyGraph } from '../sync-types.mjs';
24
24
  import { KeystoneService_V1 } from '../../keystone/keystone-service-v1.mjs';
25
25
  import { KeystoneIbGib_V1 } from '../../keystone/keystone-types.mjs';
26
- import { KEYSTONE_VERB_SYNC, } from '../../keystone/keystone-constants.mjs';
26
+ import { KEYSTONE_VERB_CONNECT, KEYSTONE_VERB_SYNC, POOL_ID_CONNECT, } from '../../keystone/keystone-constants.mjs';
27
27
  import { getTjpAddr } from '../../common/other/ibgib-helper.mjs';
28
28
  import { IbGibAddr } from '@ibgib/ts-gib/dist/types.mjs';
29
29
 
@@ -99,6 +99,52 @@ export abstract class SyncPeer_V1<
99
99
  }
100
100
  }
101
101
 
102
+ /**
103
+ * Evolves the session identity (S_n -> S_n+1) solving the demanded connect challenges.
104
+ */
105
+ public async signContextConnect({
106
+ challengeUuid,
107
+ demandedIds,
108
+ }: {
109
+ challengeUuid: string;
110
+ demandedIds: string[];
111
+ }): Promise<KeystoneIbGib_V1 | undefined> {
112
+ const lc = `${this.lc}[${this.signContextConnect.name}]`;
113
+ try {
114
+ if (!this.currentSessionIdentity) {
115
+ return undefined;
116
+ }
117
+ if (!this.opts) { throw new Error(`opts not initialized. (E: bcf5978aed789b0ebcbdc51971ebe826)`); }
118
+ const { fnSenderSecret, sagaId, localMetaspace, localSpace } = this.opts;
119
+ if (!fnSenderSecret) { throw new Error(`fnSenderSecret not initialized. (E: 207fd292a2e8c53c05fd0a74a4ae6d26)`); }
120
+ if (!sagaId) { throw new Error(`sagaId not initialized. (E: f2e35cc13ed873b638116188119d1826)`); }
121
+
122
+ const senderSecret = await fnSenderSecret();
123
+ const sessionSecret = await deriveSessionSecret({ senderSecret, sagaId });
124
+
125
+ const keystoneSvc = new KeystoneService_V1();
126
+ const evolved = await keystoneSvc.sign({
127
+ latestKeystone: this.currentSessionIdentity,
128
+ masterSecret: sessionSecret,
129
+ poolId: POOL_ID_CONNECT,
130
+ requiredChallengeIds: demandedIds,
131
+ claim: {
132
+ verb: KEYSTONE_VERB_CONNECT,
133
+ target: challengeUuid,
134
+ },
135
+ metaspace: localMetaspace,
136
+ space: localSpace,
137
+ });
138
+
139
+ this.currentSessionIdentity = evolved;
140
+ return evolved;
141
+ } catch (error) {
142
+ console.error(`${lc} ${extractErrorMsg(error)}`);
143
+ throw error;
144
+ }
145
+ }
146
+
147
+
102
148
  get classname(): string {
103
149
  if (!this.data) { throw new Error(`(UNEXPECTED) this.data falsy? (E: 1ab1841e9338b54f3aa615fa37024826)`); }
104
150
  if (!this.data.classname) { throw new Error(`invalid peer. this.data.classname is falsy (E: b0ee28a0abb84a06588d9de7afcef826)`); }
@@ -0,0 +1,42 @@
1
+ # WebSocket Sync Peers
2
+
3
+ This directory contains the WebSocket client (sender) and server (receiver) peer implementations.
4
+
5
+ ## WebSocket Resource Management & Teardown
6
+
7
+ Since WebSockets manage active OS-level TCP connections, they must be cleanly closed to prevent socket descriptor leaks on the client and resource exhaustion (DoS) on the server.
8
+
9
+ ### Analysis (2026-06-12T09:45:21-05:00)
10
+
11
+ Our analysis highlights several areas where WebSocket connections are currently left open:
12
+
13
+ 1. **Sender Connect Failures**: If connection handshake fails or times out, the client rejects the promise but does not close the socket.
14
+ 2. **Sender Runtime Failures**: If runtime message validation fails, we throw an error but leave the socket open.
15
+ 3. **Sender Saga Completion**: When the sync loop finishes successfully, the socket is left open indefinitely because there is no `disconnect` method.
16
+ 4. **Receiver Message Handling Failures**: If incoming messages fail validation/authentication, the server sends a `sync_error` or `auth_fail` frame, but relies on the client to close the connection. A buggy/malicious client can leave it open forever.
17
+
18
+ #### Design Questions & Ideas
19
+ * **Idempotent `disconnect()`**: The disconnect method on the sender should clear `this.handshakeMessageListener` and set it to `undefined` so that multiple calls to `disconnect()` are safe.
20
+ * **Reuse `disconnect()`**: In connection failure handlers (like `handleHandshakeAuthFail`), we can call `this.disconnect()` to ensure uniform cleanup.
21
+ * **Server-side Close on Error (best-effort `send`)**: If a message fails, we want to notify the client first and then close the connection. If the `send()` itself throws, we want a safe cleanup. In JavaScript, we can nest `try..catch..finally` inside a `catch` block, or use a short `setTimeout` to push the socket close operation to the next event loop tick, ensuring the outgoing frame is written before the socket is terminated.
22
+
23
+ ---
24
+
25
+ ### Implementation TODOs
26
+
27
+ - [x] **Implement `disconnect` method on `SyncPeerWebSocketSender_V1`**
28
+ - Implement a synchronous or asynchronous `disconnect()` method.
29
+ - Safely remove the message event listener `this.handshakeMessageListener`.
30
+ - Set `this.handshakeMessageListener = undefined` to make it idempotent.
31
+ - Close the WebSocket `this.ws.close()`.
32
+
33
+ - [x] **Clean up socket on Sender connection failures**
34
+ - Call `this.disconnect()` inside `handleHandshakeAuthFail`.
35
+ - Call `this.disconnect()` inside `handleHandshakeSyncError`.
36
+
37
+ - [x] **Close socket on Sender runtime message validation failures**
38
+ - In `handleRuntimeMessage` and `handleRuntimeSyncFrameResponse`, catch validation errors, trigger `this.disconnect()`, and rethrow.
39
+
40
+ - [x] **Close socket on Receiver message handling failures**
41
+ - In the catch block of `handleIncomingMessage`, call `this.socketWrapper?.close()` (or equivalent wrapper cleanup) to force-close connections after sending error frames.
42
+ - Wrap the `send()` call in a nested try/catch or use `setTimeout` to ensure best-effort message transmission before terminating the socket wrapper.
@@ -34,6 +34,7 @@ export interface IWebSocketWrapper {
34
34
  send(data: string): void;
35
35
  onMessage(callback: (data: string) => void): void;
36
36
  onClose(callback: () => void): void;
37
+ close(): void;
37
38
  }
38
39
 
39
40
  /**
@@ -67,6 +68,7 @@ export class SyncPeerWebSocketReceiver_V1
67
68
  protected pendingContext?: SyncSagaContextIbGib_V1;
68
69
  protected pendingPayloadAddrs?: Set<string>;
69
70
  protected pendingResponsePayloadsToSend: IbGib_V1[] = [];
71
+ protected receivedPayloads: IbGib_V1[] = [];
70
72
 
71
73
  constructor(
72
74
  initialData: SyncPeerWebSocketReceiverData_V1,
@@ -188,16 +190,20 @@ export class SyncPeerWebSocketReceiver_V1
188
190
  throw new Error(`controlIbGibs invalid intrinsically. validationErrors: ${validationErrors.join('|')} (E: 5ee1787d4cc53d3d2c55f3d4f2865226)`);
189
191
  }
190
192
 
191
- if (this.pendingContext && this.pendingPayloadAddrs) {
193
+ const pendingPayloadAddrs = this.pendingPayloadAddrs;
194
+ if (this.pendingContext && pendingPayloadAddrs) {
192
195
  const addr = getIbGibAddr({ ibGib });
193
- if (this.pendingPayloadAddrs.has(addr)) {
196
+ if (pendingPayloadAddrs.has(addr)) {
194
197
  const tempSpace = await this.ensureLocalTempSpace();
195
198
  await putInSpace({ space: tempSpace, ibGibs: [ibGib] });
196
- this.pendingPayloadAddrs.delete(addr);
199
+ this.receivedPayloads.push(ibGib);
200
+ pendingPayloadAddrs.delete(addr);
197
201
 
198
202
  // If all expected payloads for the pending context are received
199
- if (this.pendingPayloadAddrs.size === 0) {
203
+ if (pendingPayloadAddrs.size === 0 && this.pendingPayloadAddrs !== undefined) {
200
204
  const context = this.pendingContext;
205
+ context.payloadIbGibsDomain = this.receivedPayloads;
206
+ this.receivedPayloads = [];
201
207
  this.pendingContext = undefined;
202
208
  this.pendingPayloadAddrs = undefined;
203
209
 
@@ -213,7 +219,8 @@ export class SyncPeerWebSocketReceiver_V1
213
219
  // First, validate and authenticate the context
214
220
  const allControlIbGibs: IbGib_V1[] = [
215
221
  toDto({ ibGib: context }),
216
- context.sagaFrame
222
+ context.sagaFrame,
223
+ context.sagaFrameMsg
217
224
  ];
218
225
  if (context.signedSessionIdentity) {
219
226
  allControlIbGibs.push(context.signedSessionIdentity);
@@ -241,6 +248,7 @@ export class SyncPeerWebSocketReceiver_V1
241
248
  // Set up wait state and request payloads
242
249
  this.pendingContext = context;
243
250
  this.pendingPayloadAddrs = new Set(expectedPayloadAddrs);
251
+ this.receivedPayloads = [];
244
252
  this.socketWrapper.send(JSON.stringify({
245
253
  type: SyncWebSocketMsgType.sync_frame_authenticated,
246
254
  contextAddr: getIbGibAddr({ ibGib: context })
@@ -259,13 +267,21 @@ export class SyncPeerWebSocketReceiver_V1
259
267
  ibGib
260
268
  }));
261
269
  }
270
+ } else {
271
+ throw new Error(`(UNEXPECTED) msg.type is ${msg.type}? not expected at this time (E: bad7c4fa1958b1976904cb884e624826)`);
262
272
  }
263
273
  } catch (error) {
264
274
  console.error(`${lc} message frame handling failed: ${extractErrorMsg(error)}`);
265
- this.socketWrapper?.send(JSON.stringify({
266
- type: this.isAuthenticated ? SyncWebSocketMsgType.sync_error : SyncWebSocketMsgType.auth_fail,
267
- message: extractErrorMsg(error)
268
- }));
275
+ try {
276
+ this.socketWrapper?.send(JSON.stringify({
277
+ type: this.isAuthenticated ? SyncWebSocketMsgType.sync_error : SyncWebSocketMsgType.auth_fail,
278
+ message: extractErrorMsg(error)
279
+ }));
280
+ } catch (nestedError) {
281
+ console.error(`${lc}[nested catch] failed to send error frame: ${extractErrorMsg(nestedError)}`);
282
+ } finally {
283
+ this.socketWrapper?.close();
284
+ }
269
285
  }
270
286
  }
271
287
 
@@ -327,6 +343,7 @@ export class SyncPeerWebSocketReceiver_V1
327
343
 
328
344
  // Persist the newly validated evolved session keystone tip
329
345
  await metaspace.put({ ibGibs: [proofFrame], space });
346
+ await metaspace.registerNewIbGib({ ibGib: proofFrame, space });
330
347
 
331
348
  if (logalot) { console.log(`${lc} connect validation successful! Connection upgraded to active sync session.`); }
332
349
  this.isAuthenticated = true;
@@ -41,7 +41,7 @@ export const SESSION_KEYSTONE_POLICY = {
41
41
  SIZE: 10,
42
42
  SELECT_SEQUENTIALLY: 2,
43
43
  SELECT_RANDOMLY: 2,
44
- TARGET_BINDING_CHARS: 0,
44
+ TARGET_BINDING_COUNT: 0,
45
45
  SERVER_DEMAND_COUNT: 3,
46
46
  }
47
47
  } as const;
@@ -298,8 +298,10 @@ export async function validateAndRegisterEvolveKeystone({
298
298
  }
299
299
 
300
300
  // 2. Verify URL :domainAddr matches the keystone's tjp
301
- const { tjpGib } = getGibInfo({ ibGibAddr: addr });
302
- const expectedTjpGib = getGibInfo({ ibGibAddr: domainAddr }).punctiliarHash;
301
+ const infoAddr = getGibInfo({ ibGibAddr: addr });
302
+ const tjpGib = infoAddr.tjpGib ?? infoAddr.punctiliarHash;
303
+ const infoDomain = getGibInfo({ ibGibAddr: domainAddr });
304
+ const expectedTjpGib = infoDomain.tjpGib ?? infoDomain.punctiliarHash;
303
305
  if (tjpGib !== expectedTjpGib) {
304
306
  throw new Error(`Keystone tjpGib (${tjpGib}) does not match URL domainAddr tjpGib (${expectedTjpGib})`);
305
307
  }