@ibgib/core-gib 0.1.57 → 0.1.59

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 (162) hide show
  1. package/dist/keystone/keystone-config-builder.d.mts +12 -1
  2. package/dist/keystone/keystone-config-builder.d.mts.map +1 -1
  3. package/dist/keystone/keystone-config-builder.mjs +58 -4
  4. package/dist/keystone/keystone-config-builder.mjs.map +1 -1
  5. package/dist/keystone/keystone-constants.d.mts +40 -5
  6. package/dist/keystone/keystone-constants.d.mts.map +1 -1
  7. package/dist/keystone/keystone-constants.mjs +39 -5
  8. package/dist/keystone/keystone-constants.mjs.map +1 -1
  9. package/dist/keystone/keystone-helpers.d.mts +11 -1
  10. package/dist/keystone/keystone-helpers.d.mts.map +1 -1
  11. package/dist/keystone/keystone-helpers.mjs +37 -1
  12. package/dist/keystone/keystone-helpers.mjs.map +1 -1
  13. package/dist/keystone/keystone-policy-types.d.mts +23 -0
  14. package/dist/keystone/keystone-policy-types.d.mts.map +1 -0
  15. package/dist/keystone/keystone-policy-types.mjs +2 -0
  16. package/dist/keystone/keystone-policy-types.mjs.map +1 -0
  17. package/dist/sync/graft-info/graft-info-helpers.respec.mjs +8 -8
  18. package/dist/sync/graft-info/graft-info-helpers.respec.mjs.map +1 -1
  19. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs +22 -22
  20. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs.map +1 -1
  21. package/dist/sync/sync-conflict-basic-divergence.respec.mjs +3 -3
  22. package/dist/sync/sync-conflict-basic-divergence.respec.mjs.map +1 -1
  23. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs +6 -6
  24. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs.map +1 -1
  25. package/dist/sync/sync-conflict-text-merge.respec.mjs +26 -26
  26. package/dist/sync/sync-conflict-text-merge.respec.mjs.map +1 -1
  27. package/dist/sync/sync-helpers.d.mts +19 -0
  28. package/dist/sync/sync-helpers.d.mts.map +1 -1
  29. package/dist/sync/sync-helpers.mjs +51 -1
  30. package/dist/sync/sync-helpers.mjs.map +1 -1
  31. package/dist/sync/sync-innerspace-constants.respec.mjs +2 -2
  32. package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
  33. package/dist/sync/sync-innerspace-deep-updates.respec.mjs +2 -2
  34. package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
  35. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +4 -4
  36. package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
  37. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +2 -2
  38. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  39. package/dist/sync/sync-innerspace-partial-update.respec.mjs +3 -3
  40. package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
  41. package/dist/sync/sync-innerspace.respec.mjs +4 -4
  42. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  43. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts +5 -0
  44. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts.map +1 -1
  45. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs +24 -2
  46. package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs.map +1 -1
  47. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts +5 -0
  48. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts.map +1 -1
  49. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs +21 -3
  50. package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs.map +1 -1
  51. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.d.mts +1 -1
  52. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +13 -1
  53. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
  54. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +40 -10
  55. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  56. package/dist/sync/sync-peer/sync-peer-types.d.mts +81 -1
  57. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
  58. package/dist/sync/sync-peer/sync-peer-v1.d.mts +37 -3
  59. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
  60. package/dist/sync/sync-peer/sync-peer-v1.mjs +163 -23
  61. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
  62. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.d.mts +46 -0
  63. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.d.mts.map +1 -0
  64. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.mjs +45 -0
  65. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.mjs.map +1 -0
  66. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.d.mts +30 -0
  67. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.d.mts.map +1 -0
  68. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mjs +2 -0
  69. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mjs.map +1 -0
  70. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts +68 -0
  71. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts.map +1 -0
  72. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs +324 -0
  73. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs.map +1 -0
  74. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +85 -0
  75. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -0
  76. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +332 -0
  77. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -0
  78. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.d.mts +29 -0
  79. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.d.mts.map +1 -0
  80. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mjs +2 -0
  81. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mjs.map +1 -0
  82. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts +44 -0
  83. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts.map +1 -0
  84. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs +303 -0
  85. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs.map +1 -0
  86. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +22 -5
  87. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -1
  88. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +223 -27
  89. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -1
  90. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +9 -0
  91. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -1
  92. package/dist/sync/sync-saga-coordinator.d.mts +41 -2
  93. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  94. package/dist/sync/sync-saga-coordinator.mjs +110 -11
  95. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  96. package/dist/sync/sync-types.d.mts +24 -0
  97. package/dist/sync/sync-types.d.mts.map +1 -1
  98. package/dist/sync/sync-types.mjs +0 -1
  99. package/dist/sync/sync-types.mjs.map +1 -1
  100. package/dist/sync/sync-withid.connect.respec.d.mts +12 -0
  101. package/dist/sync/sync-withid.connect.respec.d.mts.map +1 -0
  102. package/dist/sync/sync-withid.connect.respec.mjs +205 -0
  103. package/dist/sync/sync-withid.connect.respec.mjs.map +1 -0
  104. package/dist/sync/sync-withid.establish.respec.d.mts +19 -0
  105. package/dist/sync/sync-withid.establish.respec.d.mts.map +1 -0
  106. package/dist/sync/sync-withid.establish.respec.mjs +322 -0
  107. package/dist/sync/sync-withid.establish.respec.mjs.map +1 -0
  108. package/dist/sync/sync-withid.pingpong.respec.d.mts +11 -0
  109. package/dist/sync/sync-withid.pingpong.respec.d.mts.map +1 -0
  110. package/dist/sync/sync-withid.pingpong.respec.mjs +131 -0
  111. package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -0
  112. package/dist/witness/space/inner-space/inner-space-v1.d.mts.map +1 -1
  113. package/dist/witness/space/inner-space/inner-space-v1.mjs +1 -1
  114. package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
  115. package/package.json +4 -4
  116. package/src/keystone/keystone-config-builder.mts +73 -4
  117. package/src/keystone/keystone-constants.mts +42 -6
  118. package/src/keystone/keystone-helpers.mts +44 -2
  119. package/src/keystone/keystone-policy-types.mts +25 -0
  120. package/src/keystone/keystone-policy.schema.json +51 -0
  121. package/src/keystone/keystone-service-v1.mts +3 -3
  122. package/src/sync/docs/architecture.md +20 -0
  123. package/src/sync/docs/ping_pong_plan.md +147 -0
  124. package/src/sync/docs/security.md +207 -3
  125. package/src/sync/graft-info/graft-info-helpers.respec.mts +7 -7
  126. package/src/sync/sync-conflict-adv-multitimelines.respec.mts +21 -21
  127. package/src/sync/sync-conflict-basic-divergence.respec.mts +2 -2
  128. package/src/sync/sync-conflict-basic-multitimelines.respec.mts +5 -5
  129. package/src/sync/sync-conflict-text-merge.respec.mts +25 -25
  130. package/src/sync/sync-helpers.mts +51 -1
  131. package/src/sync/sync-innerspace-constants.respec.mts +1 -1
  132. package/src/sync/sync-innerspace-deep-updates.respec.mts +1 -1
  133. package/src/sync/sync-innerspace-dest-ahead.respec.mts +3 -3
  134. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +1 -1
  135. package/src/sync/sync-innerspace-partial-update.respec.mts +2 -2
  136. package/src/sync/sync-innerspace.respec.mts +3 -3
  137. package/src/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mts +26 -2
  138. package/src/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mts +23 -3
  139. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-types.mts +1 -1
  140. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +47 -13
  141. package/src/sync/sync-peer/sync-peer-types.mts +87 -1
  142. package/src/sync/sync-peer/sync-peer-v1.mts +171 -32
  143. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-constants.mts +68 -0
  144. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mts +36 -0
  145. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mts +385 -0
  146. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +388 -0
  147. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mts +35 -0
  148. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mts +345 -0
  149. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +223 -34
  150. package/src/sync/sync-saga-context/sync-saga-context-types.mts +9 -0
  151. package/src/sync/sync-saga-coordinator.mts +162 -6
  152. package/src/sync/sync-types.mts +28 -4
  153. package/src/sync/sync-withid.connect.respec.mts +243 -0
  154. package/src/sync/sync-withid.establish.respec.mts +361 -0
  155. package/src/sync/sync-withid.pingpong.respec.mts +161 -0
  156. package/src/sync/unused-identity-backup.mts.md +1 -1
  157. package/src/witness/space/inner-space/inner-space-v1.mts +4 -5
  158. package/dist/sync/sync-innerspace-dest-ahead-withid.respec.d.mts +0 -2
  159. package/dist/sync/sync-innerspace-dest-ahead-withid.respec.d.mts.map +0 -1
  160. package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs +0 -310
  161. package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs.map +0 -1
  162. package/src/sync/sync-innerspace-dest-ahead-withid.respec.mts +0 -364
