@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.
- package/dist/keystone/keystone-helpers.mjs +3 -3
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/sync/sync-constants.d.mts +4 -1
- package/dist/sync/sync-constants.d.mts.map +1 -1
- package/dist/sync/sync-constants.mjs +3 -0
- package/dist/sync/sync-constants.mjs.map +1 -1
- package/dist/sync/sync-helpers.d.mts +18 -2
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +84 -3
- package/dist/sync/sync-helpers.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.d.mts +0 -6
- package/dist/sync/sync-innerspace.respec.d.mts.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +395 -241
- package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts +31 -0
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-types.mjs +5 -0
- package/dist/sync/sync-peer/sync-peer-types.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-v1.d.mts +22 -0
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-v1.mjs +13 -0
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-constants.d.mts +8 -0
- package/dist/sync/sync-saga-context/sync-saga-context-constants.d.mts.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-constants.mjs +8 -0
- package/dist/sync/sync-saga-context/sync-saga-context-constants.mjs.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts +54 -0
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.d.mts.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs +87 -0
- package/dist/sync/sync-saga-context/sync-saga-context-helpers.mjs.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts +66 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.d.mts.map +1 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.mjs +12 -0
- package/dist/sync/sync-saga-context/sync-saga-context-types.mjs.map +1 -0
- package/dist/sync/sync-saga-coordinator.d.mts +136 -91
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +563 -287
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.respec.mjs +7 -7
- package/dist/sync/sync-saga-coordinator.respec.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-constants.d.mts +2 -0
- package/dist/sync/sync-saga-message/sync-saga-message-constants.d.mts.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-constants.mjs +2 -0
- package/dist/sync/sync-saga-message/sync-saga-message-constants.mjs.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts +15 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs +43 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +39 -0
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -0
- package/dist/sync/sync-saga-message/sync-saga-message-types.mjs +2 -0
- package/dist/sync/sync-saga-message/sync-saga-message-types.mjs.map +1 -0
- package/dist/sync/sync-types.d.mts +81 -3
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs +27 -1
- package/dist/sync/sync-types.mjs.map +1 -1
- package/dist/timeline/timeline-api.d.mts +16 -3
- package/dist/timeline/timeline-api.d.mts.map +1 -1
- package/dist/timeline/timeline-api.mjs +7 -7
- package/dist/timeline/timeline-api.mjs.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.d.mts.map +1 -1
- package/dist/witness/space/inner-space/inner-space-v1.mjs +3 -4
- package/dist/witness/space/inner-space/inner-space-v1.mjs.map +1 -1
- package/dist/witness/space/outer-space/outer-space-types.d.mts +2 -0
- package/dist/witness/space/outer-space/outer-space-types.d.mts.map +1 -1
- package/dist/witness/space/space-base-v1.d.mts +19 -1
- package/dist/witness/space/space-base-v1.d.mts.map +1 -1
- package/dist/witness/space/space-base-v1.mjs +66 -6
- package/dist/witness/space/space-base-v1.mjs.map +1 -1
- package/dist/witness/space/space-helper.d.mts +14 -0
- package/dist/witness/space/space-helper.d.mts.map +1 -1
- package/dist/witness/space/space-helper.mjs +44 -1
- package/dist/witness/space/space-helper.mjs.map +1 -1
- package/dist/witness/space/space-respec-helper.d.mts.map +1 -1
- package/dist/witness/space/space-respec-helper.mjs +1 -1
- package/dist/witness/space/space-respec-helper.mjs.map +1 -1
- package/dist/witness/space/space-types.d.mts +12 -1
- package/dist/witness/space/space-types.d.mts.map +1 -1
- package/dist/witness/space/space-types.mjs +4 -0
- package/dist/witness/space/space-types.mjs.map +1 -1
- package/package.json +2 -2
- package/src/keystone/keystone-helpers.mts +3 -3
- package/src/sync/README.md +275 -0
- package/src/sync/sync-constants.mts +5 -0
- package/src/sync/sync-helpers.mts +105 -6
- package/src/sync/sync-innerspace.respec.mts +458 -289
- package/src/sync/sync-peer/sync-peer-types.mts +43 -0
- package/src/sync/sync-peer/sync-peer-v1.mts +28 -0
- package/src/sync/sync-saga-context/sync-saga-context-constants.mts +8 -0
- package/src/sync/sync-saga-context/sync-saga-context-helpers.mts +147 -0
- package/src/sync/sync-saga-context/sync-saga-context-types.mts +80 -0
- package/src/sync/sync-saga-coordinator.mts +762 -329
- package/src/sync/sync-saga-coordinator.respec.mts +7 -7
- package/src/sync/sync-saga-message/sync-saga-message-constants.mts +1 -0
- package/src/sync/sync-saga-message/sync-saga-message-helpers.mts +59 -0
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +53 -0
- package/src/sync/sync-types.mts +103 -3
- package/src/timeline/timeline-api.mts +20 -4
- package/src/witness/space/inner-space/inner-space-v1.mts +3 -2
- package/src/witness/space/reconciliation-space/reconciliation-space-base.mts.OLD.md +884 -0
- package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts.OLD.md +125 -0
- package/src/witness/space/space-base-v1.mts +62 -12
- package/src/witness/space/space-helper.mts +50 -1
- package/src/witness/space/space-respec-helper.mts +2 -1
- package/src/witness/space/space-types.mts +13 -1
- 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;
|
|
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.
|
|
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.
|
|
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:
|
|
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:
|
|
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:
|
|
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,
|
|
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<
|
|
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:
|
|
50
|
+
throw new Error(`Invalid sync ib. Expected 3 parts [atom uuid stage]. Got ${parts.length}. (E: 7db603e0dcf8c35af826cce89a125825)`);
|
|
47
51
|
}
|
|
48
|
-
const [
|
|
52
|
+
const [
|
|
53
|
+
atom,
|
|
54
|
+
uuid,
|
|
55
|
+
stage
|
|
56
|
+
] = parts;
|
|
49
57
|
|
|
50
58
|
if (atom !== SYNC_ATOM) {
|
|
51
|
-
throw new Error(`
|
|
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
|
+
}
|