@ibgib/core-gib 0.1.12 → 0.1.14

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 (106) hide show
  1. package/dist/keystone/keystone-helpers.mjs +3 -3
  2. package/dist/keystone/keystone-helpers.mjs.map +1 -1
  3. package/dist/sync/sync-constants.d.mts +4 -1
  4. package/dist/sync/sync-constants.d.mts.map +1 -1
  5. package/dist/sync/sync-constants.mjs +3 -0
  6. package/dist/sync/sync-constants.mjs.map +1 -1
  7. package/dist/sync/sync-helpers.d.mts +18 -2
  8. package/dist/sync/sync-helpers.d.mts.map +1 -1
  9. package/dist/sync/sync-helpers.mjs +84 -3
  10. package/dist/sync/sync-helpers.mjs.map +1 -1
  11. package/dist/sync/sync-innerspace.respec.d.mts +0 -6
  12. package/dist/sync/sync-innerspace.respec.d.mts.map +1 -1
  13. package/dist/sync/sync-innerspace.respec.mjs +395 -241
  14. package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
  15. package/dist/sync/sync-peer/sync-peer-types.d.mts +31 -0
  16. package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -0
  17. package/dist/sync/sync-peer/sync-peer-types.mjs +5 -0
  18. package/dist/sync/sync-peer/sync-peer-types.mjs.map +1 -0
  19. package/dist/sync/sync-peer/sync-peer-v1.d.mts +22 -0
  20. package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -0
  21. package/dist/sync/sync-peer/sync-peer-v1.mjs +13 -0
  22. package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -0
  23. package/dist/sync/sync-saga-context/sync-saga-context-constants.d.mts +8 -0
  24. package/dist/sync/sync-saga-context/sync-saga-context-constants.d.mts.map +1 -0
  25. package/dist/sync/sync-saga-context/sync-saga-context-constants.mjs +8 -0
  26. package/dist/sync/sync-saga-context/sync-saga-context-constants.mjs.map +1 -0
  27. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +54 -0
  28. package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -0
  29. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +87 -0
  30. package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -0
  31. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +66 -0
  32. package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -0
  33. package/dist/sync/sync-saga-context/sync-saga-context-types.mjs +12 -0
  34. package/dist/sync/sync-saga-context/sync-saga-context-types.mjs.map +1 -0
  35. package/dist/sync/sync-saga-coordinator.d.mts +136 -91
  36. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  37. package/dist/sync/sync-saga-coordinator.mjs +563 -287
  38. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  39. package/dist/sync/sync-saga-coordinator.respec.mjs +7 -7
  40. package/dist/sync/sync-saga-coordinator.respec.mjs.map +1 -1
  41. package/dist/sync/sync-saga-message/sync-saga-message-constants.d.mts +2 -0
  42. package/dist/sync/sync-saga-message/sync-saga-message-constants.d.mts.map +1 -0
  43. package/dist/sync/sync-saga-message/sync-saga-message-constants.mjs +2 -0
  44. package/dist/sync/sync-saga-message/sync-saga-message-constants.mjs.map +1 -0
  45. package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts +15 -0
  46. package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts.map +1 -0
  47. package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs +43 -0
  48. package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs.map +1 -0
  49. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +39 -0
  50. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -0
  51. package/dist/sync/sync-saga-message/sync-saga-message-types.mjs +2 -0
  52. package/dist/sync/sync-saga-message/sync-saga-message-types.mjs.map +1 -0
  53. package/dist/sync/sync-types.d.mts +81 -3
  54. package/dist/sync/sync-types.d.mts.map +1 -1
  55. package/dist/sync/sync-types.mjs +27 -1
  56. package/dist/sync/sync-types.mjs.map +1 -1
  57. package/dist/timeline/timeline-api.d.mts +16 -3
  58. package/dist/timeline/timeline-api.d.mts.map +1 -1
  59. package/dist/timeline/timeline-api.mjs +7 -7
  60. package/dist/timeline/timeline-api.mjs.map +1 -1
  61. package/dist/witness/space/inner-space/inner-space-v1.d.mts.map +1 -1
  62. package/dist/witness/space/inner-space/inner-space-v1.mjs +3 -4
  63. package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
  64. package/dist/witness/space/outer-space/outer-space-types.d.mts +2 -0
  65. package/dist/witness/space/outer-space/outer-space-types.d.mts.map +1 -1
  66. package/dist/witness/space/space-base-v1.d.mts +19 -1
  67. package/dist/witness/space/space-base-v1.d.mts.map +1 -1
  68. package/dist/witness/space/space-base-v1.mjs +66 -6
  69. package/dist/witness/space/space-base-v1.mjs.map +1 -1
  70. package/dist/witness/space/space-helper.d.mts +14 -0
  71. package/dist/witness/space/space-helper.d.mts.map +1 -1
  72. package/dist/witness/space/space-helper.mjs +44 -1
  73. package/dist/witness/space/space-helper.mjs.map +1 -1
  74. package/dist/witness/space/space-respec-helper.d.mts.map +1 -1
  75. package/dist/witness/space/space-respec-helper.mjs +1 -1
  76. package/dist/witness/space/space-respec-helper.mjs.map +1 -1
  77. package/dist/witness/space/space-types.d.mts +12 -1
  78. package/dist/witness/space/space-types.d.mts.map +1 -1
  79. package/dist/witness/space/space-types.mjs +4 -0
  80. package/dist/witness/space/space-types.mjs.map +1 -1
  81. package/package.json +2 -2
  82. package/src/keystone/keystone-helpers.mts +3 -3
  83. package/src/sync/README.md +275 -0
  84. package/src/sync/sync-constants.mts +5 -0
  85. package/src/sync/sync-helpers.mts +105 -6
  86. package/src/sync/sync-innerspace.respec.mts +458 -289
  87. package/src/sync/sync-peer/sync-peer-types.mts +43 -0
  88. package/src/sync/sync-peer/sync-peer-v1.mts +28 -0
  89. package/src/sync/sync-saga-context/sync-saga-context-constants.mts +8 -0
  90. package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +147 -0
  91. package/src/sync/sync-saga-context/sync-saga-context-types.mts +80 -0
  92. package/src/sync/sync-saga-coordinator.mts +762 -329
  93. package/src/sync/sync-saga-coordinator.respec.mts +7 -7
  94. package/src/sync/sync-saga-message/sync-saga-message-constants.mts +1 -0
  95. package/src/sync/sync-saga-message/sync-saga-message-helpers.mts +59 -0
  96. package/src/sync/sync-saga-message/sync-saga-message-types.mts +53 -0
  97. package/src/sync/sync-types.mts +103 -3
  98. package/src/timeline/timeline-api.mts +20 -4
  99. package/src/witness/space/inner-space/inner-space-v1.mts +3 -2
  100. package/src/witness/space/reconciliation-space/reconciliation-space-base.mts.OLD.md +884 -0
  101. package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts.OLD.md +125 -0
  102. package/src/witness/space/space-base-v1.mts +62 -12
  103. package/src/witness/space/space-helper.mts +50 -1
  104. package/src/witness/space/space-respec-helper.mts +2 -1
  105. package/src/witness/space/space-types.mts +13 -1
  106. package/tmp.md +3 -9
