@ibgib/core-gib 0.1.41 → 0.1.43
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/sync/sync-conflict-adv-multitimelines.respec.mjs +2 -2
- package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs.map +1 -1
- package/dist/sync/sync-conflict-text-merge.respec.d.mts +4 -1
- package/dist/sync/sync-conflict-text-merge.respec.d.mts.map +1 -1
- package/dist/sync/sync-conflict-text-merge.respec.mjs +314 -181
- package/dist/sync/sync-conflict-text-merge.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.d.mts +7 -0
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs +252 -0
- package/dist/sync/sync-innerspace-dest-ahead-withid.respec.mjs.map +1 -0
- package/dist/sync/sync-saga-coordinator.mjs +3 -3
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/test/mock-space.d.mts +1 -38
- package/dist/test/mock-space.d.mts.map +1 -1
- package/dist/test/mock-space.mjs +73 -78
- package/dist/test/mock-space.mjs.map +1 -1
- package/package.json +1 -1
- package/src/keystone/README.md +118 -0
- package/src/keystone/docs/architecture.md +30 -1
- package/src/sync/README.md +122 -5
- package/src/sync/docs/architecture.md +2 -2
- package/src/sync/{SYNC_TESTING.md → docs/testing.md} +113 -28
- package/src/sync/sync-conflict-adv-multitimelines.respec.mts +3 -3
- package/src/sync/sync-conflict-text-merge.respec.mts +326 -164
- package/src/sync/sync-innerspace-dest-ahead-withid.respec.mts +316 -0
- package/src/sync/sync-saga-coordinator.mts +4 -4
- package/src/test/mock-space.mts +72 -72
- package/src/sync/docs/verification.md +0 -43
package/src/keystone/README.md
CHANGED
|
@@ -16,5 +16,123 @@ Validation in this system is **Auditability**: to verify an identity (e.g., "Is
|
|
|
16
16
|
3. **Hash-based**: Uses hash pre-imaging for quantum resistance (`hash-reveal-v1`).
|
|
17
17
|
4. **Merkle Graphs**: Efficient communication by only transmitting missing graph nodes.
|
|
18
18
|
|
|
19
|
+
## Challenge Pools
|
|
20
|
+
|
|
21
|
+
Keystones organize challenges into **pools** with specific purposes:
|
|
22
|
+
|
|
23
|
+
* **Default Pool**: General-purpose signing operations
|
|
24
|
+
* **Revoke Pool**: Permanent identity termination (one-time use, `verb: revoke`)
|
|
25
|
+
* **Manage Pool**: Structural changes like adding pools (`verb: manage`)
|
|
26
|
+
* **Foreign Pools**: Delegation to external identities (SSO-like workflows)
|
|
27
|
+
|
|
28
|
+
Each pool configuration specifies:
|
|
29
|
+
* **`poolId`**: Unique identifier within the keystone
|
|
30
|
+
* **`verb`**: Auto-routes operations (e.g., `revoke`, `manage`, `login`)
|
|
31
|
+
* **`replenishStrategy`**: `'top-up'` (reusable) or `'scorched-earth'` (burn after use)
|
|
32
|
+
* **`challengeCount`**: Number of hash challenges in the pool
|
|
33
|
+
|
|
34
|
+
## Basic Usage
|
|
35
|
+
|
|
36
|
+
### Genesis (Create Identity)
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
const keystone = await keystoneService.genesis({
|
|
40
|
+
masterSecret: "user-password",
|
|
41
|
+
configs: [
|
|
42
|
+
{ poolId: 'default', challengeCount: 100, replenishStrategy: 'top-up' },
|
|
43
|
+
{ poolId: 'revoke', verb: 'revoke', challengeCount: 10, replenishStrategy: 'scorched-earth' }
|
|
44
|
+
],
|
|
45
|
+
metaspace,
|
|
46
|
+
space
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Sign a Claim
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const signedKeystone = await keystoneService.sign({
|
|
54
|
+
latestKeystone: keystone,
|
|
55
|
+
claim: { action: 'login', timestamp: Date.now() },
|
|
56
|
+
masterSecret: "user-password",
|
|
57
|
+
poolId: 'default', // or use verb-based auto-routing
|
|
58
|
+
metaspace,
|
|
59
|
+
space
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Revoke Identity
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
await keystoneService.revoke({
|
|
67
|
+
latestKeystone: keystone,
|
|
68
|
+
reason: "Device compromised",
|
|
69
|
+
masterSecret: "user-password",
|
|
70
|
+
metaspace,
|
|
71
|
+
space
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Delegation via Foreign Pools
|
|
76
|
+
|
|
77
|
+
> [!IMPORTANT]
|
|
78
|
+
> **🔑 Foreign pools enable delegation** - Alice can grant Bob signing authority for specific operations.
|
|
79
|
+
|
|
80
|
+
Alice can delegate signing authority to Bob by adding his challenge pool:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const aliceWithBobPool = await keystoneService.addPools({
|
|
84
|
+
latestKeystone: aliceKeystone,
|
|
85
|
+
newPoolConfigs: [
|
|
86
|
+
{
|
|
87
|
+
poolId: 'bob-sso',
|
|
88
|
+
verb: 'login',
|
|
89
|
+
foreignServerUrl: 'https://bob-server.com',
|
|
90
|
+
challengeCount: 50
|
|
91
|
+
}
|
|
92
|
+
],
|
|
93
|
+
masterSecret: aliceSecret,
|
|
94
|
+
metaspace,
|
|
95
|
+
space
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Now Bob can sign login claims on Alice's behalf, but only for operations using the `login` verb.
|
|
100
|
+
|
|
101
|
+
**Trust Model**:
|
|
102
|
+
- Alice validates Bob's pool **shape** when adding (via `validateChallengePool`)
|
|
103
|
+
- Bob never transmits his secrets
|
|
104
|
+
- Communication secured via TLS
|
|
105
|
+
- Bob can validate his pool section when Alice evolves keystone
|
|
106
|
+
|
|
107
|
+
## Validation
|
|
108
|
+
|
|
109
|
+
To verify a keystone timeline:
|
|
110
|
+
|
|
111
|
+
1. **Replay** from genesis to current frame
|
|
112
|
+
2. **Verify** each proof's hash pre-images match challenges
|
|
113
|
+
3. **Validate** challenge selection matches policy engine rules
|
|
114
|
+
4. **Check** revocation status (if revoked, identity cannot evolve)
|
|
115
|
+
|
|
116
|
+
Validation helpers:
|
|
117
|
+
* `validateEvolution()`: Verifies transition from previous to current frame
|
|
118
|
+
* `validateKeystoneTimeline()`: Replays entire timeline from genesis
|
|
119
|
+
|
|
120
|
+
## Integration with Sync
|
|
121
|
+
|
|
122
|
+
Keystones integrate with the sync protocol to provide **session identity**:
|
|
123
|
+
|
|
124
|
+
* **Session Keystones**: Ephemeral identities created per sync saga
|
|
125
|
+
* **Saga Binding**: Session keystone salted with `sagaId`
|
|
126
|
+
* **Signature Frames**: Each saga frame can be signed with session identity
|
|
127
|
+
|
|
128
|
+
> [!CAUTION]
|
|
129
|
+
> **🚧 Session identity integration is NOT yet functional.** See [sync README](file:../sync/README.md#session-identity-usesessionidentity) for current status.
|
|
130
|
+
|
|
19
131
|
## Implementation
|
|
132
|
+
|
|
20
133
|
The v1 implementation creates a pure TypeScript, isomorphic, hash-based identity system. It is designed to be "Non-Evil" and "Graph-Native".
|
|
134
|
+
|
|
135
|
+
**Key Files**:
|
|
136
|
+
* [keystone-service-v1.mts](file:./keystone-service-v1.mts): Core service implementation
|
|
137
|
+
* [keystone-types.mts](file:./keystone-types.mts): TypeScript type definitions
|
|
138
|
+
* [keystone-helpers.mts](file:./keystone-helpers.mts): Utility functions
|
|
@@ -14,6 +14,33 @@
|
|
|
14
14
|
* **Challenge Pool**: A collection of puzzles stored in each Keystone Frame.
|
|
15
15
|
* **Constraint/Policy Engine**: Logic that determines *which* challenges must be solved to authorize a specific action (Binding, FIFO, Stochastic).
|
|
16
16
|
|
|
17
|
+
### Foreign Pools (Delegation)
|
|
18
|
+
|
|
19
|
+
> [!IMPORTANT]
|
|
20
|
+
> **🔀 Keystones support delegation** by embedding challenges from external identities.
|
|
21
|
+
|
|
22
|
+
**Mechanism**:
|
|
23
|
+
* **Use Case**: Alice adds Bob's challenge pool to her keystone
|
|
24
|
+
* **Validation**: Alice validates Bob's pool **shape** when adding (via `validateChallengePool`)
|
|
25
|
+
* **Signing**: Bob can sign claims on Alice's behalf (limited by `verb` field)
|
|
26
|
+
* **Trust Model**:
|
|
27
|
+
- Bob never transmits secrets across network
|
|
28
|
+
- Communication secured via TLS
|
|
29
|
+
- Bob can validate his pool section when Alice evolves keystone
|
|
30
|
+
* **SSO Pattern**: Enables Single Sign-On workflows without shared secrets
|
|
31
|
+
|
|
32
|
+
**Configuration**:
|
|
33
|
+
```typescript
|
|
34
|
+
{
|
|
35
|
+
poolId: 'bob-sso',
|
|
36
|
+
verb: 'login',
|
|
37
|
+
foreignServerUrl: 'https://bob-server.com',
|
|
38
|
+
challengeCount: 50
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
See [KeystoneService_V1.addPools()](file:../keystone-service-v1.mts) for implementation.
|
|
43
|
+
|
|
17
44
|
## 3. Policy Engine (Selection & Mitigation)
|
|
18
45
|
|
|
19
46
|
To prevent replay attacks by Man-in-the-Middle (MITM) actors, the challenges required to sign a frame are selected via a multi-layered pipeline:
|
|
@@ -45,7 +72,9 @@ Inputs: `LatestKeystone`, `Claim`, `MasterSecret`.
|
|
|
45
72
|
1. **Auto-Routing**: Selects pool based on Verb (e.g., 'revoke').
|
|
46
73
|
2. **Selection**: Runs Policy Engine.
|
|
47
74
|
3. **Solving**: Generates solutions.
|
|
48
|
-
4. **Replenishment**: Adds new challenges
|
|
75
|
+
4. **Replenishment**: Adds new challenges based on pool's `replenishStrategy`:
|
|
76
|
+
* **`'top-up'`**: Refills consumed challenges (default, reusable identity)
|
|
77
|
+
* **`'scorched-earth'`**: Burns challenges permanently (revocation, one-time operations)
|
|
49
78
|
|
|
50
79
|
### 5.3 Validate
|
|
51
80
|
Inputs: `PreviousFrame`, `CurrentFrame`.
|
package/src/sync/README.md
CHANGED
|
@@ -39,15 +39,132 @@ Init -> Ack -> Delta(s) -> Conflict/Commit via "ping pong" exchange.
|
|
|
39
39
|
* 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
40
|
* 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
41
|
|
|
42
|
-
###
|
|
42
|
+
### Session Identity (`useSessionIdentity`)
|
|
43
|
+
|
|
43
44
|
The sync protocol can optionally generate an ephemeral Keystone Identity for the duration of the saga.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
### Conflict Resolution via Grafting
|
|
76
|
+
|
|
77
|
+
> [!IMPORTANT]
|
|
78
|
+
> **🔀 When timelines diverge**, the sync protocol uses **grafting** to merge branches.
|
|
79
|
+
|
|
80
|
+
**Mechanism**:
|
|
81
|
+
1. **Detect divergence** in Ack stage (tips differ, neither recognizes other's tip)
|
|
82
|
+
2. **Exchange full timeline history** via `SyncSagaConflictInfo.receiverKnowledgeTimelineAddrs`
|
|
83
|
+
3. **Find Latest Common Ancestor (LCA)**
|
|
84
|
+
4. **Replay DNA transforms** from both branches
|
|
85
|
+
5. **Create graft timeline** with merged result
|
|
86
|
+
6. **Track via** `GraftInfoIbGib_V1` ([graft-info-types.mts](file:./graft-info/graft-info-types.mts))
|
|
87
|
+
|
|
88
|
+
**Conflict Info Tracking**:
|
|
89
|
+
* `graftInfoAddr`: Points to graft metadata
|
|
90
|
+
* `createdDnaAddrs`: DNA ibgibs created during merge
|
|
91
|
+
* `replayedDnaAddrs`: Original DNAs replayed
|
|
92
|
+
* `createdDomainAddrs`: Final timeline frames (in order)
|
|
93
|
+
|
|
94
|
+
See [graft-info-helpers.mts](file:./graft-info/graft-info-helpers.mts) for implementation.
|
|
95
|
+
|
|
96
|
+
### Transaction Model (Temp Spaces)
|
|
97
|
+
|
|
98
|
+
> [!NOTE]
|
|
99
|
+
> **💾 Sync uses a two-phase commit pattern** with temporary spaces for atomic operations.
|
|
100
|
+
|
|
101
|
+
**Phases**:
|
|
102
|
+
1. **Accumulation**: Domain ibgibs received during sync go to `tempSpace`
|
|
103
|
+
2. **Commit**: On successful saga completion, verified ibgibs move to durable `localSpace`
|
|
104
|
+
|
|
105
|
+
**Rollback**: If saga fails, temp space cleanup discards unverified data.
|
|
106
|
+
|
|
107
|
+
**Lifecycle**:
|
|
108
|
+
* **Created**: `tempSpace = await metaspace.createNewLocalSpace()` at saga start
|
|
109
|
+
* **Destroyed**: Cleanup function runs in `sync()` finally block
|
|
110
|
+
|
|
111
|
+
**Why**: Ensures atomic sync - either all data validates and commits, or none does.
|
|
112
|
+
|
|
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)
|
|
47
164
|
|
|
48
165
|
## 📚 Documentation
|
|
49
166
|
* **[Architecture](./docs/architecture.md)**: Detailed design of the Saga Protocol, State Machine, and Smart Diff logic.
|
|
50
|
-
* **[Verification](./docs/
|
|
167
|
+
* **[Testing & Verification](./docs/testing.md)**: Test strategy, verification matrix, and comprehensive testing guidelines.
|
|
51
168
|
|
|
52
169
|
## Features
|
|
53
170
|
* **Symmetric Design**: No distinct "Client" or "Server" logic.
|
|
@@ -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 hashes (`ib` vs `gib`)
|
|
45
|
+
* **Verifies**: Checks content-address hashes (`ib` vs `gib`). ⚠️ Keystone signature verification pending.
|
|
46
46
|
* **Merges**: Puts data into his local space.
|
|
47
47
|
* Bob sends `Commit` frame.
|
|
48
48
|
|
|
@@ -65,5 +65,5 @@ 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
|
-
* **Session Keystone**: Each Saga
|
|
68
|
+
* **Session Keystone**: Each Saga can optionally generate an ephemeral identity to sign frames, linked to a master identity. ⚠️ **Currently non-functional** - see [README Session Identity section](file:../README.md#session-identity-usesessionidentity) for status.
|
|
69
69
|
* **Trust Chain**: Each frame points to the previous one (`past` rel8n), creating a hash-linked chain of custody.
|
|
@@ -1,38 +1,116 @@
|
|
|
1
|
-
# Sync Testing
|
|
1
|
+
# Sync Protocol Testing & Verification
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This document outlines the testing strategy, verification results, and comprehensive guidelines for testing the Symmetric Sync Protocol.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
exponentially. I mean you won't believe just how vastly, hugely, mind-bogglingly
|
|
7
|
-
fast...
|
|
5
|
+
💣💥 **USE THESE GUIDELINES TO MITIGATE COMPLEXITY EXPLOSION** 💣💥
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
Sync needs thorough testing and complexity increases exponentially. Ridiculously exponentially. I mean you won't believe just how vastly, hugely, mind-bogglingly fast...
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Testing Strategy
|
|
12
|
+
|
|
13
|
+
We use `respec-gib` (a BDD-style framework) with **In-Memory Simulation (`InnerSpace`)** to verify logic. This allows us to test complex graph scenarios without network or disk I/O overhead.
|
|
14
|
+
|
|
15
|
+
### Key Test Files
|
|
16
|
+
|
|
17
|
+
To see the most up-to-date test files for sync, search inside `libs/core-gib/src/sync` for `*.respec.mts` files.
|
|
18
|
+
|
|
19
|
+
**Basic Scenarios**:
|
|
20
|
+
* `sync-innerspace.respec.mts`: Basic Push/Pull scenarios
|
|
21
|
+
* `sync-innerspace-dest-ahead.respec.mts`: Receiver has newer data scenarios
|
|
22
|
+
* `sync-innerspace-multiple-timelines.respec.mts`: Multiple independent timelines in one saga
|
|
23
|
+
* `sync-innerspace-partial-update.respec.mts`: Smart diff verification (delta-only transfers)
|
|
24
|
+
* `sync-innerspace-deep-updates.respec.mts`: Timelines with extensive history (past link traversal)
|
|
25
|
+
* `sync-innerspace-constants.respec.mts`: Synchronization of stones (constants/non-TJPs)
|
|
26
|
+
|
|
27
|
+
**Conflict Resolution**:
|
|
28
|
+
* `sync-conflict-basic-divergence.respec.mts`: Single timeline divergence and merge
|
|
29
|
+
* `sync-conflict-basic-multitimelines.respec.mts`: Multiple timelines with conflicts
|
|
30
|
+
* `sync-conflict-adv-multitimelines.respec.mts`: Advanced multi-timeline, multi-round sync
|
|
31
|
+
* `sync-conflict-text-merge.respec.mts`: LCS-based text merging for `data.text` fields
|
|
32
|
+
|
|
33
|
+
**Identity & Security**:
|
|
34
|
+
* `sync-innerspace-dest-ahead-withid.respec.mts`: Session identity test (🚧 **currently failing**)
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Verification Matrix
|
|
39
|
+
|
|
40
|
+
### 1. Basic Scenarios
|
|
41
|
+
| Feature | Description | Status | Test File |
|
|
42
|
+
| :--- | :--- | :--- | :--- |
|
|
43
|
+
| **Push** | Source syncs a new timeline to an empty Dest. | ✅ Verified | `sync-innerspace.respec.mts` |
|
|
44
|
+
| **Pull** | Dest requests data from Source (Reverse Sync). | ⏳ Pending | `sync-innerspace.respec.mts` |
|
|
45
|
+
| **Dest Ahead** | Dest has newer data; Source receives update. | ✅ Verified | `sync-innerspace-dest-ahead.respec.mts` |
|
|
46
|
+
|
|
47
|
+
### 2. Complex Graph Scenarios
|
|
48
|
+
| Feature | Description | Status | Test File |
|
|
49
|
+
| :--- | :--- | :--- | :--- |
|
|
50
|
+
| **Multi-Timeline** | Syncing multiple separate timelines. | ✅ Verified | `sync-innerspace-multiple-timelines.respec.mts` |
|
|
51
|
+
| **Deep Updates** | Syncing deep dependency chains (Ancestors). | ✅ Verified | `sync-innerspace-deep-updates.respec.mts` |
|
|
52
|
+
| **Smart Diff** | Sender skips data Receiver already has. | ✅ Verified | `sync-innerspace-partial-update.respec.mts` |
|
|
53
|
+
| **Constants** | Syncing immutable stones (no TJP). | ✅ Verified | `sync-innerspace-constants.respec.mts` |
|
|
54
|
+
|
|
55
|
+
### 3. Conflict Resolution & Edge Cases
|
|
56
|
+
| Feature | Description | Status | Test File |
|
|
57
|
+
| :--- | :--- | :--- | :--- |
|
|
58
|
+
| **Hash Validation** | Verifying integrity checks (ib/gib). | ✅ Verified | Built-in to all tests |
|
|
59
|
+
| **Basic Conflicts** | Divergent branches (single timeline). | ✅ Verified | `sync-conflict-basic-divergence.respec.mts` |
|
|
60
|
+
| **Multi-Timeline Conflicts** | Multiple timelines diverging. | ✅ Verified | `sync-conflict-basic-multitimelines.respec.mts` |
|
|
61
|
+
| **Advanced Conflicts** | Complex multi-round scenarios. | ✅ Verified | `sync-conflict-adv-multitimelines.respec.mts` |
|
|
62
|
+
| **Text Merge Conflicts** | LCS-based text merging. | ✅ Verified | `sync-conflict-text-merge.respec.mts` |
|
|
63
|
+
|
|
64
|
+
### 4. Security & Identity
|
|
65
|
+
| Feature | Description | Status | Test File |
|
|
66
|
+
| :--- | :--- | :--- | :--- |
|
|
67
|
+
| **Session Keystones** | Ephemeral identity for sync sessions. | 🚧 **Non-functional** | `sync-innerspace-dest-ahead-withid.respec.mts` |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Recent Verifications
|
|
72
|
+
|
|
73
|
+
### Sync Constants (2026-01-08)
|
|
74
|
+
* **Goal**: Ensure constants (ibGibs without `tjp` or `past`) are synced.
|
|
75
|
+
* **Result**: ✅ Passed.
|
|
76
|
+
* **Fixes**: Updated `handleInitFrame` to request missing stones; updated `handleAckFrame` to include "tip" stones in payload even without dependencies.
|
|
77
|
+
|
|
78
|
+
### Text Merge Conflicts (2026-01-14)
|
|
79
|
+
* **Goal**: Verify LCS-based text merging for divergent `data.text` fields.
|
|
80
|
+
* **Result**: ✅ Passed.
|
|
81
|
+
* **Implementation**: Uses Longest Common Subsequence algorithm for three-way merge.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Testing Guidelines
|
|
86
|
+
|
|
87
|
+
### Core Concepts
|
|
88
|
+
|
|
89
|
+
#### Rounds
|
|
12
90
|
|
|
13
91
|
Tests are organized into **rounds**. Each round represents a complete cycle of:
|
|
14
92
|
1. **State Setup**: Establish expected initial conditions
|
|
15
93
|
2. **Actions**: Perform mutations, relations, and sync operations
|
|
16
94
|
3. **Verification**: Assert final state expectations
|
|
17
95
|
|
|
18
|
-
The **first round**
|
|
19
|
-
subsequent rounds perform one or more op(s) and sync(s).
|
|
96
|
+
The **first round** establishes common history between source and dest, and subsequent rounds perform one or more op(s) and sync(s).
|
|
20
97
|
|
|
21
|
-
|
|
98
|
+
#### Broad Test Scenarios
|
|
22
99
|
|
|
23
100
|
Here are the broad category of ops that can occur to timelines WRT syncing:
|
|
24
101
|
* **Fast-Forward (Source Ahead)**: Source has newer versions that dest lacks
|
|
25
102
|
* **Fast-Forward (Dest Ahead)**: Dest has newer versions that source lacks
|
|
26
103
|
* **Conflict**: Both source and dest have divergent edits from a common ancestor
|
|
27
104
|
* **No Change**: Timeline exists on both sides with no edits
|
|
28
|
-
* **Stones**: Stones are treated slightly differently in the sync engine right
|
|
29
|
-
|
|
105
|
+
* **Stones**: Stones are treated slightly differently in the sync engine right now, so these must also be tested in various permutations.
|
|
106
|
+
|
|
107
|
+
---
|
|
30
108
|
|
|
31
109
|
## Variable Naming Convention
|
|
32
110
|
|
|
33
111
|
Variable names are a COMBINATION of BOTH snake_case AND camelCase:
|
|
34
112
|
|
|
35
|
-
Format
|
|
113
|
+
**Format**: `round_timeline_version_location_commentsHereInCamelCase_possiblyOtherComments`
|
|
36
114
|
|
|
37
115
|
### Components
|
|
38
116
|
|
|
@@ -54,6 +132,8 @@ r1_delta_v1_source_createdInR1_rel8edFromAlpha // Round 1, delta v1 with multip
|
|
|
54
132
|
r2_alpha_v4_dest_postConflict // Round 2, alpha v4 on dest, after conflict resolution
|
|
55
133
|
```
|
|
56
134
|
|
|
135
|
+
---
|
|
136
|
+
|
|
57
137
|
## Test Structure
|
|
58
138
|
|
|
59
139
|
### File Organization
|
|
@@ -144,8 +224,7 @@ Pattern examples:
|
|
|
144
224
|
|
|
145
225
|
* **`respecfully`**: Outer verification block per round with text "r{N} verify pre/post"
|
|
146
226
|
* **`ifWeMight`**: Per-timeline verification within a round
|
|
147
|
-
* NOTE: Graphs should be used for verification of sync, not individual addrs/ibgibs
|
|
148
|
-
in most (all?) cases
|
|
227
|
+
* NOTE: Graphs should be used for verification of sync, not individual addrs/ibgibs in most (all?) cases
|
|
149
228
|
* **`iReckon`**: Individual assertions
|
|
150
229
|
|
|
151
230
|
Example:
|
|
@@ -163,15 +242,15 @@ await respecfully(sir, `r1 verify results`, async () => {
|
|
|
163
242
|
});
|
|
164
243
|
```
|
|
165
244
|
|
|
245
|
+
---
|
|
246
|
+
|
|
166
247
|
## Multiple Syncs Per Round
|
|
167
248
|
|
|
168
249
|
A single round can have **multiple sync calls**. This is useful when:
|
|
169
250
|
* Creating multiple timeline pairs (some on source, some on dest)
|
|
170
251
|
* Testing specific sync scenarios in isolation
|
|
171
252
|
|
|
172
|
-
Key principle: Verify **final state** at end of round, not intermediate states
|
|
173
|
-
between syncs. We don't want to get bogged down trying to test every detail
|
|
174
|
-
after every sync. The broad coverage should handle this.
|
|
253
|
+
Key principle: Verify **final state** at end of round, not intermediate states between syncs. We don't want to get bogged down trying to test every detail after every sync. The broad coverage should handle this.
|
|
175
254
|
|
|
176
255
|
```typescript
|
|
177
256
|
// Round 1: Establish common history
|
|
@@ -187,6 +266,8 @@ await respecfully(sir, `r1 verify all timelines synced`, async () => {
|
|
|
187
266
|
});
|
|
188
267
|
```
|
|
189
268
|
|
|
269
|
+
---
|
|
270
|
+
|
|
190
271
|
## Domain-Level Focus
|
|
191
272
|
|
|
192
273
|
Advanced tests focus on **domain-level correctness**, not protocol mechanics:
|
|
@@ -195,6 +276,8 @@ Advanced tests focus on **domain-level correctness**, not protocol mechanics:
|
|
|
195
276
|
❌ **Don't use**: `delay()` calls to control base/orphan selection
|
|
196
277
|
✅ **Do focus on**: Data field correctness, relation preservation, dependency completeness
|
|
197
278
|
|
|
279
|
+
---
|
|
280
|
+
|
|
198
281
|
## Complexity Management
|
|
199
282
|
|
|
200
283
|
### Principles
|
|
@@ -207,14 +290,9 @@ Advanced tests focus on **domain-level correctness**, not protocol mechanics:
|
|
|
207
290
|
|
|
208
291
|
### Test Transformer
|
|
209
292
|
|
|
210
|
-
Tests are making relatively arbitrary fork/mut8/rel8 calls. To help reduce
|
|
211
|
-
implementation detail noise, the `TestTransformer` helper class was created.
|
|
212
|
-
|
|
213
|
-
These methods return a `TestStepInfo` which itself has references to the `ibGib`
|
|
214
|
-
and `addr`, as well as other metadata. So the variable
|
|
215
|
-
`r2_alpha_v2_source_arbitraryMut8` does NOT reference an ibgib, rather, it
|
|
216
|
-
references one of these test step info objects.
|
|
293
|
+
Tests are making relatively arbitrary fork/mut8/rel8 calls. To help reduce implementation detail noise, the `TestTransformer` helper class was created.
|
|
217
294
|
|
|
295
|
+
These methods return a `TestStepInfo` which itself has references to the `ibGib` and `addr`, as well as other metadata. So the variable `r2_alpha_v2_source_arbitraryMut8` does NOT reference an ibgib, rather, it references one of these test step info objects.
|
|
218
296
|
|
|
219
297
|
```typescript
|
|
220
298
|
const r2_beta_v0_source = await testTransformer.create({
|
|
@@ -233,6 +311,8 @@ const r2_alpha_v2_source_arbitraryMut8 = await testTransformer.mut8({
|
|
|
233
311
|
|
|
234
312
|
See `test-helpers.mts` and `test-types.mts` for implementations.
|
|
235
313
|
|
|
314
|
+
---
|
|
315
|
+
|
|
236
316
|
## Scenarios to Test
|
|
237
317
|
|
|
238
318
|
### Basic Scenarios
|
|
@@ -248,9 +328,14 @@ See `test-helpers.mts` and `test-types.mts` for implementations.
|
|
|
248
328
|
* **Reverse dependencies**: Timeline created on dest, source adds rel8n to it
|
|
249
329
|
* **Removal conflicts**: Source removes rel8n while dest mut8s the rel8'd ibgib
|
|
250
330
|
* **Chain edits**: Mutation → rel8 → mutation again on same timeline in one round
|
|
251
|
-
* **Text edits**: Edit ibGib.data.text
|
|
252
|
-
|
|
253
|
-
|
|
331
|
+
* **Text edits**: Edit `ibGib.data.text`. If divergent, uses LCS-based merge. See `sync-conflict-text-merge.respec.mts`.
|
|
332
|
+
|
|
333
|
+
### Security Scenarios
|
|
334
|
+
* **Session identity creation**: Verify ephemeral keystone generation (🚧 pending implementation)
|
|
335
|
+
* **Signature verification**: (🚧 pending implementation)
|
|
336
|
+
* **Authorization checks**: Verify identity-based permissions (🚧 pending implementation)
|
|
337
|
+
|
|
338
|
+
---
|
|
254
339
|
|
|
255
340
|
## Summary
|
|
256
341
|
|
|
@@ -114,6 +114,8 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
|
|
|
114
114
|
in: 'source',
|
|
115
115
|
name: 'r1_alpha_v0_source', // should be auto-calculated?
|
|
116
116
|
});
|
|
117
|
+
const alpha_tjpAddr =
|
|
118
|
+
getTjpAddr({ ibGib: r1_alpha_v0_source.ibGib, defaultIfNone: 'incomingAddr' })!;
|
|
117
119
|
|
|
118
120
|
// Create v1 (common) - will be synced to dest
|
|
119
121
|
const r1_alpha_v1_source_common = await testTransformer.mut8({
|
|
@@ -121,8 +123,6 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
|
|
|
121
123
|
strField: 'commonField',
|
|
122
124
|
name: 'r1_alpha_v1_source_common'
|
|
123
125
|
});
|
|
124
|
-
const alpha_tjpAddr =
|
|
125
|
-
getTjpAddr({ ibGib: r1_alpha_v0_source.ibGib, defaultIfNone: 'incomingAddr' })!;
|
|
126
126
|
|
|
127
127
|
// #endregion r1 setup
|
|
128
128
|
|
|
@@ -170,7 +170,7 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
|
|
|
170
170
|
});
|
|
171
171
|
|
|
172
172
|
// Get alpha_v1_common from destSpace for Round 2
|
|
173
|
-
const resGetDest = await getFromSpace({ space: destSpace, addr:
|
|
173
|
+
const resGetDest = await getFromSpace({ space: destSpace, addr: r1_alpha_v1_source_common.addr });
|
|
174
174
|
if (!resGetDest.success || !resGetDest.ibGibs || resGetDest.ibGibs.length === 0) {
|
|
175
175
|
throw new Error(`Failed to retrieve alpha from destSpace after sync. (E: 40c67811eba14f729880a46dcbb65126)`);
|
|
176
176
|
}
|