@@ -44,7 +44,7 @@ import {
44
44
  import { getSyncSagaMessageIb } from "./sync-saga-message/sync-saga-message-helpers.mjs";
45
45
  import { SYNC_SAGA_MSG_ATOM } from "./sync-saga-message/sync-saga-message-constants.mjs";
46
46
  import { SyncSagaInfo } from "./sync-types.mjs";
47
- import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib, getIbGibsFromCache_fallbackToSpaces } from "../common/other/ibgib-helper.mjs";
47
+ import { splitPerTjpAndOrDna, getTimelinesGroupedByTjp, isIbGib, getIbGibsFromCache_fallbackToSpaces, toDto, getTjpAddr } from "../common/other/ibgib-helper.mjs";
48
48
  import { SyncPeerWitness } from "./sync-peer/sync-peer-types.mjs";
49
49
  import { SyncSagaContextData_V1, SyncSagaContextIbGib_V1, SyncSagaContextRel8ns_V1, } from "./sync-saga-context/sync-saga-context-types.mjs";
50
50
  import { getSyncSagaContextIb, validateContextAndSagaFrame } from "./sync-saga-context/sync-saga-context-helpers.mjs";
@@ -60,10 +60,17 @@ import { GRAFT_INFO_REL8N_NAME } from "./graft-info/graft-info-constants.mjs";
60
60
  import { GraftInfoIbGib_V1 } from "./graft-info/graft-info-types.mjs";