@@ -125,5 +125,9 @@ export const IbGibSpaceOptionsCmdModifier = {
125
125
  * Get the tjp ibgibs/addrs for given ibgib(s)
126
126
  */
127
127
  tjps: 'tjps',
128
+ /**
129
+ * Modifies the get command to retrieve a dependency graph.
130
+ */
131
+ dependencyGraph: 'dependency-graph',
128
132
  };
129
133
  //# sourceMappingURL=space-types.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"space-types.mjs","sourceRoot":"","sources":["../../../src/witness/space/space-types.mts"],"names":[],"mappings":"AAQA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AAUnF,MAAM,CAAC,MAAM,aAAa,GAAG;IACzB;;;;;;OAMG;IACH,KAAK,EAAE,OAAwB;IAC/B;;;OAGG;IACH,UAAU,EAAE,YAA6B;IACzC;;;;;;OAMG;IACH,UAAU,EAAE,YAA6B;CAC5C,CAAA;AACD,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,MAAM,EAAE,CAAC;AAG3E,MAAM,CAAC,MAAM,SAAS,GAAG;IACrB;;;;;;OAMG;IACH,IAAI,EAAE,MAAmB;IACzB;;;OAGG;IACH,IAAI,EAAE,MAAmB;CAC5B,CAAA;AACD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;AAGnE;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC5B;;;;;;;;OAQG;IACH,eAAe,EAAE,iBAAqC;CACzD,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC5B,YAAY,EAAE,cAAkC;CACnD,CAAA;AAGD;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,GAAG,gBAAgB;IACnB,GAAG,gBAAgB;CACtB,CAAA;AACD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,CAAC;AAgHzE,+CAA+C;AAC/C,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAChC,iEAAiE;IACjE,GAAG,EAAE,KAA6B;IAClC,iDAAiD;IACjD,GAAG,EAAE,KAA6B;IAClC,mCAAmC;IACnC,MAAM,EAAE,QAAgC;CAC3C,CAAA;AAOD;;GAEG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG;IACxC;;;;OAIG;IACH,GAAG,EAAE,KAAqC;IAC1C;;OAEG;IACH,KAAK,EAAE,OAAuC;IAC9C;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,QAAwC;IAChD;;OAEG;IACH,KAAK,EAAE,OAAuC;IAC9C;;OAEG;IACH,OAAO,EAAE,SAAyC;IAClD;;OAEG;IACH,IAAI,EAAE,MAAsC;CAC/C,CAAA"}
