@ibgib/core-gib 0.1.55 → 0.1.58
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/CHANGELOG.md +1 -0
- package/dist/keystone/keystone-config-builder.d.mts +12 -1
- package/dist/keystone/keystone-config-builder.d.mts.map +1 -1
- package/dist/keystone/keystone-config-builder.mjs +58 -4
- package/dist/keystone/keystone-config-builder.mjs.map +1 -1
- package/dist/keystone/keystone-constants.d.mts +40 -5
- package/dist/keystone/keystone-constants.d.mts.map +1 -1
- package/dist/keystone/keystone-constants.mjs +39 -5
- package/dist/keystone/keystone-constants.mjs.map +1 -1
- package/dist/keystone/keystone-helpers.d.mts +11 -1
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +37 -1
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-policy-types.d.mts +23 -0
- package/dist/keystone/keystone-policy-types.d.mts.map +1 -0
- package/dist/keystone/keystone-policy-types.mjs +2 -0
- package/dist/keystone/keystone-policy-types.mjs.map +1 -0
- package/dist/sync/graft-info/graft-info-helpers.respec.mjs +8 -8
- package/dist/sync/graft-info/graft-info-helpers.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs +22 -22
- package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-basic-divergence.respec.mjs +3 -3
- package/dist/sync/sync-conflict-basic-divergence.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs +6 -6
- package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-text-merge.respec.mjs +26 -26
- package/dist/sync/sync-conflict-text-merge.respec.mjs.map +1 -1
- package/dist/sync/sync-helpers.d.mts +19 -0
- package/dist/sync/sync-helpers.d.mts.map +1 -1
- package/dist/sync/sync-helpers.mjs +51 -1
- package/dist/sync/sync-helpers.mjs.map +1 -1
- package/dist/sync/sync-innerspace-constants.respec.mjs +2 -2
- package/dist/sync/sync-innerspace-constants.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs +2 -2
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs +4 -4
- package/dist/sync/sync-innerspace-dest-ahead.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs +2 -2
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-partial-update.respec.mjs +3 -3
- package/dist/sync/sync-innerspace-partial-update.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +4 -4
- package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts +5 -0
- package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs +18 -0
- package/dist/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts +5 -0
- package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs +21 -3
- package/dist/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts +12 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +34 -0
- package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts +69 -1
- package/dist/sync/sync-peer/sync-peer-types.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.d.mts +30 -0
- package/dist/sync/sync-peer/sync-peer-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs +88 -1
- package/dist/sync/sync-peer/sync-peer-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.d.mts +30 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mjs +2 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts +66 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs +280 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +85 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +332 -0
- package/dist/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-types.d.mts +29 -0
- package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-types.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mjs +2 -0
- package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts +42 -0
- package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.d.mts.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs +282 -0
- package/dist/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs.map +1 -0
- package/dist/sync/sync-saga-coordinator.d.mts +35 -1
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +62 -1
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-withid.connect.respec.d.mts +12 -0
- package/dist/sync/sync-withid.connect.respec.d.mts.map +1 -0
- package/dist/sync/sync-withid.connect.respec.mjs +205 -0
- package/dist/sync/sync-withid.connect.respec.mjs.map +1 -0
- package/dist/sync/sync-withid.establish.respec.d.mts +19 -0
- package/dist/sync/sync-withid.establish.respec.d.mts.map +1 -0
- package/dist/sync/sync-withid.establish.respec.mjs +322 -0
- package/dist/sync/sync-withid.establish.respec.mjs.map +1 -0
- package/package.json +1 -1
- package/src/keystone/keystone-config-builder.mts +73 -4
- package/src/keystone/keystone-constants.mts +42 -6
- package/src/keystone/keystone-helpers.mts +44 -2
- package/src/keystone/keystone-policy-types.mts +25 -0
- package/src/keystone/keystone-policy.schema.json +51 -0
- package/src/keystone/keystone-service-v1.mts +3 -3
- package/src/sync/README.md +1 -104
- package/src/sync/docs/architecture.md +28 -8
- package/src/sync/docs/security.md +380 -0
- package/src/sync/graft-info/graft-info-helpers.respec.mts +7 -7
- package/src/sync/sync-conflict-adv-multitimelines.respec.mts +21 -21
- package/src/sync/sync-conflict-basic-divergence.respec.mts +2 -2
- package/src/sync/sync-conflict-basic-multitimelines.respec.mts +5 -5
- package/src/sync/sync-conflict-text-merge.respec.mts +25 -25
- package/src/sync/sync-helpers.mts +51 -1
- package/src/sync/sync-innerspace-constants.respec.mts +1 -1
- package/src/sync/sync-innerspace-deep-updates.respec.mts +1 -1
- package/src/sync/sync-innerspace-dest-ahead.respec.mts +3 -3
- package/src/sync/sync-innerspace-multiple-timelines.respec.mts +1 -1
- package/src/sync/sync-innerspace-partial-update.respec.mts +2 -2
- package/src/sync/sync-innerspace.respec.mts +3 -3
- package/src/sync/sync-peer/sync-peer-http-receiver/sync-peer-http-receiver-v1.mts +20 -0
- package/src/sync/sync-peer/sync-peer-http-sender/sync-peer-http-sender-v1.mts +23 -3
- package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +38 -1
- package/src/sync/sync-peer/sync-peer-types.mts +70 -1
- package/src/sync/sync-peer/sync-peer-v1.mts +94 -1
- package/src/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-types.mts +36 -0
- package/src/sync/sync-peer/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mts +337 -0
- package/src/sync/sync-peer/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +388 -0
- package/src/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-types.mts +35 -0
- package/src/sync/sync-peer/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mts +321 -0
- package/src/sync/sync-saga-coordinator.mts +84 -0
- package/src/sync/sync-withid.connect.respec.mts +243 -0
- package/src/sync/sync-withid.establish.respec.mts +361 -0
- package/src/sync/unused-identity-backup.mts.md +1 -1
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.d.mts +0 -2
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.d.mts.map +0 -1
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs +0 -310
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs.map +0 -1
- package/src/sync/sync-innerspace-dest-ahead-withid.respec.mts +0 -364
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "KeystonePolicyConfigTemplate",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"behaviorProfiles": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"properties": {
|
|
11
|
+
"size": { "type": "integer", "minimum": 1 },
|
|
12
|
+
"replenish": { "type": "string", "enum": ["top-up", "replace-all", "consume", "delete-all"] },
|
|
13
|
+
"selectSequentially": { "type": "integer", "minimum": 0 },
|
|
14
|
+
"selectRandomly": { "type": "integer", "minimum": 0 },
|
|
15
|
+
"targetBindingChars": { "type": "integer", "minimum": 0 }
|
|
16
|
+
},
|
|
17
|
+
"required": ["size", "replenish", "selectSequentially", "selectRandomly", "targetBindingChars"]
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"pools": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"additionalProperties": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"id": { "type": "string" },
|
|
26
|
+
"allowedVerbs": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"items": { "type": "string" }
|
|
29
|
+
},
|
|
30
|
+
"behaviorProfile": { "type": "string" },
|
|
31
|
+
"behaviorInline": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"size": { "type": "integer", "minimum": 1 },
|
|
35
|
+
"replenish": { "type": "string", "enum": ["top-up", "replace-all", "consume", "delete-all"] },
|
|
36
|
+
"selectSequentially": { "type": "integer", "minimum": 0 },
|
|
37
|
+
"selectRandomly": { "type": "integer", "minimum": 0 },
|
|
38
|
+
"targetBindingChars": { "type": "integer", "minimum": 0 }
|
|
39
|
+
},
|
|
40
|
+
"required": ["size", "replenish", "selectSequentially", "selectRandomly", "targetBindingChars"]
|
|
41
|
+
},
|
|
42
|
+
"algo": { "type": "string", "enum": ["SHA-256", "SHA-512"] },
|
|
43
|
+
"rounds": { "type": "integer", "minimum": 1 },
|
|
44
|
+
"type": { "type": "string", "enum": ["hash-reveal-v1"] }
|
|
45
|
+
},
|
|
46
|
+
"required": ["id", "allowedVerbs", "algo", "rounds"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"required": ["pools"]
|
|
51
|
+
}
|
|
@@ -256,10 +256,10 @@ export class KeystoneService_V1 {
|
|
|
256
256
|
|
|
257
257
|
/**
|
|
258
258
|
* Retrieves the latest keystone frame for the given address.
|
|
259
|
-
*
|
|
259
|
+
*
|
|
260
260
|
* Uses `metaspace.getLatestAddr` to find the actual tip instead of trusting
|
|
261
261
|
* an incoming payload's `past` rel8n.
|
|
262
|
-
*
|
|
262
|
+
*
|
|
263
263
|
* @see `ibgib-get-pattern` SKILL.md
|
|
264
264
|
*/
|
|
265
265
|
async getLatestKeystone({
|
|
@@ -274,7 +274,7 @@ export class KeystoneService_V1 {
|
|
|
274
274
|
const lc = `${this.lc}[${this.getLatestKeystone.name}]`;
|
|
275
275
|
try {
|
|
276
276
|
if (logalot) { console.log(`${lc} starting...`); }
|
|
277
|
-
|
|
277
|
+
|
|
278
278
|
const latestAddr = await metaspace.getLatestAddr({ tjpAddr: addr, space });
|
|
279
279
|
if (!latestAddr) {
|
|
280
280
|
throw new Error(`could not find latest addr for ${addr}`);
|
package/src/sync/README.md
CHANGED
|
@@ -20,7 +20,6 @@ Init -> Ack -> Delta(s) -> Conflict/Commit via "ping pong" exchange.
|
|
|
20
20
|
|
|
21
21
|
1. **Init**: Sender initiates sync.
|
|
22
22
|
* Creates the `SyncSagaIbGib_V1` with an `Init` msg frame containing knowledge about domain ibgibs to sync.
|
|
23
|
-
* Optionally creates session keystone based off of Sender's primary keystone.
|
|
24
23
|
* Wraps the saga ibgib in a `SyncSagaContextIbGib_V1` and passes to the `SyncPeer_V1`.
|
|
25
24
|
2. **Ack**: Receiver gets incoming `SyncSagaContextIbGib_V1` and passes to its `SyncSagaCoordinator`.
|
|
26
25
|
* Handles the `Init` frame, comparing knowledge with Receiver's own status of each timeline/stone in domain ibgibs.
|
|
@@ -34,44 +33,11 @@ Init -> Ack -> Delta(s) -> Conflict/Commit via "ping pong" exchange.
|
|
|
34
33
|
* `SyncPeer_V1.payloadIbGibsDomainReceived$` is an observable for the actual domain payload ibgibs transmitted.
|
|
35
34
|
* So the a node sends domain payloads out in bulk to the `SyncPeer_V1`, receives the return saga ibgib with a manifest, but may have to wait for the observable to complete to actually process the ibgibs.
|
|
36
35
|
* `Delta` frame can include `requests` (e.g., asking for missing conflict history).
|
|
37
|
-
* Once no deltas required, `Delta` frame sets `proposeCommit` flag. If the other has nothing to add, the
|
|
36
|
+
* Once no deltas required, `Delta` frame sets `proposeCommit` flag. If the other has nothing to add, the saga transitions to `Commit`.
|
|
38
37
|
4. **Commit**: When receive `Delta` frame with `proposeCommit`, should trigger the receiver of this to start its commit. If that is successful, it should return a `Commit` frame, which signals the commit was successful on their end. When this `Commit` frame is received, this end should not perform its own commit.
|
|
39
38
|
* For local-only syncs, the `SyncPeer_V1` implementation is responsible for moving verified `ibGibs` from the temporary saga space to the durable/persistent domain space.
|
|
40
39
|
* From each endpoint's POV, the cleanup should remove the temp spaces used, in addition to any other required transaction cleanup (like closing any connections).
|
|
41
40
|
|
|
42
|
-
### Session Identity (`useSessionIdentity`)
|
|
43
|
-
|
|
44
|
-
The sync protocol can optionally generate an ephemeral Keystone Identity for the duration of the saga.
|
|
45
|
-
|
|
46
|
-
**Purpose**: To allow secure, signed exchanges even if the Node doesn't have a long-lived identity, or to isolate the sync session security from master identity.
|
|
47
|
-
|
|
48
|
-
**Current Status**:
|
|
49
|
-
* ✅ Session keystone creation implemented ([getSessionIdentity()](file:./sync-saga-coordinator.mts#L328-L362))
|
|
50
|
-
* ✅ Keystone passed via Init message ([initData.identity](file:./sync-saga-message/sync-saga-message-types.mts#L75))
|
|
51
|
-
* ⚠️ **Signature generation/verification: NOT yet implemented**
|
|
52
|
-
* ⚠️ **Per-frame proof creation: NOT yet implemented**
|
|
53
|
-
|
|
54
|
-
**Usage**:
|
|
55
|
-
```typescript
|
|
56
|
-
const { done } = await coordinator.sync({
|
|
57
|
-
peer,
|
|
58
|
-
domainIbGibs: [timeline],
|
|
59
|
-
localSpace,
|
|
60
|
-
metaspace,
|
|
61
|
-
useSessionIdentity: true, // Default, but currently non-functional
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
> [!CAUTION]
|
|
66
|
-
> **🚧 Session identity is NOT working yet.** Tests explicitly set `useSessionIdentity: false` to avoid failures. See [sync-innerspace-dest-ahead-withid.respec.mts](file:./sync-innerspace-dest-ahead-withid.respec.mts) for the failing test case.
|
|
67
|
-
|
|
68
|
-
**Authentication/Authorization Hooks**:
|
|
69
|
-
|
|
70
|
-
Validation occurs in [SyncPeer_V1.witness()](file:./sync-peer/sync-peer-v1.mts#L129-L219):
|
|
71
|
-
1. `validateContextAndSagaFrame()` - Intrinsic validation
|
|
72
|
-
2. `authenticateContext()` - Verify identity (stub)
|
|
73
|
-
3. `authorizeContext()` - Check permissions (stub)
|
|
74
|
-
|
|
75
41
|
### Conflict Resolution via Grafting
|
|
76
42
|
|
|
77
43
|
> [!IMPORTANT]
|
|
@@ -110,58 +76,6 @@ See [graft-info-helpers.mts](file:./graft-info/graft-info-helpers.mts) for imple
|
|
|
110
76
|
|
|
111
77
|
**Why**: Ensures atomic sync - either all data validates and commits, or none does.
|
|
112
78
|
|
|
113
|
-
|
|
114
|
-
## 🔒 Security Considerations
|
|
115
|
-
|
|
116
|
-
> [!WARNING]
|
|
117
|
-
> Session identity is **currently non-functional**. The mitigations below describe the intended security model.
|
|
118
|
-
|
|
119
|
-
### Attack Vectors & Mitigations
|
|
120
|
-
|
|
121
|
-
| Attack Vector | Description | Mitigation |
|
|
122
|
-
|---------------|-------------|------------|
|
|
123
|
-
| **MITM Replay** | Attacker replays captured saga frames | • Frame signatures include timestamp<br>• Freshness checks reject old frames<br>• Challenge depletion prevents reuse<br>• sagaId deduplication |
|
|
124
|
-
| **Session Fixation** | Attacker pre-generates session keystone | • Session secret derived via KDF (strategy in keystone metadata)<br>• `sessionSecret = KDF(masterSecret, sagaId, strategy)` |
|
|
125
|
-
| **Identity Spoofing** | Impersonation via fake session keystone | • Session keystone includes `derivedFrom` master address<br>• Receiver validates master keystone is known/trusted |
|
|
126
|
-
| **Challenge Grinding** | Brute-force challenge solutions for target binding | • Hash pre-imaging computationally infeasible<br>• Stochastic selection adds randomness |
|
|
127
|
-
| **DoS (Pool Exhaustion)** | Excessive syncs deplete challenge pools | • Top-up replenishment refills challenges<br>• Rate limiting per identity<br>• Pool separation for critical ops |
|
|
128
|
-
| **Saga Hijacking** | Mid-flight frame injection | • All frames signed with session keystone<br>• `past` rel8n creates tamper-evident chain |
|
|
129
|
-
|
|
130
|
-
### Cryptographic Primitives
|
|
131
|
-
|
|
132
|
-
* **Hash Function**: SHA-256 (content addressing, challenge generation)
|
|
133
|
-
* **Proof System**: Hash-reveal protocol (`hash-reveal-v1`)
|
|
134
|
-
* **Binding**: Target address → hex bucket → challenge selection
|
|
135
|
-
* **Entropy**: Stochastic challenge selection (anti-grinding)
|
|
136
|
-
|
|
137
|
-
### Trust Model
|
|
138
|
-
|
|
139
|
-
**Session Keystones**:
|
|
140
|
-
- Ephemeral identity per sync saga
|
|
141
|
-
- Derived from master keystone + `sagaId`
|
|
142
|
-
- Validates via proof-of-work (solving challenges)
|
|
143
|
-
- Post-hoc audit trail: master can prove session authorship
|
|
144
|
-
|
|
145
|
-
**Propagation**:
|
|
146
|
-
- Sync does NOT rely on global PKI or certificate authorities
|
|
147
|
-
- Trust propagates through sync sessions (Alice syncs with Bob → Bob witnesses Alice's keystone)
|
|
148
|
-
- Revocation propagates same way (Alice revokes → syncs revocation to Bob → Bob sees revoked timeline)
|
|
149
|
-
|
|
150
|
-
**Threat Boundaries**:
|
|
151
|
-
- ✅ Protects against: MITM replay, impersonation, frame tampering
|
|
152
|
-
- ⚠️ Does NOT protect against: Compromised endpoint (live memory access during active session)
|
|
153
|
-
- ⚠️ Master secrets: Raw secrets should NOT stored; keystones master secrets to keystones should be derived secrets via key stretching passwords. raw secret files of course can drive these passwords and must be protected
|
|
154
|
-
- ⚠️ Transport security: Use TLS for network layer encryption (ibgib protocol provides authentication/integrity, not confidentiality)
|
|
155
|
-
|
|
156
|
-
### Best Practices
|
|
157
|
-
|
|
158
|
-
1. **Master Secret Protection**: Store master secrets in secure enclaves/keychains
|
|
159
|
-
2. **Session Lifetime**: Limit saga duration, revoke sessions post-completion
|
|
160
|
-
3. **Rate Limiting**: Implement per-identity sync rate limits
|
|
161
|
-
4. **Freshness**: Reject frames older than 60 seconds
|
|
162
|
-
5. **Audit Logs**: Persist saga timelines for post-hoc validation (saga timelines are ibgibs, so integrity is built-in via content addressing)
|
|
163
|
-
6. **TLS**: Always use TLS for network transport (defense-in-depth)
|
|
164
|
-
|
|
165
79
|
## 📚 Documentation
|
|
166
80
|
* **[Architecture](./docs/architecture.md)**: Detailed design of the Saga Protocol, State Machine, and Smart Diff logic.
|
|
167
81
|
* **[Testing & Verification](./docs/testing.md)**: Test strategy, verification matrix, and comprehensive testing guidelines.
|
|
@@ -175,20 +89,3 @@ See [graft-info-helpers.mts](file:./graft-info/graft-info-helpers.mts) for imple
|
|
|
175
89
|
## Verification
|
|
176
90
|
We rely on rigorous In-Memory Simulation (`InnerSpace`) to verify complex graph scenarios. See **[Verification](./docs/verification.md)** for the full status.
|
|
177
91
|
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## ❓ Open Questions
|
|
181
|
-
|
|
182
|
-
### Payload Scope Validation: Can the Protocol Police What ibGibs Are Transmitted?
|
|
183
|
-
|
|
184
|
-
**Question**: During a sync session authorized by session substone S (itself authorized by domain keystone I with `claim.target = S^Stjp` and sync subject `X^X10.Xtjp`), should the server validate that all Delta payload ibgibs are genuinely related to X's timeline? Or is it sufficient that they arrive under a valid substone?
|
|
185
|
-
|
|
186
|
-
**Context**: The substone's `frameDetails` records both the parent identity `I^Itjp` and the sync subject `X^X10.Xtjp`. In theory the server could walk the dependency graph of each incoming Delta payload and confirm every ibgib is either:
|
|
187
|
-
- A control ibgib (sync saga, context, substone frames), or
|
|
188
|
-
- Part of the dependency graph rooted at some frame of X's timeline (`X^Xtjp`)
|
|
189
|
-
|
|
190
|
-
**Problem**: This check may not be enforceable at the protocol layer. A client who controls the keystone (has the master secret) could always evolve X's timeline to hard-link any arbitrary ibgib, and that ibgib would then pass the "is in X's dependency graph" check. So the constraint can be trivially bypassed by the legitimate key-holder, making it only useful against *illegitimate* actors — who are already blocked by the keystone proof requirement.
|
|
191
|
-
|
|
192
|
-
**Current position**: This is likely a **higher-layer business rule** rather than a sync protocol concern. The sync protocol's job is to ensure the session is cryptographically authorized (via keystone proofs). What is stored under an authorized session is the domain owner's responsibility. Enforcement of content policies (e.g., "only ibgibs of type X are allowed") belongs in the server's `authorizeContext` hook, not in the core sync coordinator.
|
|
193
|
-
|
|
194
|
-
**Tracked in**: `space-gib.sync-walkthrough.md`
|
|
@@ -21,7 +21,7 @@ The `SyncSagaCoordinator` drives a Finite State Machine (FSM) via the `handleSag
|
|
|
21
21
|
### 2.1 The Ping-Pong Flow
|
|
22
22
|
|
|
23
23
|
1. **Start (Initiator)**:
|
|
24
|
-
* Initiator (Alice) calls `
|
|
24
|
+
* Initiator (Alice) calls `sync`.
|
|
25
25
|
* Generates `Init` frame containing her `KnowledgeMap` and `Mode` (Push/Pull/Sync).
|
|
26
26
|
* Sends `Init` to Bob.
|
|
27
27
|
|
|
@@ -42,7 +42,7 @@ The `SyncSagaCoordinator` drives a Finite State Machine (FSM) via the `handleSag
|
|
|
42
42
|
|
|
43
43
|
4. **Convergence (`handleDeltaFrame`)**:
|
|
44
44
|
* Bob receives `Delta`.
|
|
45
|
-
* **Verifies**: Checks content-address hashes
|
|
45
|
+
* **Verifies**: Checks content-address hashes.
|
|
46
46
|
* **Merges**: Puts data into his local space.
|
|
47
47
|
* Bob sends `Commit` frame.
|
|
48
48
|
|
|
@@ -55,7 +55,7 @@ The `SyncSagaCoordinator` drives a Finite State Machine (FSM) via the `handleSag
|
|
|
55
55
|
### 3.1 SyncSagaMessage
|
|
56
56
|
All frames are `SyncIbGib`s carrying specific data payloads.
|
|
57
57
|
|
|
58
|
-
* `sagaId`: Unique UUID for the
|
|
58
|
+
* `sagaId`: Unique UUID for the duration of this exchange.
|
|
59
59
|
* `stage`: `init` | `ack` | `delta` | `commit` | `conflict`.
|
|
60
60
|
* `knowledgeMap`: `{ [tjpAddr]: [tipAddr, ...ancestors] }`.
|
|
61
61
|
|
|
@@ -65,8 +65,19 @@ The protocol supports two types of data:
|
|
|
65
65
|
* **Stones (Constants)**: Immutable data without a history (e.g., Code lists, Configs). Syncing involves simple existence checks.
|
|
66
66
|
|
|
67
67
|
## 4. Security & Identity
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
|
|
69
|
+
See [Security](./security.md) documentation.
|
|
70
|
+
|
|
71
|
+
### 4.1 Ephemeral Session Domain-Binding
|
|
72
|
+
To prevent an ephemeral session keystone `S` from acting as a "blank check" that could authorize operations on arbitrary timelines, the protocol implements strict domain-binding:
|
|
73
|
+
* **Genesis Binding**: The first frame of the session keystone ($S^{Stjp}$) must explicitly contain a `targetAddrs` array inside its `frameDetails` mapping to the exact addresses of the ibgibs being synchronized.
|
|
74
|
+
* **Verification Boundary**: During the initial turn-based synchronization (e.g., `handleInitFrame`), the validating peer (Receiver) must extract this `targetAddrs` array from $S^{Stjp}$'s genesis `frameDetails` and verify it contains the synchronizing domain's target address, rejecting the handshake on any mismatch.
|
|
75
|
+
|
|
76
|
+
## 5. Sync Peer Lifetime
|
|
77
|
+
|
|
78
|
+
A `SyncPeer` is **single-saga scoped**. One peer instance services exactly one `sync(...)` call from creation through saga completion. The coordinator's `newTestPeer()` factory pattern (or equivalent in production) must create a fresh peer per saga.
|
|
79
|
+
|
|
80
|
+
**Rationale**: The session keystone `S` is derived from `KDF(senderSecret, sagaId)`. Since `sagaId` is unique per saga, `S` is inherently saga-bound. A peer that outlived its saga would hold stale identity state, violating the security model.
|
|
70
81
|
|
|
71
82
|
> [!WARNING]
|
|
72
83
|
> ## 5. Sync Storage & Persistence Rules (Critical Security)
|
|
@@ -74,17 +85,26 @@ The protocol supports two types of data:
|
|
|
74
85
|
> Knowing **when** to put **what** ibgib frames into **which** space is a high-priority security concern. The sync algorithm must balance maintaining a robust audit trail with protecting durable spaces from unauthorized pollution. Always follow these rules:
|
|
75
86
|
>
|
|
76
87
|
> ### Rule 1: Saga/Control Audit Trail (Immediate Persistence)
|
|
77
|
-
> We need an audit trail of the sync saga itself (including context and
|
|
88
|
+
> We need an audit trail of the sync saga itself (including context and authentication info).
|
|
78
89
|
> * The initial session keystone (or any valid evolution performed locally) and saga control ibgibs are trusted because *we* just generated them.
|
|
79
90
|
> * Persist these immediately in the **local durable space** (and possibly the local temporary space if needed for transit) so they are available for validation and auditing.
|
|
80
91
|
>
|
|
81
92
|
> ### Rule 2: Domain IbGibs (Deferred Persistence)
|
|
82
93
|
> We do NOT persist *any* domain ibgibs (e.g., user payloads) into the durable space before the `commit` phase of the sync process.
|
|
83
|
-
> * Domain ibgibs must be routed to the **tempSpace** during the `delta` or `ack` phases.
|
|
94
|
+
> * Domain ibgibs must be routed to the **tempSpace** during the `delta` or `ack` phases.
|
|
84
95
|
> * The `commit` phase acts as the transaction boundary. By waiting for the commit, we avoid polluting durable spaces on error, preventing the need for complex rollbacks.
|
|
85
96
|
>
|
|
86
97
|
> ### Rule 3: Validate First, Store Second
|
|
87
98
|
> We never chance storing intrinsically invalid ibgibs.
|
|
88
99
|
> * **First step:** Intrinsically validate ibgibs (hashes, basic structure).
|
|
89
|
-
> * **Second step:** Perform AuthN/AuthZ checks (using the
|
|
100
|
+
> * **Second step:** Perform AuthN/AuthZ checks (using the session keystone).
|
|
90
101
|
> * **Final step:** Once verified, saga "control" ibgibs (which only contain "soft" links to domain ibgibs) can be safely persisted to durable spaces.
|
|
102
|
+
|
|
103
|
+
## 6. Transport & Framing (Naive WebSocket Implementation)
|
|
104
|
+
|
|
105
|
+
> [!NOTE]
|
|
106
|
+
> ### TODO: Robust WebSocket Framing & TCP Reassembly
|
|
107
|
+
> The current custom WebSocket integration on the node server (`ws-helper.mts`) relies on a naive framing implementation. It assumes each socket `data` event emitted by Node's TCP stream contains exactly one complete WebSocket frame.
|
|
108
|
+
>
|
|
109
|
+
> * **TCP Fragmentation Risk**: When payload sizes grow (e.g. sending larger cryptographic proofs or domain datasets), TCP will split the frame across multiple packets or buffers. The naive decoder will decode a partial frame, causing parse errors (e.g., `Unterminated string in JSON`).
|
|
110
|
+
> * **Future Action**: Replace the custom, naive WebSocket framing code with a robust stateful accumulator that aggregates incoming TCP chunks until the entire frame length (indicated in the frame header) is present in memory, or transition to a fully tested, production-grade library (such as the NPM `ws` package).
|