61
61
  import { validateIbGibIntrinsically } from "@ibgib/ts-gib/dist/V1/validate-helper.mjs";
62
62
  import { SYNC_SAGA_CONTEXT_ATOM } from "./sync-saga-context/sync-saga-context-constants.mjs";
63
+ import { KeystoneIbGib_V1 } from "../keystone/keystone-types.mjs";
64
+ import { createStandardPoolConfig } from "../keystone/keystone-config-builder.mjs";
65
+ import {
66
+ POOL_ID_CONNECT, POOL_ID_SYNC, KEYSTONE_VERB_CONNECT, KEYSTONE_VERB_SYNC
67
+ } from "../keystone/keystone-constants.mjs";
68
+ import { KeystonePoolConfig, KeystoneReplenishStrategy } from "../keystone/keystone-types.mjs";
69
+ import { SyncPeer_V1 } from "./sync-peer/sync-peer-v1.mjs";
63
70
 
64
71
 
65
- const logalot = GLOBAL_LOG_A_LOT;
66
- const logalotControlDomain = false;
72
+ const logalot = GLOBAL_LOG_A_LOT || true;
73
+ const logalotControlDomain = true;
67
74
  const lcControlDomain = '[ControlDomain]';
68
75
 
69
76
  /**
@@ -86,6 +93,46 @@ export class SyncSagaCoordinator {
86
93
 
87
94
  }
88
95
 
96
+ /**
97
+ * Default pool config for the session keystone's `connect` pool.
98
+ * Consumed exactly once per saga during `peer.connect()`.
99
+ * Uses `deleteAll` replenish strategy — after the handshake, the pool is gone.
100
+ *
101
+ * Override in subclasses or pass custom config via opts to change security parameters.
102
+ */
103
+ protected defaultSessionConnectPoolConfig(): KeystonePoolConfig {
104
+ return createStandardPoolConfig({
105
+ id: POOL_ID_CONNECT,
106
+ salt: `session-connect-${Date.now()}`,
107
+ verbs: [KEYSTONE_VERB_CONNECT],
108
+ size: 20,
109
+ sequential: 2,
110
+ random: 2,
111
+ targetBinding: 0,
112
+ replenishStrategy: KeystoneReplenishStrategy.deleteAll,
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Default pool config for the session keystone's per-turn `sync` pool.
118
+ * Used on each outgoing context frame (Init, Delta, Commit).
119
+ * Uses `topUp` replenish strategy to stay active throughout the saga.
120
+ *
121
+ * Override in subclasses or pass custom config via opts to change security parameters.
122
+ */
123
+ protected defaultSessionSyncPoolConfig(): KeystonePoolConfig {
124
+ return createStandardPoolConfig({
125
+ id: POOL_ID_SYNC,
126
+ salt: `session-sync-${Date.now()}`,
127
+ verbs: [KEYSTONE_VERB_SYNC],
128
+ size: 200,
129
+ sequential: 3,
130
+ random: 3,
131
+ targetBinding: 3,
132
+ replenishStrategy: KeystoneReplenishStrategy.topUp,
133
+ });
134
+ }
135
+
89
136
  /**
90
137
  * Executes a synchronization saga using the Symmetric Sync Protocol.
91
138
  *
@@ -96,6 +143,8 @@ export class SyncSagaCoordinator {
96
143
  public async sync({
97
144
  peer,
98
145
  domainIbGibs,
146
+ senderIdentity,
147
+ fnSenderSecret,
99
148
  conflictStrategy = SyncConflictStrategy.abort,
100
149
  metaspace,
101
150
  localSpace,
@@ -111,6 +160,22 @@ export class SyncSagaCoordinator {
111
160
  * These should all exist in {@link localSpace}
112
161
  */
113
162
  domainIbGibs: IbGib_V1[],
163
+ /**
164
+ * If present, this is the primary identity of the sender, i.e., who is
165
+ * initiating the sync.
166
+ *
167
+ * This should be the most recent frame (tip) of the senderIdentity's
168
+ * timeline.
169
+ *
170
+ * NOTE: {@link fnSenderSecret} must be truthy if this is truthy, and vice versa.
171
+ */
172
+ senderIdentity?: KeystoneIbGib_V1,
173
+ /**
174
+ * secret corresponding to {@link senderIdentity}.
175
+ *
176
+ * NOTE: {@link senderIdentity} must be truthy if this is truthy, and vice versa.
177
+ */
178
+ fnSenderSecret?: () => Promise<string>;
114
179
  /**
115
180
  * The space containing the {@link domainIbGibs} we want to sync. If
116
181
  * sync is successful, any updates to timelines will be stored here.
@@ -136,6 +201,11 @@ export class SyncSagaCoordinator {
136
201
  throw new Error(`${lc} source (or localSpace) required (E: 25df3761f7686a1099a552f83c95d326)`);
137
202
  }
138
203
 
204
+ if (senderIdentity || fnSenderSecret) {
205
+ if (!senderIdentity) { throw new Error(`(UNEXPECTED) senderIdentity falsy but fnSenderSecret truthy? if either is used, both must be truthy. (E: e366628a4919c7d727d2cbd8e5b75e26)`); }
206
+ if (!fnSenderSecret) { throw new Error(`(UNEXPECTED) fnSenderSecret falsy but senderIdentity truthy? if either is used, both must be truthy. (E: 7d55ce37ae482fec48e3398158161926)`); }
207
+ }
208
+
139
209
  // 1. SETUP SAGA METADATA
140
210
  const sagaId = await getUUID();
141
211
 
@@ -171,6 +241,21 @@ export class SyncSagaCoordinator {
171
241
  // Async execution wrapper
172
242
  (async () => {
173
243
  try {
244
+ const targetAddrs = domainIbGibs ? domainIbGibs.map(domain => getIbGibAddr({ ibGib: domain })) : undefined;
245
+
246
+ // Attach saga-scoped identity opts to peer now that sagaId is known.
247
+ peer.setOptionalOpts({
248
+ sagaId,
249
+ senderIdentity,
250
+ fnSenderSecret,
251
+ sessionConnectPoolConfig: this.defaultSessionConnectPoolConfig(),
252
+ sessionSyncPoolConfig: this.defaultSessionSyncPoolConfig(),
253
+ targetAddrs,
254
+ } as any);
255
+
256
+ // ESTABLISH SESSION IDENTITY (pre-connect, if identity provided)
257
+ await peer.establishSessionIdentity();
258
+
174
259
  // CONNECT PEER (if needed)
175
260
  await peer.connect({ sagaId });
176
261
 
@@ -180,6 +265,7 @@ export class SyncSagaCoordinator {
180
265
  domainIbGibs,
181
266
  conflictStrategy,
182
267
  metaspace, localSpace, tempSpace,
268
+ peer,
183
269
  });
184
270
 
185
271
  // KICK OFF THE PING-PONG SAGA LOOP (FSM)
@@ -225,6 +311,7 @@ export class SyncSagaCoordinator {
225
311
  mySpace,
226
312
  myTempSpace,
227
313
  metaspace,
314
+ peer,
228
315
  }: {
229
316
  sagaContext: SyncSagaContextIbGib_V1,
230
317
  /**
@@ -236,6 +323,10 @@ export class SyncSagaCoordinator {
236
323
  */
237
324
  myTempSpace: IbGibSpaceAny,
238
325
  metaspace: MetaspaceService,
326
+ /**
327
+ * the peer that is continuing the sync
328
+ */
329
+ peer: SyncPeer_V1,
239
330
  }): Promise<SyncSagaContextIbGib_V1 | null> {
240
331
  const lc = `${this.lc}[${this.continueSync.name}]`;
241
332
  try {
@@ -273,6 +364,9 @@ export class SyncSagaCoordinator {
273
364
  localSpace: mySpace,
274
365
  payloadIbGibsDomain,
275
366
  metaspace,
367
+ sessionIdentityAddr: sagaContext.rel8ns?.sessionIdentity?.[0],
368
+ peer,
369
+ skipSign: true,
276
370
  });
277
371
 
278
372
 
@@ -384,6 +478,8 @@ export class SyncSagaCoordinator {
384
478
  payloadIbGibsDomain: nextDomainIbGibs,
385
479
  localSpace,
386
480
  metaspace,
481
+ sessionIdentityAddr: peer.currentSessionIdentityAddr,
482
+ peer,
387
483
  });
388
484
 
389
485
  // #region Log what we're sending
@@ -528,6 +624,9 @@ export class SyncSagaCoordinator {
528
624
  payloadIbGibsDomain,
529
625
  metaspace,
530
626
  localSpace,
627
+ sessionIdentityAddr,
628
+ peer,
629
+ skipSign,
531
630
  }: {
532
631
  /**
533
632
  * The main saga frame (Init, Ack, etc.).
@@ -547,6 +646,22 @@ export class SyncSagaCoordinator {
547
646
  * execution POV) right when we create it.
548
647
  */
549
648
  localSpace: IbGibSpaceAny;
649
+ sessionIdentityAddr?: string;
650
+ peer: SyncPeerWitness;
651
+ /**
652
+ * If true, will not sign context.
653
+ *
654
+ * For now, this should be true when we call {@link continueSync}, as
655
+ * the receiver does not sign any keystones.
656
+ *
657
+ * ## intent
658
+ *
659
+ * In V1, our receiving endpoint is not signing the context, rather, it
660
+ * is just returning its response context ibgib with the same session
661
+ * identity addr. We are thus trusting the security of the transport
662
+ * itself for now.
663
+ */
664
+ skipSign?: boolean,
550
665
  }): Promise<SyncSagaContextIbGib_V1> {
551
666
  const lc = `[${this.createSyncSagaContext.name}]`;
552
667
  try {
@@ -580,6 +695,20 @@ export class SyncSagaCoordinator {
580
695
  sagaFrame: [getIbGibAddr({ ibGib: sagaFrame })],
581
696
  };
582
697
 
698
+ let sessionIdentity: KeystoneIbGib_V1 | undefined = undefined;
699
+ if (sessionIdentityAddr) {
700
+ sessionIdentityAddr =
701
+ await metaspace.getLatestAddr({ addr: sessionIdentityAddr, space: localSpace }) ?? sessionIdentityAddr;
702
+ const resGet = await getFromSpace({ addr: sessionIdentityAddr, space: localSpace });
703
+ if (resGet.success && resGet.ibGibs?.length === 1) {
704
+ sessionIdentity = resGet.ibGibs![0]! as KeystoneIbGib_V1;
705
+ rel8ns.sessionIdentity = [sessionIdentityAddr];
706
+ } else {
707
+ debugger; // what stage of the algo is this? which side?
708
+ throw new Error(`Couldn't get sessionIdentityAddr (${sessionIdentityAddr}) in space: ${localSpace.ib} (E: 5c29e80d68dbd1749866c358be093826)`);
709
+ }
710
+ }
711
+
583
712
  // Generate standard ib
584
713
  const ib = await getSyncSagaContextIb({ data });
585
714
 
@@ -590,8 +719,16 @@ export class SyncSagaCoordinator {
590
719
  rel8ns,
591
720
  }) as SyncSagaContextIbGib_V1;
592
721
 
593
- // put/register immediately. Note that contextIbGib at this point is
594
- // pure DTO, i.e., only ib, gib, data, rel8ns props.
722
+ const contextAddr = getIbGibAddr({ ibGib: contextIbGib });
723
+ let signedSessionIdentity: KeystoneIbGib_V1 | undefined = undefined;
724
+ if (sessionIdentity && !skipSign) {
725
+ // signing context persists and registers the new keystone
726
+ // using the peer's localMetaspace and localSpace
727
+ signedSessionIdentity = await peer.signContext({ contextAddr });
728
+ }
729
+
730
+ // put/register context immediately in local space (currently is a
731
+ // DTO) but we don't want a broadcast on the metaspace
595
732
  await putInSpace({ ibGib: contextIbGib, space: localSpace, });
596
733
  await registerNewIbGib({
597
734
  ibGib: contextIbGib,
@@ -599,11 +736,14 @@ export class SyncSagaCoordinator {
599
736
  fnBroadcast: undefined,
600
737
  });
601
738
 
602
- // Attach actual ibgibs for transport (not pure DTO now)
739
+ // Attach actual ibgibs for transport
603
740
  contextIbGib.sagaFrame = sagaFrame;
604
741
  if (payloadIbGibsDomain && payloadIbGibsDomain.length > 0) {
605
742
  contextIbGib.payloadIbGibsDomain = payloadIbGibsDomain;
606
743
  }
744
+ if (signedSessionIdentity) {
745
+ contextIbGib.signedSessionIdentity = signedSessionIdentity;
746
+ }
607
747
 
608
748
  return contextIbGib;
609
749
  } catch (error) {
@@ -733,6 +873,7 @@ export class SyncSagaCoordinator {
733
873
  metaspace,
734
874
  localSpace,
735
875
  tempSpace,
876
+ peer,
736
877
  }: {
737
878
  sagaId: string,
738
879
  domainIbGibs: IbGib_V1[],
@@ -740,6 +881,7 @@ export class SyncSagaCoordinator {
740
881
  metaspace: MetaspaceService,
741
882
  localSpace: IbGibSpaceAny,
742
883
  tempSpace: IbGibSpaceAny,
884
+ peer: SyncPeerWitness,
743
885
  }): Promise<{ initFrame: SyncIbGib_V1, initDomainGraph: { [addr: string]: IbGib_V1 } }> {
744
886
  const lc = `${this.lc}[${this.createInitFrame.name}]`;
745
887
  try {
@@ -786,6 +928,7 @@ export class SyncSagaCoordinator {
786
928
  conflictStrategy,
787
929
  metaspace,
788
930
  localSpace,
931
+ sessionIdentity: peer.currentSessionIdentity,
789
932
  });
790
933
 
791
934
  if (logalot) { console.log(`${lc} sagaFrame (init): ${pretty(sagaFrame)} (I: b3d6a8be69248f18713cc3073cb08626)`); }
@@ -2509,12 +2652,19 @@ export class SyncSagaCoordinator {
2509
2652
  msgStones,
2510
2653
  localSpace,
2511
2654
  metaspace,
2655
+ sessionIdentity,
2512
2656
  }: {
2513
2657
  prevSagaIbGib?: SyncIbGib_V1,
2514
2658
  conflictStrategy?: SyncConflictStrategy,
2515
2659
  msgStones: IbGib_V1[],
2516
2660
  localSpace: IbGibSpaceAny,
2517
2661
  metaspace: MetaspaceService,
2662
+ /**
2663
+ * this is only used on the init frame ({@link prevSagaIbGib}).
2664
+ * otherwise, the sessionIdentityTjpAddr for the next sync saga ibgib
2665
+ * frame will be driven by the {@link prevSagaIbGib}.
2666
+ */
2667
+ sessionIdentity?: KeystoneIbGib_V1 | undefined,
2518
2668
  }): Promise<SyncIbGib_V1> {
2519
2669
  const lc = `${this.lc}[${this.evolveSyncSagaIbGib.name}]`;
2520
2670
  try {
@@ -2584,6 +2734,12 @@ export class SyncSagaCoordinator {
2584
2734
  isTjp: true,
2585
2735
  conflictStrategy,
2586
2736
  };
2737
+ if (sessionIdentity) {
2738
+ const sessionIdentityTjpAddr =
2739
+ getTjpAddr({ ibGib: sessionIdentity });
2740
+ if (!sessionIdentityTjpAddr) { throw new Error(`(UNEXPECTED) sessionIdentity is truthy but sessionIdentityTjpAddr falsy? (E: f52004c10288987a6886f4e8fdf90826)`); }
2741
+ data.sessionIdentityTjpAddr = sessionIdentityTjpAddr;
2742
+ }
2587
2743
  // ib
2588
2744
  const ib = await getSyncIb({ data });
2589
2745
  // rel8ns
@@ -4,11 +4,8 @@ import { IbGib_V1, IbGibData_V1, IbGibRel8ns_V1 } from "@ibgib/ts-gib/dist/V1/ty
4
4
  import { SubjectWitness } from "../common/pubsub/subject/subject-types.mjs";
5
5
  import { SyncSagaContextIbGib_V1 } from "./sync-saga-context/sync-saga-context-types.mjs";
6
6
  import { SYNC_ATOM, SYNC_MSG_REL8N_NAME, SyncConflictStrategy, } from "./sync-constants.mjs";
7
- import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
8
- import { MetaspaceService } from "../witness/space/metaspace/metaspace-types.mjs";
9
- import { SyncPeerWitness } from "./sync-peer/sync-peer-types.mjs";
10
7
  import { FlatIbGibGraph } from "../common/other/graph-types.mjs";
11
- import { SyncSagaConflictInfo, SyncSagaMessageIbGib_V1 } from "./sync-saga-message/sync-saga-message-types.mjs";
8
+ import { SyncSagaMessageIbGib_V1 } from "./sync-saga-message/sync-saga-message-types.mjs";
12
9
 
13
10
 
14
11
  // #region SyncMode
@@ -184,6 +181,14 @@ export interface SyncData_V1 extends IbGibData_V1 {
184
181
  * 2. Rate limiting / Backpressure (Don't process this old request).
185
182
  */
186
183
  expirationTimestamp?: string;
184
+
185
+ /**
186
+ * If using session identity in the sync saga, this should be set and
187
+ * carried over in each saga frame
188
+ *
189
+ * TODO: IMPLEMENT THIS IN CODE. I HAVE JUST STUBBED THIS PROPERTY BUT IT DOES NOT EXIST IN CODE.
190
+ */
191
+ sessionIdentityTjpAddr?: IbGibAddr;
187
192
  }
188
193
 
189
194
  export interface SyncRel8ns_V1 extends IbGibRel8ns_V1 {
@@ -197,3 +202,22 @@ export interface SyncRel8ns_V1 extends IbGibRel8ns_V1 {
197
202
  export interface SyncIbGib_V1 extends IbGib_V1<SyncData_V1, SyncRel8ns_V1> { }
198
203
 
199
204
  // #endregion Sync Ib, Data, Rel8ns, IbGib
205
+
206
+ /**
207
+ * A session keystone should have this info in
208
+ * `sessionIdentity.data.frameDetails`.
209
+ */
210
+ export interface SessionGenesisFrameDetails {
211
+ senderIdentityAddr: IbGibAddr;
212
+ senderIdentityTjpAddr: IbGibAddr;
213
+ targetAddrs?: IbGibAddr[];
214
+ }
215
+
216
+ /**
217
+ * used in authentication/validation/authorization
218
+ * basically, **when** are we calling this validation method?
219
+ *
220
+ * terrible name, probably a code smell, but i'm getting desperate here
221
+ * to get this working.
222
+ */
223
+ export type StageInProtocol = "beforeSend" | "beforeReceive";
@@ -0,0 +1,243 @@
1
+ /**
2
+ * @module sync-withid.connect.respec
3
+ *
4
+ * Phase 2 — `peer.connect()` (Transport Handshake)
5
+ *
6
+ * Goal: Verify that the connection phase executes without error. S's connect
7
+ * pool remains undepleted (no-op on innerspace).
8
+ *
9
+ * @see libs/core-gib/src/sync/docs/security.md — Implementation Plan, Phase 2A
10
+ */
11
+
12
+ import {
13
+ respecfully, ifWe, iReckon,
14
+ } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
15
+ const maam = `[${import.meta.url}]`, sir = maam;
16
+ import { clone, delay, extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
17
+ import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
18
+
19
+ import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
20
+ import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
21
+ import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
22
+ import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
23
+ import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
24
+ import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-space-types.mjs';
25
+ import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
26
+ import { KeystoneService_V1 } from '../keystone/keystone-service-v1.mjs';
27
+ import { KeystoneIbGib_V1 } from '../keystone/keystone-types.mjs';
28
+ import {
29
+ KEYSTONE_VERB_SYNC, POOL_ID_SYNC, POOL_ID_CONNECT, KEYSTONE_VERB_CONNECT,
30
+ } from '../keystone/keystone-constants.mjs';
31
+ import { createStandardPoolConfig } from '../keystone/keystone-config-builder.mjs';
32
+ import { KeystoneReplenishStrategy } from '../keystone/keystone-types.mjs';
33
+ import { SyncConflictStrategy } from './sync-constants.mjs';
34
+ import { IbGibAddr, TransformResult } from '@ibgib/ts-gib/dist/types.mjs';
35
+ import { getIdentity_throwIfUndefined } from '../keystone/keystone-helpers.mjs';
36
+ import { Factory_V1 } from '@ibgib/ts-gib/dist/V1/factory.mjs';
37
+ import { IbGib_V1 } from '@ibgib/ts-gib/dist/V1/types.mjs';
38
+ import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
39
+ import { fork } from '@ibgib/ts-gib/dist/V1/transforms/fork.mjs';
40
+
41
+ const logalot = GLOBAL_LOG_A_LOT;
42
+ const lc = sir;
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Test-only identity constants
46
+ // ---------------------------------------------------------------------------
47
+
48
+ const SENDER_SECRET = 'test-sender-secret-phase1';
49
+
50
+ // ---------------------------------------------------------------------------
51
+ // Session keystone pool configs
52
+ // ---------------------------------------------------------------------------
53
+
54
+ const SESSION_CONNECT_POOL_CONFIG = createStandardPoolConfig({
55
+ id: POOL_ID_CONNECT,
56
+ salt: 'session-connect-salt-phase1',
57
+ verbs: [KEYSTONE_VERB_CONNECT],
58
+ size: 10,
59
+ sequential: 1,
60
+ random: 1,
61
+ targetBinding: 2,
62
+ replenishStrategy: KeystoneReplenishStrategy.deleteAll,
63
+ });
64
+
65
+ const SESSION_SYNC_POOL_CONFIG = createStandardPoolConfig({
66
+ id: POOL_ID_SYNC,
67
+ salt: 'session-sync-salt-phase1',
68
+ verbs: [KEYSTONE_VERB_SYNC],
69
+ size: 200,
70
+ sequential: 1,
71
+ random: 1,
72
+ targetBinding: 2,
73
+ replenishStrategy: KeystoneReplenishStrategy.topUp,
74
+ });
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // Top-level senderIdentity (I) pool config
78
+ // ---------------------------------------------------------------------------
79
+
80
+ const SENDER_IDENTITY_SYNC_POOL_CONFIG = createStandardPoolConfig({
81
+ id: POOL_ID_SYNC,
82
+ salt: 'senderidentitysyncsaltphase1',
83
+ verbs: [KEYSTONE_VERB_SYNC],
84
+ size: 200,
85
+ sequential: 1,
86
+ random: 1,
87
+ targetBinding: 2,
88
+ replenishStrategy: KeystoneReplenishStrategy.topUp,
89
+ });
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Main test suite
93
+ // ---------------------------------------------------------------------------
94
+
95
+ await respecfully(sir, `Test Phase 2: peer.connect()`, async () => {
96
+
97
+ // #region Init/Setup
98
+
99
+ const metaspace = new Metaspace_Innerspace(undefined);
100
+ await metaspace.initialize({
101
+ getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
102
+ getFnPrompt: () => async ({ title, msg }) => { console.log(`[Prompt] ${title}: ${msg}`); return ''; },
103
+ getFnPromptPassword: () => async (title, msg) => { console.log(`[PromptPwd] ${title}: ${msg}`); return null; },
104
+ });
105
+ while (!metaspace.initialized) { await delay(10); }
106
+
107
+ const defaultLocalUserSpace = await metaspace.getLocalUserSpace({ lock: false });
108
+ await defaultLocalUserSpace!.initialized;
109
+
110
+ const sourceSpace = new InnerSpace_V1({
111
+ ...DEFAULT_INNER_SPACE_DATA_V1,
112
+ name: 'source',
113
+ uuid: 'source_uuid',
114
+ description: 'sender durable space',
115
+ });
116
+ await sourceSpace.initialized;
117
+
118
+ const destSpace = new InnerSpace_V1({
119
+ ...DEFAULT_INNER_SPACE_DATA_V1,
120
+ name: 'dest',
121
+ uuid: 'dest_uuid',
122
+ description: 'receiver (domain provider) durable space',
123
+ });
124
+ await destSpace.initialized;
125
+
126
+ const senderCoordinator = new SyncSagaCoordinator();
127
+ const receiverCoordinator = new SyncSagaCoordinator();
128
+
129
+ async function newTestIbGib_stone({ ib = 'test', data }: { ib: string, data?: any }): Promise<IbGib_V1> {
130
+ const stone = await Factory_V1.stone({
131
+ parentPrimitiveIb: ib.split(' ').at(0) ?? 'test',
132
+ ib,
133
+ data,
134
+ uuid: true,
135
+ });
136
+ return stone;
137
+ }
138
+
139
+ async function newTestPeer(): Promise<SyncPeerInnerspace_V1> {
140
+ const peer = new SyncPeerInnerspace_V1(clone(SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1));
141
+ await peer.initialized;
142
+ await peer.initializeOpts({
143
+ sagaId: '',
144
+ localMetaspace: metaspace,
145
+ localSpace: sourceSpace,
146
+ receiverSpace: destSpace,
147
+ receiverCoordinator,
148
+ receiverMetaspace: metaspace,
149
+ });
150
+ return peer;
151
+ }
152
+
153
+ const keystoneSvc = new KeystoneService_V1();
154
+
155
+ // #endregion Init/Setup
156
+
157
+ let senderIdentity: KeystoneIbGib_V1 | undefined;
158
+
159
+ // Create senderIdentity genesis (I^Itjp) in sourceSpace
160
+ senderIdentity = await keystoneSvc.genesis({
161
+ masterSecret: SENDER_SECRET,
162
+ configs: [SENDER_IDENTITY_SYNC_POOL_CONFIG],
163
+ metaspace,
164
+ space: sourceSpace,
165
+ });
166
+
167
+ // post the senderIdentity to receiver
168
+ await metaspace.put({ ibGib: senderIdentity, space: destSpace });
169
+ await metaspace.registerNewIbGib({ ibGib: senderIdentity, space: destSpace });
170
+
171
+ // Execute sync
172
+ let syncError: any = null;
173
+ let xStone: IbGib_V1;
174
+ let xStoneAddr: IbGibAddr;
175
+ let peer: SyncPeerInnerspace_V1;
176
+
177
+ try {
178
+ xStone = await newTestIbGib_stone({ ib: 'test' });
179
+ xStoneAddr = getIbGibAddr({ ibGib: xStone });
180
+ await metaspace.put({ ibGib: xStone, space: sourceSpace });
181
+ await metaspace.registerNewIbGib({ ibGib: xStone, space: sourceSpace });
182
+
183
+ peer = await newTestPeer();
184
+
185
+ const syncSaga = await senderCoordinator.sync({
186
+ domainIbGibs: [xStone],
187
+ senderIdentity,
188
+ fnSenderSecret: async () => SENDER_SECRET,
189
+ peer,
190
+ localSpace: sourceSpace,
191
+ metaspace,
192
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
193
+ });
194
+ await syncSaga.done;
195
+
196
+ } catch (error) {
197
+ syncError = error;
198
+ if (logalot) { console.log(`${lc} Captured expected downstream/coordinator error: ${extractErrorMsg(error)}`); }
199
+ }
200
+
201
+ // #region Step 3: Check states
202
+
203
+ await ifWe(sir, 'does not throw connection-specific errors', async () => {
204
+ const errorMsg = syncError ? extractErrorMsg(syncError) : '';
205
+ // If it throws, the error should NOT be related to establishment or connection.
206
+ // It's expected to throw downstream in executeSagaLoop or continueSync due to missing context authentication/turn signing.
207
+ iReckon(sir, errorMsg.includes('establish') || errorMsg.includes('connect')).asTo('error is NOT related to establish or connect').isGonnaBeFalsy();
208
+ });
209
+
210
+ await ifWe(sir, 'session identity S connect pool is not depleted (innerspace no-op asymmetry)', async () => {
211
+ // Resolve latest evolved I1
212
+ const newSenderIdentityAddr = await metaspace.getLatestAddr({
213
+ ibGib: senderIdentity,
214
+ space: sourceSpace,
215
+ });
216
+ if (!newSenderIdentityAddr) { throw new Error(`newSenderIdentity not found`); }
217
+
218
+ const newSenderIdentity = await getIdentity_throwIfUndefined({
219
+ addr: newSenderIdentityAddr,
220
+ metaspace,
221
+ space: sourceSpace,
222
+ });
223
+
224
+ const sessionIdentityTjpAddr = newSenderIdentity.data.proofs
225
+ .find(p => p.claim.verb === KEYSTONE_VERB_SYNC)?.claim.target;
226
+ if (!sessionIdentityTjpAddr) { throw new Error(`sessionIdentityTjpAddr not found`); }
227
+
228
+ const sessionIdentity = await getIdentity_throwIfUndefined({
229
+ addr: sessionIdentityTjpAddr,
230
+ metaspace,
231
+ space: sourceSpace,
232
+ });
233
+
234
+ // The connect pool should exist and have all challenges remaining (no-op innerspace connect)
235
+ const connectPool = sessionIdentity.data.challengePools?.find(p => p.id === POOL_ID_CONNECT);
236
+ iReckon(sir, connectPool).asTo('connect pool exists on S').isGonnaBeTruthy();
237
+ const remainingChallenges = Object.keys(connectPool?.challenges ?? {});
238
+ iReckon(sir, remainingChallenges.length).asTo('connect pool has all challenges').willEqual(20);
239
+ });
240
+
241
+ // #endregion Step 3: Check states
242
+
243
+ });