1
+ {"version":3,"file":"space-types.mjs","sourceRoot":"","sources":["../../../src/witness/space/space-types.mts"],"names":[],"mappings":"AAQA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AAUnF,MAAM,CAAC,MAAM,aAAa,GAAG;IACzB;;;;;;OAMG;IACH,KAAK,EAAE,OAAwB;IAC/B;;;OAGG;IACH,UAAU,EAAE,YAA6B;IACzC;;;;;;OAMG;IACH,UAAU,EAAE,YAA6B;CAC5C,CAAA;AACD,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,MAAM,EAAE,CAAC;AAG3E,MAAM,CAAC,MAAM,SAAS,GAAG;IACrB;;;;;;OAMG;IACH,IAAI,EAAE,MAAmB;IACzB;;;OAGG;IACH,IAAI,EAAE,MAAmB;CAC5B,CAAA;AACD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;AAGnE;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC5B;;;;;;;;OAQG;IACH,eAAe,EAAE,iBAAqC;CACzD,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC5B,YAAY,EAAE,cAAkC;CACnD,CAAA;AAGD;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,GAAG,gBAAgB;IACnB,GAAG,gBAAgB;CACtB,CAAA;AACD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,CAAC;AAgHzE,+CAA+C;AAC/C,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAChC,iEAAiE;IACjE,GAAG,EAAE,KAA6B;IAClC,iDAAiD;IACjD,GAAG,EAAE,KAA6B;IAClC,mCAAmC;IACnC,MAAM,EAAE,QAAgC;CAC3C,CAAA;AAOD;;GAEG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG;IACxC;;;;OAIG;IACH,GAAG,EAAE,KAAqC;IAC1C;;OAEG;IACH,KAAK,EAAE,OAAuC;IAC9C;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,QAAwC;IAChD;;OAEG;IACH,KAAK,EAAE,OAAuC;IAC9C;;OAEG;IACH,OAAO,EAAE,SAAyC;IAClD;;OAEG;IACH,IAAI,EAAE,MAAsC;IAC5C;;OAEG;IACH,eAAe,EAAE,kBAAkD;CACtE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ibgib/core-gib",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "ibgib core functionality, including base architecture for witnesses, spaces, apps, robbots, etc., as well as shared utility functions. Node v19+ needed for heavily-used isomorphic webcrypto hashing consumed in both node and browsers.",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -50,7 +50,7 @@
50
50
  "dependencies": {
51
51
  "@ibgib/encrypt-gib": "^0.2.34",
52
52
  "@ibgib/helper-gib": "^0.0.33",
53
- "@ibgib/ts-gib": "^0.5.27"
53
+ "@ibgib/ts-gib": "^0.5.28"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/node": "^20.2.1"
@@ -103,7 +103,7 @@ export function getDeterministicRequirements({
103
103
  if (requiredChallengeIds && requiredChallengeIds.length > 0) {
104
104
  for (const id of requiredChallengeIds) {
105
105
  if (!pool.challenges[id]) {
106
- throw new Error(`(UNEXPECTED) Required challenge ID not found in pool: ${id} (E: 2c9f8...)`);
106
+ throw new Error(`(UNEXPECTED) Required challenge ID not found in pool: ${id} (E: 10c5619d80d8cf6cc84ffbf8be0dbc25)`);
107
107
  }
108
108
  // Strict: Consume it.
109
109
  if (!available.includes(id)) {
@@ -133,7 +133,7 @@ export function getDeterministicRequirements({
133
133
  const match = bucket.find(id => available.includes(id));
134
134
 
135
135
  if (!match) {
136
- throw new Error(`Entropy Exhaustion. Cannot satisfy binding for char '${char}'. (E: 8d3a1...)`);
136
+ throw new Error(`Entropy Exhaustion. Cannot satisfy binding for char '${char}'. (E: 341b95dc3a58be3e083d1d9c4a0c4925)`);
137
137
  }
138
138
 
139
139
  // Strict: Consume it.
@@ -150,7 +150,7 @@ export function getDeterministicRequirements({
150
150
  // Take the first N from the remaining available list
151
151
  if (available.length < behavior.selectSequentially) {
152
152
  console.error(`[getDeterministicRequirements] Entropy Exhaustion! AvailableCount: ${available.length}, Required Seq: ${behavior.selectSequentially}. Available IDs: ${available.join(',')}`);
153
- throw new Error(`Entropy Exhaustion. Insufficient challenges for FIFO requirement. (E: 9c2b4...)`);
153
+ throw new Error(`Entropy Exhaustion. Insufficient challenges for FIFO requirement. (E: 9efa88003e1d5eccb837c85154ee5625)`);
154
154
  }
155
155
 
156
156
  const fifoIds = available.slice(0, behavior.selectSequentially);
@@ -0,0 +1,275 @@
1
+ # Symmetric Sync Protocol Architecture
2
+
3
+ This document outlines the architecture for the symmetric synchronization protocol between two "Sync Nodes" (e.g., Alice/Client and Bob/Server).
4
+
5
+ ## Concepts
6
+
7
+ * **Saga**: A linear conversation of ibGibs (`Init` -> `Ack` -> `Delta` -> `Commit`).
8
+ * **SyncSagaCoordinator**: The "Driver" that manages the saga state machine. It is symmetric: both Alice and Bob use the same coordinator logic.
9
+ * **SyncWitness**: An abstraction for the "Other Side". It implements the `Witness` interface (IbGib pattern) to transport messages.
10
+ * `LocalSyncWitness`: Direct in-memory call (for testing/local sync).
11
+ * `RemoteSyncWitness`: HTTP/WebSocket transport (for client-server).
12
+ * **SyncSagaContext**: The payload exchanged between witnesses. It is a full-fledged ibGib containing the saga frame and all necessary proof/data stones.
13
+
14
+ ## Data Structures
15
+
16
+ ### SyncSagaContext (The Payload)
17
+
18
+ We treat the context itself as an ibGib.
19
+
20
+ ```typescript
21
+ import { IbGib_V1, IbGibData_V1, IbGibRel8ns_V1 } from '@ibgib/ts-gib/dist/V1';
22
+
23
+ export const SYNC_SAGA_CONTEXT_ATOM = 'sync_sagacontext';
24
+
25
+ export interface SyncSagaContextData_V1 extends IbGibData_V1 {
26
+ /**
27
+ * Contextual metadata, if any.
28
+ */
29
+ cmd?: string;
30
+ }
31
+
32
+ export interface SyncSagaContextRel8ns_V1 extends IbGibRel8ns_V1 {
33
+ /**
34
+ * The primary Saga Frame being transported (Init, Ack, Delta, etc.).
35
+ * 0..1 (Optional only if trivial ping)
36
+ */
37
+ sagaFrame?: string[]; // [addr]
38
+
39
+ /**
40
+ * Message stones associated with this context (e.g. Init payload stone).
41
+ */
42
+ msgs?: string[];
43
+
44
+ /**
45
+ * The Ephemeral Session Keystone Identity used for this saga.
46
+ */
47
+ sessionKeystone?: string[];
48
+
49
+ /**
50
+ * Authorization claims linking Session Key to Primary Identity.
51
+ */
52
+ claims?: string[];
53
+
54
+ /**
55
+ * Any other dependency stones/ibgibs required for validation.
56
+ */
57
+ other?: string[];
58
+ }
59
+
60
+ export interface SyncSagaContextIbGib_V1 extends IbGib_V1<SyncSagaContextData_V1, SyncSagaContextRel8ns_V1> {}
61
+ ```
62
+
63
+ ## Abstract Transport (`SyncPeer`)
64
+
65
+ The transport layer is just another Witness.
66
+
67
+ ```typescript
68
+ import { LightWitnessBase_V1 } from '../../witness/light-witness-base-v1';
69
+
70
+ /**
71
+ * Witness for talking to a Sync Peer.
72
+ * Input: SyncSagaContextIbGib_V1 (The Request)
73
+ * Output: SyncSagaContextIbGib_V1 (The Response)
74
+ */
75
+ export abstract class SyncPeer_V1 extends LightWitnessBase_V1 {
76
+ // The 'witness' method is the transport mechanism.
77
+ abstract witness(arg: SyncSagaContextIbGib_V1): Promise<SyncSagaContextIbGib_V1 | undefined>;
78
+ }
79
+ ```
80
+
81
+ ## Workflow Execution (Alice -> Bob)
82
+
83
+ ### 1. Startup: Alice Initiates (Client Side)
84
+
85
+ Create the `SessionKeystone`, authorize it with `PrimaryIdentity`, and start the Saga.
86
+
87
+ ```typescript
88
+ // IN: SyncSagaCoordinator (Alice)
89
+ async function sync({
90
+ peer, // The SyncPeer_V1 (connected to Bob)
91
+ localSpace, // Alice's main space
92
+ metaspace,
93
+ primaryIdentity // Alice's Master Keystone
94
+ }: SyncOptions) {
95
+
96
+ // 0. WORKING CONTEXT (Transactional)
97
+ // Create a temp space to squash "in-flight" saga metadata
98
+ // so we don't pollute localSpace until commit.
99
+ const tempSpace = await metaspace.createLocalSpace({ inMemory: true });
100
+
101
+ try {
102
+ // 1. BOOTSTRAP IDENTITY
103
+ const sagaId = await getUUID();
104
+
105
+ // A. Create Fresh Session Keystone (TJP)
106
+ // Derived deterministically from Master Secret + SagaId
107
+ const sessionIdentity = await createSessionKeystone({
108
+ seed: sagaId,
109
+ masterSecret: primaryIdentity.secret
110
+ });
111
+
112
+ // B. Authorize Session Key (Primary signs Session TJP)
113
+ // Primary creates a "Claim" saying "I authorize this Session Key"
114
+ // Target: SessionIdentity TJPAddr
115
+ // Context: SagaId
116
+ const authClaim = await createKeystoneClaim({
117
+ issuer: primaryIdentity,
118
+ subject: sessionIdentity.tjp,
119
+ context: sagaId,
120
+ });
121
+ // (Sign the claim with Primary)
122
+
123
+ // 2. CREATE SAGA INIT (in Temp Space)
124
+ // A. Create Init Msg Stone
125
+ const initStone = await this.createSyncMsgStone({
126
+ data: {
127
+ sagaId,
128
+ stage: 'init',
129
+ identity: sessionIdentity.toPublic(),
130
+ authorization: authClaim,
131
+ },
132
+ space: tempSpace,
133
+ metaspace
134
+ });
135
+
136
+ // B. Evolve Saga Frame (Root)
137
+ // Rel8s to Session Identity TJP (Soft Link via signing)
138
+ const sagaFrame = await this.evolveSyncSagaIbGib({
139
+ msgStones: [initStone],
140
+ identity: sessionIdentity, // Signed by Session Key
141
+ space: tempSpace,
142
+ metaspace
143
+ });
144
+
145
+ // 3. TRANSPORT (Alice -> Bob)
146
+ // Construct Context IbGib via Helper
147
+ const requestCtx = await createSyncSagaContext({
148
+ data: { cmd: 'process' },
149
+ rel8ns: {
150
+ sagaFrame: [getIbGibAddr(sagaFrame)],
151
+ msgs: [getIbGibAddr(initStone)],
152
+ sessionKeystone: [getIbGibAddr(sessionIdentity)],
153
+ claims: [getIbGibAddr(authClaim)],
154
+ }
155
+ });
156
+ // (We would physically put these artifacts into a transmit buffer/space here)
157
+
158
+ // "Witness" the request (Put to Bob)
159
+ const responseCtx = await peer.witness(requestCtx);
160
+
161
+ // 4. HANDLE RESPONSE (Bob -> Alice)
162
+ if (responseCtx) {
163
+ // Bob replied with Ack/Counter
164
+ await this.handleSagaFrame({
165
+ incomingCtx: responseCtx,
166
+ localSpace,
167
+ tempSpace,
168
+ identity: sessionIdentity
169
+ });
170
+ }
171
+
172
+ // 5. COMMIT (If success)
173
+ // Merge relevant artifacts from tempSpace to localSpace
174
+
175
+ } finally {
176
+ // CLEANUP
177
+ // Destroy tempSpace
178
+ }
179
+ }
180
+ ```
181
+
182
+ ### 2. Receiver: Bob Reacts (Server Side)
183
+
184
+ Bob receives the context, validates the `SessionKey` -> `PrimaryIdentity` chain, and negotiates.
185
+
186
+ ```typescript
187
+ // IN: SyncSagaCoordinator (Bob)
188
+ // Called via Internal Plumbing when SyncPeer receives 'witness()' call
189
+ async function handleRequest(ctx: SyncSagaContextIbGib_V1): Promise<SyncSagaContextIbGib_V1> {
190
+
191
+ // 1. VALIDATION (The Guard Rails)
192
+ // A. Validate Structure (Stones exist, hashes match)
193
+ // B. Validate Identity Chain:
194
+ // - Verify `ctx.sagaFrame` signed by `SessionKey` (in `ctx.sessionKeystone`)
195
+ // - Verify `SessionKey` authorized by `AuthClaim` (in `ctx.claims`)
196
+ // - Verify `AuthClaim` signed by `PrimaryIdentity` (Known/Trusted?)
197
+
198
+ // 2. WORKING CONTEXT
199
+ const tempSpace = ...; // Bob's temp space management
200
+
201
+ // 3. LOGIC DISPATCH
202
+ const incomingFrameAddr = ctx.rel8ns.sagaFrame[0];
203
+ const incomingFrame = await getFromSpace(incomingFrameAddr); // (From ctx payload)
204
+
205
+ const stage = incomingFrame.data.stage; // 'init'
206
+
207
+ let responseFrame;
208
+ if (stage === 'init') {
209
+ responseFrame = await this.handleInitFrame(incomingFrame, tempSpace);
210
+ } else if (stage === 'delta') {
211
+ // ...
212
+ }
213
+
214
+ // 4. RESPONSE
215
+ // Construct Response Context with new frame + stones
216
+ return responseCtx;
217
+ }
218
+ ```
219
+
220
+ ## Calling Code Example
221
+
222
+ The `sync` method returns a `SyncSagaInfo` object, which behaves like a mini-pubsub communicator. This allows the caller to subscribe to progress updates or await completion.
223
+
224
+ ### Types
225
+
226
+ ```typescript
227
+ import { SubjectWitness } from '../../common/pubsub/subject/subject-types';
228
+
229
+ export interface SyncSagaInfo {
230
+ sagaId: string;
231
+ /**
232
+ * Stream of context updates happening during the saga.
233
+ * Subscribe to this to receive real-time progress.
234
+ */
235
+ updates$: SubjectWitness<SyncSagaContextIbGib_V1>;
236
+
237
+ /**
238
+ * Promise that resolves when the saga completes successfully.
239
+ */
240
+ done: Promise<void>;
241
+ }
242
+ ```
243
+
244
+ ### Usage
245
+
246
+ ```typescript
247
+ import { SyncSagaCoordinator } from './sync-saga-coordinator';
248
+
249
+ // 1. Initialize Coordinator
250
+ const coordinator = new SyncSagaCoordinator( ... );
251
+
252
+ // 2. Start Sync
253
+ const sagaInfo = await coordinator.sync({
254
+ peer: remoteWitness,
255
+ localSpace: myLocalSpace,
256
+ metaspace: myServices,
257
+ primaryIdentity: myIdentity
258
+ });
259
+
260
+ // 3. Subscribe to Updates
261
+ await sagaInfo.updates$.subscribe(async (context) => {
262
+ // context is SyncSagaContextIbGib_V1
263
+ // You can inspect the frame stage, look for errors, etc.
264
+ const frameAddr = context.rel8ns.sagaFrame?.[0];
265
+ console.log(`Saga Update: ${frameAddr}`);
266
+ });
267
+
268
+ // 4. Await Completion
269
+ try {
270
+ await sagaInfo.done;
271
+ console.log("Sync Complete!");
272
+ } catch (error) {
273
+ console.error("Sync Failed:", error);
274
+ }
275
+ ```
@@ -1,23 +1,28 @@
1
1
  export const SYNC_ATOM = "sync";
2
2
 
3
+ export const SYNC_MSG_REL8N_NAME = "sync-msg";
4
+
3
5
  /**
4
6
  * Protocol version string for V1.
5
7
  */
6
8
  export const SYNC_PROTOCOL_V1 = "sync 1.0.0";
7
9
 
8
10
  export const SYNC_STAGE_INIT = "init";
11
+ export const SYNC_STAGE_ACK = "ack";
9
12
  export const SYNC_STAGE_REQUEST = "request";
10
13
  export const SYNC_STAGE_DELTA = "delta";
11
14
  export const SYNC_STAGE_COMMIT = "commit";
12
15
 
13
16
  export type SyncStage =
14
17
  | typeof SYNC_STAGE_INIT
18
+ | typeof SYNC_STAGE_ACK
15
19
  | typeof SYNC_STAGE_REQUEST
16
20
  | typeof SYNC_STAGE_DELTA
17
21
  | typeof SYNC_STAGE_COMMIT;
18
22
 
19
23
  export const SyncStage = {
20
24
  init: SYNC_STAGE_INIT,
25
+ ack: SYNC_STAGE_ACK,
21
26
  request: SYNC_STAGE_REQUEST,
22
27
  delta: SYNC_STAGE_DELTA,
23
28
  commit: SYNC_STAGE_COMMIT,
@@ -1,9 +1,13 @@
1
1
  import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
2
+ import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
2
3
  import { Ib } from "@ibgib/ts-gib/dist/types.mjs";
4
+
5
+ import { GLOBAL_LOG_A_LOT } from "../core-constants.mjs";
3
6
  import { SYNC_ATOM } from "./sync-constants.mjs";
4
- import { SyncData_V1, SyncIbInfo_V1 } from "./sync-types.mjs";
7
+ import { SyncData_V1, SyncIb_V1 } from "./sync-types.mjs";
8
+ import { IbGibSpaceAny } from "../witness/space/space-base-v1.mjs";
9
+ import { getFromSpace } from "../witness/space/space-helper.mjs";
5
10
 
6
- const GLOBAL_LOG_A_LOT = false; // Todo: import from core constants if needed
7
11
  const logalot = GLOBAL_LOG_A_LOT;
8
12
 
9
13
  /**
@@ -38,17 +42,21 @@ export async function parseSyncIb({
38
42
  ib,
39
43
  }: {
40
44
  ib: Ib,
41
- }): Promise<SyncIbInfo_V1> {
45
+ }): Promise<SyncIb_V1> {
42
46
  const lc = `[${parseSyncIb.name}]`;
43
47
  try {
44
48
  const parts = ib.split(' ');
45
49
  if (parts.length !== 3) {
46
- throw new Error(`Invalid sync ib. Expected 3 parts [atom uuid stage]. Got ${parts.length}. (E: 7c8d9...)`);
50
+ throw new Error(`Invalid sync ib. Expected 3 parts [atom uuid stage]. Got ${parts.length}. (E: 7db603e0dcf8c35af826cce89a125825)`);
47
51
  }
48
- const [atom, uuid, stage] = parts;
52
+ const [
53
+ atom,
54
+ uuid,
55
+ stage
56
+ ] = parts;
49
57
 
50
58
  if (atom !== SYNC_ATOM) {
51
- throw new Error(`Invalid sync ib. Expected atom '${SYNC_ATOM}', got '${atom}'. (E: 8f9e1...)`);
59
+ throw new Error(`Atom mismatch. Expected ${SYNC_ATOM}. Got ${atom}.`);
52
60
  }
53
61
 
54
62
  return { atom, uuid, stage: stage as any };
@@ -57,3 +65,94 @@ export async function parseSyncIb({
57
65
  throw error;
58
66
  }
59
67
  }
68
+
69
+ /**
70
+ * Checks if an address (`olderAddr`) is in the past frames of another (`newerAddr`).
71
+ *
72
+ * Efficiently uses `inMemoryGraph` if provided to avoid Space I/O.
73
+ * Otherwise falls back to traversing using `space`.
74
+ */
75
+ export async function isPastFrame({
76
+ olderAddr,
77
+ newerAddr,
78
+ space,
79
+ inMemoryGraph,
80
+ }: {
81
+ olderAddr: string, // Was ancestorAddr
82
+ newerAddr: string, // Was descendantAddr
83
+ space?: IbGibSpaceAny,
84
+ inMemoryGraph?: { [addr: string]: IbGib_V1 },
85
+ }): Promise<boolean> {
86
+ if (olderAddr === newerAddr) return true;
87
+
88
+ // Quick check if optimization allowed
89
+ if (inMemoryGraph) {
90
+ // Optimization: Iterate the graph values (or keys) and see if olderAddr is in any 'past'
91
+ // Actually we need to traverse from newerAddr back to olderAddr using the graph.
92
+
93
+ const queue = [newerAddr];
94
+ const visited = new Set<string>();
95
+
96
+ while (queue.length > 0) {
97
+ const currentAddr = queue.shift()!;
98
+ if (visited.has(currentAddr)) continue;
99
+ visited.add(currentAddr);
100
+
101
+ if (currentAddr === olderAddr) return true;
102
+
103
+ const node = inMemoryGraph[currentAddr];
104
+ // If node not in memory graph, we can't traverse further in memory.
105
+ // If we have space, we could fallback, but usually mixed mode is tricky.
106
+ // For now, if provided inMemoryGraph, assume it contains the relevant segment.
107
+ if (!node && space) {
108
+ // optional fallback to space if allowed?
109
+ // User requirement: "at most we are looking... in dependency graph".
110
+ // So let's fallback if space provided.
111
+ const res = await getFromSpace({ space, addr: currentAddr });
112
+ if (res.success && res.ibGibs?.length) {
113
+ const spaceNode = res.ibGibs[0];
114
+ if (spaceNode.rel8ns && spaceNode.rel8ns.past) {
115
+ for (const pastAddr of spaceNode.rel8ns.past) {
116
+ if (!visited.has(pastAddr)) queue.push(pastAddr);
117
+ }
118
+ }
119
+ }
120
+ continue;
121
+ } else if (!node) {
122
+ continue; // Dead end in memory
123
+ }
124
+
125
+ if (node.rel8ns && node.rel8ns.past) {
126
+ for (const pastAddr of node.rel8ns.past) {
127
+ if (!visited.has(pastAddr)) queue.push(pastAddr);
128
+ }
129
+ }
130
+ }
131
+ return false;
132
+ }
133
+
134
+ // Fallback to purely space-based traversal if no memory graph
135
+ if (!space) { throw new Error(`Must provide either space or inMemoryGraph for ancestry check.`); }
136
+
137
+ const queue = [newerAddr];
138
+ const visited = new Set<string>();
139
+
140
+ while (queue.length > 0) {
141
+ const currentAddr = queue.shift()!;
142
+ if (visited.has(currentAddr)) continue;
143
+ visited.add(currentAddr);
144
+
145
+ if (currentAddr === olderAddr) return true;
146
+
147
+ const res = await getFromSpace({ space, addr: currentAddr });
148
+ if (!res.success || !res.ibGibs || res.ibGibs.length === 0) continue;
149
+ const node = res.ibGibs[0];
150
+ if (node.rel8ns && node.rel8ns.past) {
151
+ for (const pastAddr of node.rel8ns.past) {
152
+ if (!visited.has(pastAddr)) queue.push(pastAddr);
153
+ }
154
+ }
155
+ }
156
+
157
+ return false;
158
+ }