@ibgib/core-gib 0.1.19 → 0.1.20
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/common/other/ibgib-helper.d.mts +13 -0
- package/dist/common/other/ibgib-helper.d.mts.map +1 -1
- package/dist/common/other/ibgib-helper.mjs +44 -0
- package/dist/common/other/ibgib-helper.mjs.map +1 -1
- package/dist/sync/merge-info/merge-info-constants.d.mts +2 -0
- package/dist/sync/merge-info/merge-info-constants.d.mts.map +1 -0
- package/dist/sync/merge-info/merge-info-constants.mjs +2 -0
- package/dist/sync/merge-info/merge-info-constants.mjs.map +1 -0
- package/dist/sync/merge-info/merge-info-helpers.d.mts +51 -0
- package/dist/sync/merge-info/merge-info-helpers.d.mts.map +1 -0
- package/dist/sync/merge-info/merge-info-helpers.mjs +92 -0
- package/dist/sync/merge-info/merge-info-helpers.mjs.map +1 -0
- package/dist/sync/merge-info/merge-info-helpers.respec.d.mts +2 -0
- package/dist/sync/merge-info/merge-info-helpers.respec.d.mts.map +1 -0
- package/dist/sync/merge-info/merge-info-helpers.respec.mjs +32 -0
- package/dist/sync/merge-info/merge-info-helpers.respec.mjs.map +1 -0
- package/dist/sync/merge-info/merge-info-types.d.mts +26 -0
- package/dist/sync/merge-info/merge-info-types.d.mts.map +1 -0
- package/dist/sync/merge-info/merge-info-types.mjs +2 -0
- package/dist/sync/merge-info/merge-info-types.mjs.map +1 -0
- package/dist/sync/strategies/conflict-optimistic.d.mts +37 -0
- package/dist/sync/strategies/conflict-optimistic.d.mts.map +1 -0
- package/dist/sync/strategies/conflict-optimistic.mjs +162 -0
- package/dist/sync/strategies/conflict-optimistic.mjs.map +1 -0
- package/dist/sync/sync-conflict.respec.d.mts +8 -0
- package/dist/sync/sync-conflict.respec.d.mts.map +1 -0
- package/dist/sync/sync-conflict.respec.mjs +158 -0
- package/dist/sync/sync-conflict.respec.mjs.map +1 -0
- 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 +0 -1
- package/dist/sync/sync-innerspace-deep-updates.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace.respec.mjs +1 -1
- package/dist/sync/sync-innerspace.respec.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts +23 -12
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +473 -107
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts +11 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.d.mts.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs +24 -0
- package/dist/sync/sync-saga-message/sync-saga-message-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +23 -0
- package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
- package/dist/sync/sync-types.d.mts +31 -4
- package/dist/sync/sync-types.d.mts.map +1 -1
- package/dist/sync/sync-types.mjs.map +1 -1
- package/ibgib-foundations.md +129 -0
- package/package.json +1 -1
- package/roadmap.md +59 -0
- package/src/common/other/ibgib-helper.mts +52 -0
- package/src/keystone/README.md +13 -155
- package/src/keystone/docs/architecture.md +55 -0
- package/src/sync/README.md +37 -42
- package/src/sync/docs/architecture.md +69 -0
- package/src/sync/docs/verification.md +43 -0
- package/src/sync/merge-info/merge-info-constants.mts +1 -0
- package/src/sync/merge-info/merge-info-helpers.mts +134 -0
- package/src/sync/merge-info/merge-info-helpers.respec.mts +41 -0
- package/src/sync/merge-info/merge-info-types.mts +28 -0
- package/src/sync/strategies/conflict-optimistic.mts +208 -0
- package/src/sync/sync-conflict.respec.mts +194 -0
- package/src/sync/sync-innerspace-constants.respec.mts +1 -1
- package/src/sync/sync-innerspace-deep-updates.respec.mts +0 -1
- package/src/sync/sync-innerspace.respec.mts +1 -1
- package/src/sync/sync-saga-coordinator.mts +524 -118
- package/src/sync/sync-saga-message/sync-saga-message-helpers.mts +41 -0
- package/src/sync/sync-saga-message/sync-saga-message-types.mts +23 -0
- package/src/sync/sync-types.mts +33 -4
- package/tmp.md +2 -425
|
@@ -14,9 +14,61 @@ import { GLOBAL_LOG_A_LOT, IB_REGEXP_DEFAULT } from '../../core-constants.mjs';
|
|
|
14
14
|
import { SpecialIbGibType } from './other-types.mjs';
|
|
15
15
|
import { INVALID_DATE_STRING, SPECIAL_IBGIB_TYPE_REGEXP } from './other-constants.mjs';
|
|
16
16
|
import { CONFIG_KEY_ATOM } from './ibgib-constants.mjs';
|
|
17
|
+
import { IbGibSpaceAny } from '../../witness/space/space-base-v1.mjs';
|
|
18
|
+
import { getFromSpace } from '../../witness/space/space-helper.mjs';
|
|
17
19
|
|
|
18
20
|
const logalot = GLOBAL_LOG_A_LOT || false;
|
|
19
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Gets ibgibs that are directly related to the given `ibGib` via `rel8nNames`.
|
|
24
|
+
*
|
|
25
|
+
* @returns map of rel8nName -> ibgibs that are related.
|
|
26
|
+
*/
|
|
27
|
+
export async function getRel8dIbGibs({
|
|
28
|
+
ibGib,
|
|
29
|
+
rel8nNames,
|
|
30
|
+
space,
|
|
31
|
+
}: {
|
|
32
|
+
ibGib: IbGib_V1,
|
|
33
|
+
rel8nNames?: string[],
|
|
34
|
+
space: IbGibSpaceAny,
|
|
35
|
+
}): Promise<{ [rel8nName: string]: IbGib_V1[] }> {
|
|
36
|
+
const lc = `[${getRel8dIbGibs.name}]`;
|
|
37
|
+
try {
|
|
38
|
+
if (!ibGib) { throw new Error(`ibGib required (E: a3c1b2d4e5f6a7b8c9d0e1f2a3b4c5d6)`); }
|
|
39
|
+
if (!space) { throw new Error(`space required (E: b4d2c3e4f5a6b7c8d9e0f1a2b3c4d5e6)`); }
|
|
40
|
+
|
|
41
|
+
// Default to all rel8n names if none provided
|
|
42
|
+
if (!rel8nNames) {
|
|
43
|
+
rel8nNames = Object.keys(ibGib.rel8ns || {});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const rel8dIbGibs: { [rel8nName: string]: IbGib_V1[] } = {};
|
|
47
|
+
|
|
48
|
+
for (const rel8nName of rel8nNames) {
|
|
49
|
+
const rel8dAddrs = ibGib.rel8ns?.[rel8nName] || [];
|
|
50
|
+
|
|
51
|
+
if (rel8dAddrs.length > 0) {
|
|
52
|
+
const resGet = await getFromSpace({ addrs: rel8dAddrs, space });
|
|
53
|
+
if (resGet.success && resGet.ibGibs?.length === rel8dAddrs.length) {
|
|
54
|
+
rel8dIbGibs[rel8nName] = resGet.ibGibs.concat();
|
|
55
|
+
} else {
|
|
56
|
+
// Start logging warnings instead of throwing immediately?
|
|
57
|
+
// For now, consistent with user request to adapt legacy code
|
|
58
|
+
throw new Error(`Problem getting rel8d ibgibs for rel8nName: ${rel8nName}. ${resGet.errorMsg || 'Unknown error'} (E: c5e3d4f5a6b7c8d9e0f1a2b3c4d5e6)`);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
rel8dIbGibs[rel8nName] = [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return rel8dIbGibs;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
20
72
|
/**
|
|
21
73
|
* Utility function to generate hard-coded ibgibs to use at runtime "on-chain" but
|
|
22
74
|
* written at compile-time in (for now) "off-chain" source code.
|
package/src/keystone/README.md
CHANGED
|
@@ -1,162 +1,20 @@
|
|
|
1
1
|
# keystone-gib
|
|
2
2
|
|
|
3
|
-
"Keystones" are a new ibgib-specific identity mechanism that leverages ibgib's unique Merkle-based DLT structure.
|
|
3
|
+
"Keystones" are a new ibgib-specific identity mechanism that leverages ibgib's unique Merkle-based DLT structure.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 📚 Documentation
|
|
6
|
+
* **[Architecture](./docs/architecture.md)**: Detailed design of the Policy Engine, Hash-Based Cryptography, and Lifecycle.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
## Overview
|
|
9
|
+
A **Keystone** is a "Stone" (ibgib without `dna`) used for Identity. It is a graph of ibgib frames that evolves over time but maintains a strict, verifiable lineage.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Validation in this system is **Auditability**: to verify an identity (e.g., "Is this Alice?"), one "replays" the Keystone timeline from its Genesis to the current Frame, verifying the cryptographic proofs at every step.
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
## Principles
|
|
14
|
+
1. **DRY**: Leverages the ubiquity of ibgib's existing crypto hypergraph.
|
|
15
|
+
2. **Non-Evil Root**: Prioritizes security/correctness over pre-optimization (signing speed).
|
|
16
|
+
3. **Hash-based**: Uses hash pre-imaging for quantum resistance (`hash-reveal-v1`).
|
|
17
|
+
4. **Merkle Graphs**: Efficient communication by only transmitting missing graph nodes.
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* **Validation is Replay:** In traditional PKI, a Certificate Authority (CA) acts as a centralized trust anchor to establish a secure channel. In ibgib, validation is **Auditability**. To verify an identity (e.g., "Is this Alice?"), one "replays" the Keystone timeline from its Genesis to the current Frame, verifying the cryptographic proofs at every step.
|
|
17
|
-
* **Propagation as Witnessing:** When a Keystone Frame is transmitted from Alice's Space to Bob's Space, Bob "witnesses" it. By accepting it into his Space, he effectively votes on its validity. If it is invalid, it is rejected and does not propagate.
|
|
18
|
-
|
|
19
|
-
## principles
|
|
20
|
-
|
|
21
|
-
### DRY - leverage the ubiquity of ibgib's already-existing crypto hypergraph
|
|
22
|
-
|
|
23
|
-
Ibgib already provides the foundations of hypergraph generation dynamics. This is core to the very being of all data within ibgib. Therefore, we do not need a separate library/algorithm that only works with specialized Merkle-based technologies like many NIST-approved technologies. We leverage the existing ibgib mechanism without having to maintain two codebases for things that do essentially the same thing.
|
|
24
|
-
|
|
25
|
-
With ibgib, keystones are one reified use case of the more general ibgib timeline dynamics.
|
|
26
|
-
|
|
27
|
-
### Non-Evil Root - ignore signing pre-optimization
|
|
28
|
-
|
|
29
|
-
Similar to the slowness of some blockchains, like Bitcoin, we prioritize security first over other optimization dimensions like signing speed or size of keys.
|
|
30
|
-
|
|
31
|
-
The "speed" of the security can occur in what I call "scoping", but this assumes a relatively small number of connections. This is not designed for general-purpose internet traffic, where signing size/rates are paramount.
|
|
32
|
-
|
|
33
|
-
### Hash-based - pre-imaging is hard
|
|
34
|
-
|
|
35
|
-
If this is not true, then the security of keystones fails. This does not mean that all hashes are equal of course, but the general principle exists.
|
|
36
|
-
|
|
37
|
-
Note that, similar to PoW, hash-based challenges must be hard only for the time of the keystone's proximal use case. The keystones themselves must be spread with witnessing protocols to make them durable beyond that proximal window.
|
|
38
|
-
|
|
39
|
-
### The need for `ib` - collisions in our future
|
|
40
|
-
|
|
41
|
-
We are nearing a future where hash-spaces may seem large to us now, much like our previous beliefs that 1 MB of RAM was a lot. But this is misleading of course. With the amount of hashes that will be used (not only in ibgib, but other technologies as well as this will be the primary addressing mechanism across all software) these hash spaces will shrink precipitously. It's a small world after all.
|
|
42
|
-
|
|
43
|
-
This is why ibgib's addressing mechanism has a per-use-case `ib` component, to reduce accidental collisions in a world of ubiquitous hashing.
|
|
44
|
-
|
|
45
|
-
### Merkle graphs - inherently optimal communication
|
|
46
|
-
|
|
47
|
-
A keystone is an entire dependency graph, not a single ibgib frame. This is like thinking, in today's understanding, of a keystone as an entire repo and not a single commit. Though, due to the coolness of Merkle graphs, we do not always need to transmit the entire dependency graph, just as we do not need to push/pull an entire repo every time. This is an inherent quality of Merkle graph transmission and is heavily relied upon.
|
|
48
|
-
|
|
49
|
-
## current implementation
|
|
50
|
-
|
|
51
|
-
The v1 implementation creates a pure TypeScript, isomorphic, hash-based identity system. It is designed to be "Non-Evil" (prioritizing security/correctness over bandwidth) and "Graph-Native" (the signature is part of the DAG).
|
|
52
|
-
|
|
53
|
-
### jargon - the language of the stone
|
|
54
|
-
|
|
55
|
-
* **Stone:** Any ibgib without `dna`. A constant or fixed point in the graph.
|
|
56
|
-
* **Keystone:** A specific type of Stone used for Identity. It is a graph of ibgib frames that evolves over time but maintains a strict, verifiable lineage.
|
|
57
|
-
* **Challenge Pool:** A collection of puzzles stored in a Keystone Frame.
|
|
58
|
-
* **Challenge/Solution:** An abstract pair where the `Challenge` is public and the `Solution` is private. Revealing the Solution proves authorization.
|
|
59
|
-
* **Claim:** The semantic intent of a signature. Composed of a `target` (ibgib addr), `verb` (e.g., "post"), and optional `scope`.
|
|
60
|
-
* **Proof:** The structure linking a `Claim` to a set of revealed `Solutions`.
|
|
61
|
-
* **Policy:** The strict rules determining *which* challenges must be solved to authorize a specific Claim.
|
|
62
|
-
|
|
63
|
-
### policy engine - selection and mitigation
|
|
64
|
-
|
|
65
|
-
Why do we need complex selection logic?
|
|
66
|
-
|
|
67
|
-
In a Keystone system, authorizing a frame involves **publicly revealing** solutions to challenges. Once revealed, a solution is known to the network. If a Man-In-The-Middle (MITM) isolates a victim, they might attempt to **replay** these known solutions to forge new frames.
|
|
68
|
-
|
|
69
|
-
Our policy engine enforces a multi-layered challenge selection pipeline to mitigate this. To sign a claim, the challenges consumed must satisfy **ALL** of the following layers (without double-dipping):
|
|
70
|
-
|
|
71
|
-
1. **Mandatory Demands (The "Alice" Layer):**
|
|
72
|
-
* *Mechanism:* The verifier (or context) explicitly demands specific `challengeIds`.
|
|
73
|
-
* *Why:* Enables parties to proactively include challenges if they think the existing layers are insufficient, e.g., Alice can demand Bob select certain challenges that Alice thinks may be stronger/more secure (for whatever reason).
|
|
74
|
-
2. **Target Binding (The "Content" Layer):**
|
|
75
|
-
* *Mechanism:* Challenges are explicitly mapped to buckets (e.g., Hex Characters), and the first N number of characters in the target `ibgib.gib` (frame's hash) are required to be solved.
|
|
76
|
-
* *Why:* **Content-Dependent Binding.** A revealed solution for "Target A" with first 5 characters of "a1c04" cannot be used to sign "a40d9", because they only overlap with "a" and "0". The attacker has to forge an ibgib frame whose hash matches the target binded characters in the target addr, which is hard, similar to existing PoW leading-zero hashing. This becomes harder with more binding characters required.
|
|
77
|
-
3. **FIFO (The "Order" Layer):**
|
|
78
|
-
* *Mechanism:* Must consume $N$ challenges from the "front" of the pool.
|
|
79
|
-
* *Why:* Enables lower security but faster keystones.
|
|
80
|
-
4. **Stochastic (The "Chaos" Layer):**
|
|
81
|
-
* *Mechanism:* Randomly select $N$ additional challenges from the remainder.
|
|
82
|
-
* *Why:* Mitigates pre-computation attacks and grinding.
|
|
83
|
-
|
|
84
|
-
#### replenishment - sustaining the timeline
|
|
85
|
-
|
|
86
|
-
Once challenges are consumed to sign a frame, the pool must react. This dictates the longevity and bandwidth profile of the identity.
|
|
87
|
-
|
|
88
|
-
* **Top-Up (Standard):**
|
|
89
|
-
* *Mechanism:* Generate $N$ new challenges to replace the $N$ consumed ones.
|
|
90
|
-
* *Use Case:* Long-lived identities (e.g., User Profiles). Maintains a constant pool size and consistent entropy.
|
|
91
|
-
* **Replace-All (Paranoid):**
|
|
92
|
-
* *Mechanism:* Discard *all* remaining challenges in the pool and generate a completely fresh set.
|
|
93
|
-
* *Use Case:* High-security contexts where one desires total forward secrecy for the entire pool after any usage. High bandwidth cost.
|
|
94
|
-
* **Consume (Burn):**
|
|
95
|
-
* *Mechanism:* Remove consumed challenges. Do *not* add new ones.
|
|
96
|
-
* *Use Case:* Revocation (Scorched Earth) or One-Time-Use ephemeral identities. When the pool is empty, the capability associated with that pool is permanently disabled.
|
|
97
|
-
|
|
98
|
-
### cryptographic engine - `strategy` is a concrete challenge/solution discriminator
|
|
99
|
-
|
|
100
|
-
While the Policy Engine handles *selection*, the Cryptographic Engine handles the concrete specifics.
|
|
101
|
-
|
|
102
|
-
We currently implement the `hash-reveal-v1` strategy, with other possibilities on the horizon:
|
|
103
|
-
|
|
104
|
-
* simpler math challenges
|
|
105
|
-
* for testing
|
|
106
|
-
* for low/no-security
|
|
107
|
-
* for other non-security-based requirements (captcha)
|
|
108
|
-
* encryption-based solutions
|
|
109
|
-
* must decrypt with known qualities
|
|
110
|
-
* can be much more novel, such as decrypt challenge text to a sonnet verified by an agent
|
|
111
|
-
* PoW-style solutions
|
|
112
|
-
* config qualities like leading/trailing 0s
|
|
113
|
-
* not necessarily known ahead of time/at time of challenge-generation
|
|
114
|
-
|
|
115
|
-
#### `strategy: 'hash-reveal-v1'`
|
|
116
|
-
|
|
117
|
-
In the `hash-reveal-v1` strategy, we do not use RSA or Elliptic Curves. We use
|
|
118
|
-
**Hash-Based Cryptography** preimages for quantum resistance and simplicity.
|
|
119
|
-
These are configurable in algorithm and number of recursive rounds.
|
|
120
|
-
|
|
121
|
-
* **The Mechanism:** A variation of a Sigma protocol.
|
|
122
|
-
* Alice commits to a value $H$ (The Challenge) in Frame $N$.
|
|
123
|
-
* To authorize Frame $N+1$, Alice reveals the pre-image $S$ (The Challenge's Solution) such that $Hash(S) = H$ (with `$Hash` being parameterizable in algo/rounds).
|
|
124
|
-
* **Key Derivation (The Salted Wrap):**
|
|
125
|
-
* To prevent exposure of the Master Secret, we use a derivation chain:
|
|
126
|
-
* `MasterSecret` + `PoolSalt` $\to$ `PoolSecret`
|
|
127
|
-
* `PoolSecret` + `ChallengeID` $\to$ `Solution`
|
|
128
|
-
* `Solution` $\to$ `Challenge`
|
|
129
|
-
* *Note:* We use a "Salted Wrap" technique (hashing `salt + secret + salt`) to act as a rudimentary Key Derivation Function without external dependencies that mitigates against length extension attacks.
|
|
130
|
-
|
|
131
|
-
### spacetime integration - the service facade
|
|
132
|
-
|
|
133
|
-
The `KeystoneService_V1` acts as a consumer's API for keystone manipulations. These include both the cryptographic requirements, as well as persistence in ibgib spaces via the metaspace. It ensures that every cryptographic action is executed and atomically persisted to the local Space.
|
|
134
|
-
|
|
135
|
-
#### 1. genesis (creation)
|
|
136
|
-
* **Input:** A Master Secret and Pool Configs.
|
|
137
|
-
* **Process:** Generates the initial challenges and the Genesis Frame.
|
|
138
|
-
* **Persistence:** The new Identity is immediately `put` into the local `Space`.
|
|
139
|
-
|
|
140
|
-
#### 2. sign (evolution)
|
|
141
|
-
* **Input:** The Latest Keystone, Master Secret, Claim, and optional Demands.
|
|
142
|
-
* **Process:**
|
|
143
|
-
1. **Auto-Routing:** Locates the correct Pool based on the Claim's Verb (e.g., routing "revoke" to the revocation pool).
|
|
144
|
-
2. **Selection:** Runs the Policy Engine (Binding -> FIFO -> Stochastic) to select challenges.
|
|
145
|
-
3. **Solving:** Generates solutions using the Master Secret.
|
|
146
|
-
4. **Replenishment:** Tops up the pool with fresh challenges (or burns them, depending on strategy).
|
|
147
|
-
* **Persistence:** The new Frame (containing the Proof) is persisted to the local `Space`. The `target` address is now cryptographically linked to this new frame.
|
|
148
|
-
|
|
149
|
-
#### 3. validate (audit)
|
|
150
|
-
* **Input:** The Previous Frame and the Current Frame.
|
|
151
|
-
* **Process:**
|
|
152
|
-
1. **Policy Check:** Reconstructs the deterministic requirements (Demands, Binding, FIFO) and ensures the Proof satisfies them.
|
|
153
|
-
2. **Crypto Check:** Verifies the revealed Solutions match the Challenges in the Previous Frame.
|
|
154
|
-
* **Result:** If valid, the Current Frame is accepted as the legitimate successor.
|
|
155
|
-
|
|
156
|
-
#### 4. revoke (kill switch)
|
|
157
|
-
* **Input:** The Latest Keystone and Master Secret.
|
|
158
|
-
* **Process:**
|
|
159
|
-
1. Locates the specialized **Revocation Pool**.
|
|
160
|
-
2. Consumes **ALL** challenges in that pool ("Scorched Earth").
|
|
161
|
-
3. Sets the `revocationInfo` on the new frame.
|
|
162
|
-
* **Result:** The Identity is permanently marked as dead. No further valid frames can be generated because the revocation pool (required for the 'revoke' verb) is empty.
|
|
19
|
+
## Implementation
|
|
20
|
+
The v1 implementation creates a pure TypeScript, isomorphic, hash-based identity system. It is designed to be "Non-Evil" and "Graph-Native".
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Keystone Identity Architecture
|
|
2
|
+
|
|
3
|
+
"Keystones" combine the concept of a cryptographic key with the ibgib concept of a "stone" (immutable data) to create an audit-trail-based identity system.
|
|
4
|
+
|
|
5
|
+
## 1. Principles
|
|
6
|
+
|
|
7
|
+
* **Auditability as Validation**: Validation is not checking a certificate authority; it is replaying the history of the Keystone timeline to verify the cryptographic proofs from Genesis to present.
|
|
8
|
+
* **Propagation as Witnessing**: When a Space accepts a Keystone frame, it "witnesses" it, effectively voting on its validity.
|
|
9
|
+
* **Hash-Based (Post-Quantum)**: Security relies on hash pre-imaging difficulty (`hash-reveal-v1`) rather than RSA/ECC, leveraging the intrinsic properties of the ibgib DAG.
|
|
10
|
+
|
|
11
|
+
## 2. Core Concepts
|
|
12
|
+
|
|
13
|
+
* **Keystone**: An identity timeline. A graph of ibgib frames evolving over time.
|
|
14
|
+
* **Challenge Pool**: A collection of puzzles stored in each Keystone Frame.
|
|
15
|
+
* **Constraint/Policy Engine**: Logic that determines *which* challenges must be solved to authorize a specific action (Binding, FIFO, Stochastic).
|
|
16
|
+
|
|
17
|
+
## 3. Policy Engine (Selection & Mitigation)
|
|
18
|
+
|
|
19
|
+
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:
|
|
20
|
+
|
|
21
|
+
1. **Mandatory Demands**: The verifier explicitly requests specific challenge IDs.
|
|
22
|
+
2. **Target Binding**: Challenges are mapped to buckets (e.g., hex characters). Signing a target `a1c04...` requires solving challenges in the `a`, `1`, `c` buckets. This creates **Content-Dependent Binding**.
|
|
23
|
+
3. **FIFO**: Consumes $N$ challenges from the "front" of the pool (Performance).
|
|
24
|
+
4. **Stochastic**: Randomly selects $N$ additional challenges (Anti-Grinding).
|
|
25
|
+
|
|
26
|
+
## 4. Cryptographic Strategy (`hash-reveal-v1`)
|
|
27
|
+
|
|
28
|
+
A variation of a Sigma protocol using Hash-Based Cryptography.
|
|
29
|
+
|
|
30
|
+
* **Commit**: Alice publishes $H = Hash(S)$ in Frame $N$.
|
|
31
|
+
* **Reveal**: To authorize Frame $N+1$, Alice reveals $S$.
|
|
32
|
+
* **Derived Secrets**:
|
|
33
|
+
* `MasterSecret` + `PoolSalt` -> `PoolSecret`
|
|
34
|
+
* `PoolSecret` + `ChallengeID` -> `Solution`
|
|
35
|
+
* `Solution` -> `Challenge`
|
|
36
|
+
|
|
37
|
+
## 5. Lifecycle
|
|
38
|
+
|
|
39
|
+
### 5.1 Genesis
|
|
40
|
+
Inputs: `MasterSecret`, `PoolConfig`.
|
|
41
|
+
Generates initial challenges and the Genesis Frame. Persists to local Space.
|
|
42
|
+
|
|
43
|
+
### 5.2 Sign (Evolution)
|
|
44
|
+
Inputs: `LatestKeystone`, `Claim`, `MasterSecret`.
|
|
45
|
+
1. **Auto-Routing**: Selects pool based on Verb (e.g., 'revoke').
|
|
46
|
+
2. **Selection**: Runs Policy Engine.
|
|
47
|
+
3. **Solving**: Generates solutions.
|
|
48
|
+
4. **Replenishment**: Adds new challenges (Top-Up) or burns them.
|
|
49
|
+
|
|
50
|
+
### 5.3 Validate
|
|
51
|
+
Inputs: `PreviousFrame`, `CurrentFrame`.
|
|
52
|
+
Re-runs the Policy Engine to ensure the correct challenges were solved and verifies the hash pre-images.
|
|
53
|
+
|
|
54
|
+
### 5.4 Revoke
|
|
55
|
+
Consumes **ALL** challenges in the revocation pool ("Scorched Earth"). The identity can no longer evolve.
|
package/src/sync/README.md
CHANGED
|
@@ -11,45 +11,40 @@ The synchronization process is modeled as a "Saga" — a linear but distributed
|
|
|
11
11
|
* **[SyncPeerWitness](file:./sync-peer/sync-peer-types.mts)**: The interface for the "Transport Layer". It abstracts the communication channel (Method Call, HTTP, WebSocket) into a standard Witness pattern.
|
|
12
12
|
* **[SyncPeer_V1](file:./sync-peer/sync-peer-v1.mts)** provides the base plumbing.
|
|
13
13
|
* Concrete implementations (e.g., in-memory) handle the actual byte transfer.
|
|
14
|
-
* **[SyncSagaContextIbGib_V1](file:./sync-saga-context/sync-saga-context-types.mts)**: The "Transport Envelope". This is the payload passed to the Witness. It contains the current Saga Frame, the Message Stones (Data), and any cryptographic proofs required for the step
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
###
|
|
34
|
-
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
### 4. Conflict Resolution (Optimistic)
|
|
53
|
-
* [ ] **Simple Branch (Divergent)**: Handling trivial forks.
|
|
54
|
-
* [ ] **Mixed Edits**: Concurrent modifications to the same timeline.
|
|
55
|
-
* [ ] **Optimistic Merge**: Automatic reconciliation of non-conflicting branches.
|
|
14
|
+
* **[SyncSagaContextIbGib_V1](file:./sync-saga-context/sync-saga-context-types.mts)**: The "Transport Envelope". This is the payload passed to the Witness. It contains the current Saga Frame, the Message Stones (Data), and any cryptographic proofs required for the step.## Conflict Resolution & Protocol Refinements
|
|
15
|
+
|
|
16
|
+
### Unified Ack Protocol
|
|
17
|
+
To optimize the handshake, the `Ack` frame now carries both synchronization data (requests/offers) AND conflict information.
|
|
18
|
+
* **Conflicts**: If the Receiver detects divergence (multiple timeline tips), it includes a `conflicts` array in the `Ack`.
|
|
19
|
+
* **Structure**: Each conflict includes the `tjpAddr`, `localAddr` (Receiver's Tip), `remoteAddr` (Sender's Tip), and crucially `timelineAddrs` (History).
|
|
20
|
+
|
|
21
|
+
### Iterative Delta Protocol (Ping-Pong)
|
|
22
|
+
The `Delta` phase is no longer a single push. It is an iterative negotiation:
|
|
23
|
+
1. **Symmetrical**: Both Sender and Receiver can send `Delta` frames.
|
|
24
|
+
2. **Payloads**: Frames carry `payloadAddrs` (manifest) and the actual data is persisted to the peer's space.
|
|
25
|
+
3. **Requests**: A Delta can include `requests` (e.g., asking for missing conflict history).
|
|
26
|
+
4. **Merge**: If a peer resolves a conflict locally (using `optimistic` strategies), the resulting **Merge Frame** is added to the outgoing payload.
|
|
27
|
+
5. **Completion**: A `Delta` frame includes a `proposeCommit` flag. If one peer sets this and the other has nothing to add, the session transitions to `Commit`.
|
|
28
|
+
|
|
29
|
+
### Commit Phase
|
|
30
|
+
* **Persistence**: The `Commit` frame signals success.
|
|
31
|
+
* **Cleanup**: The `SyncPeer` is responsible for moving verified `ibGibs` from the temporary saga space to the persistent domain space.
|
|
32
|
+
|
|
33
|
+
### Ephemeral Identity (`useSessionIdentity`)
|
|
34
|
+
The sync protocol can optionally generate an ephemeral Keystone Identity for the duration of the saga.
|
|
35
|
+
* **Purpose**: Allows secure, signed exchanges even if the Node doesn't have a long-lived identity, or wishes to isolate the sync session.
|
|
36
|
+
* **Mechanism**: If `useSessionIdentity: true` (default) is passed to `sync()`, a new Keystone frame is generated and linked to the Saga.
|
|
37
|
+
* **Pending Work**: Full verification of these ephemeral signatures is currently pending implementation.
|
|
38
|
+
|
|
39
|
+
## 📚 Documentation
|
|
40
|
+
* **[Architecture](./docs/architecture.md)**: Detailed design of the Saga Protocol, State Machine, and Smart Diff logic.
|
|
41
|
+
* **[Verification](./docs/verification.md)**: Test matrix, scenarios, and verification results.
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
* **Symmetric Design**: No distinct "Client" or "Server" logic.
|
|
45
|
+
* **Smart Diff**: Efficient "Knowledge Vector" exchange to transfer only missing deltas.
|
|
46
|
+
* **Constants & Timelines**: Support for syncing both mutable histories and immutable stones.
|
|
47
|
+
* **Optimistic Concurrency**: (In Progress) Branch detection and merge strategies.
|
|
48
|
+
|
|
49
|
+
## Verification
|
|
50
|
+
We rely on rigorous In-Memory Simulation (`InnerSpace`) to verify complex graph scenarios. See **[Verification](./docs/verification.md)** for the full status.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Symmetric Sync Protocol Architecture
|
|
2
|
+
|
|
3
|
+
This document details the architecture of the `ibgib` Symmetric Sync Protocol. It describes the "Saga" model, the state machine, and the data structures used to ensure robust, audit-trail-based synchronization.
|
|
4
|
+
|
|
5
|
+
## 1. Core Principles
|
|
6
|
+
|
|
7
|
+
### 1.1 The Saga Model
|
|
8
|
+
Synchronization is not a stateless request/response. It is a **Saga**: a durable, linear timeline of immutable frames that represents the conversation between two peers.
|
|
9
|
+
* **Audit Trail**: The Saga Timeline (`SyncIbGib`) records every step (`Init`, `Ack`, `Delta`, `Commit`).
|
|
10
|
+
* **Symmetry**: Both peers (Alice and Bob) run the exact same `SyncSagaCoordinator`. There is no "Client" or "Server" code, only "Initiator" and "Reactor".
|
|
11
|
+
|
|
12
|
+
### 1.2 Smart Diff (Partial Updates)
|
|
13
|
+
To optimize bandwidth, the protocol exchanges "Knowledge Vectors" to calculate the precise difference between peers.
|
|
14
|
+
* **Knowledge Vector**: A summary of what a peer knows (Timelines, Tips, and Ancestors).
|
|
15
|
+
* **Space-Side Dependency Graph**: The `Space.getDependencyGraph` capability allows the "Network" layer to request *only* the new nodes in a graph, skipping everything the receiver already has.
|
|
16
|
+
|
|
17
|
+
## 2. Protocol Workflow (The State Machine)
|
|
18
|
+
|
|
19
|
+
The `SyncSagaCoordinator` drives a Finite State Machine (FSM) via the `handleSagaFrame` method.
|
|
20
|
+
|
|
21
|
+
### 2.1 The Ping-Pong Flow
|
|
22
|
+
|
|
23
|
+
1. **Start (Initiator)**:
|
|
24
|
+
* Initiator (Alice) calls `startSaga`.
|
|
25
|
+
* Generates `Init` frame containing her `KnowledgeVector` and `Mode` (Push/Pull/Sync).
|
|
26
|
+
* Sends `Init` to Bob.
|
|
27
|
+
|
|
28
|
+
2. **Gap Analysis (`handleInitFrame`)**:
|
|
29
|
+
* Reactor (Bob) receives `Init`.
|
|
30
|
+
* Bob compares Alice's `KnowledgeVector` against his own local space.
|
|
31
|
+
* **Identifies Gaps**:
|
|
32
|
+
* **Delta Request**: "Alice has `X`, I don't. Send me `X`."
|
|
33
|
+
* **Push Offer**: "I have `Y` (newer than Alice's). Do you want `Y`?"
|
|
34
|
+
* Bob sends `Ack` frame containing `deltaReqAddrs` and `pushOfferAddrs`.
|
|
35
|
+
|
|
36
|
+
3. **Payload Generation (`handleAckFrame`)**:
|
|
37
|
+
* Alice receives `Ack`.
|
|
38
|
+
* **Process Requests**: Iterates `deltaReqAddrs` (what Bob wants).
|
|
39
|
+
* **Smart Diff**: Uses Bob's `KnowledgeVector` (sent in Ack) to exclude all shared history.
|
|
40
|
+
* **Payload**: Generates a bundle of new ibGibs.
|
|
41
|
+
* Alice sends `Delta` frame containing the payload.
|
|
42
|
+
|
|
43
|
+
4. **Convergence (`handleDeltaFrame`)**:
|
|
44
|
+
* Bob receives `Delta`.
|
|
45
|
+
* **Verifies**: Checks hashes (`ib` vs `gib`) and signatures.
|
|
46
|
+
* **Merges**: Puts data into his local space.
|
|
47
|
+
* Bob sends `Commit` frame.
|
|
48
|
+
|
|
49
|
+
5. **Completion (`handleCommitFrame`)**:
|
|
50
|
+
* Alice receives `Commit`.
|
|
51
|
+
* Saga ends.
|
|
52
|
+
|
|
53
|
+
## 3. Data Structures
|
|
54
|
+
|
|
55
|
+
### 3.1 SyncSagaMessage
|
|
56
|
+
All frames are `SyncIbGib`s carrying specific data payloads.
|
|
57
|
+
|
|
58
|
+
* `sagaId`: Unique UUID for the session.
|
|
59
|
+
* `stage`: `init` | `ack` | `delta` | `commit` | `conflict`.
|
|
60
|
+
* `knowledgeVector`: `{ [tjpAddr]: [tipAddr, ...ancestors] }`.
|
|
61
|
+
|
|
62
|
+
### 3.2 Constants vs Timelines
|
|
63
|
+
The protocol supports two types of data:
|
|
64
|
+
* **Timelines**: Mutable data with a "Temporal Junction Point" (TJP). Syncing involves finding the "Latest" tip.
|
|
65
|
+
* **Stones (Constants)**: Immutable data without a history (e.g., Code lists, Configs). Syncing involves simple existence checks.
|
|
66
|
+
|
|
67
|
+
## 4. Security & Identity
|
|
68
|
+
* **Session Keystone**: Each Saga typically generates an ephemeral identity to sign frames, linked to a master identity.
|
|
69
|
+
* **Trust Chain**: Each frame points to the previous one (`past` rel8n), creating a hash-linked chain of custody.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Sync Protocol Verification
|
|
2
|
+
|
|
3
|
+
This document outlines the testing strategy and verification results for the Symmetric Sync Protocol.
|
|
4
|
+
|
|
5
|
+
## Testing Strategy
|
|
6
|
+
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.
|
|
7
|
+
|
|
8
|
+
### Key Test Files
|
|
9
|
+
* `sync-innerspace.respec.mts`: Basic Push/Pull scenarios.
|
|
10
|
+
* `sync-innerspace-multiple-timelines.respec.mts`: Handling multiple independent timelines in one Saga.
|
|
11
|
+
* `sync-innerspace-deep-updates.respec.mts`: Syncing timelines with extensive history (ensuring `past` links are traversed).
|
|
12
|
+
* `sync-innerspace-partial-update.respec.mts`: **Smart Diff** verification (Sender uses Receiver's context to send only deltas).
|
|
13
|
+
* `sync-innerspace-dest-ahead.respec.mts`: Verifying "Fast-Backward" or "Push Offer" logic (Sender offers update, Receiver accepts).
|
|
14
|
+
* `sync-innerspace-constants.respec.mts`: Verifying synchronization of **Stones** (Constants/Non-TJPs).
|
|
15
|
+
|
|
16
|
+
## Verification Matrix
|
|
17
|
+
|
|
18
|
+
### 1. Basic Scenarios
|
|
19
|
+
| Feature | Description | Status | Test File |
|
|
20
|
+
| :--- | :--- | :--- | :--- |
|
|
21
|
+
| **Push** | Source syncs a new timeline to an empty Dest. | ✅ Verified | `sync-innerspace.respec.mts` |
|
|
22
|
+
| **Pull** | Dest requests data from Source (Reverse Sync). | ⏳ Pending | `sync-innerspace.respec.mts` |
|
|
23
|
+
| **Dest Ahead** | Dest has newer data; Source offers Push. | ✅ Verified | `sync-innerspace-dest-ahead.respec.mts` |
|
|
24
|
+
|
|
25
|
+
### 2. Complex Graph Scenarios
|
|
26
|
+
| Feature | Description | Status | Test File |
|
|
27
|
+
| :--- | :--- | :--- | :--- |
|
|
28
|
+
| **Multi-Timeline** | Syncing multiple separate timelines. | ✅ Verified | `sync-innerspace-multiple-timelines.respec.mts` |
|
|
29
|
+
| **Deep Updates** | Syncing deep dependency chains (Ancestors). | ✅ Verified | `sync-innerspace-deep-updates.respec.mts` |
|
|
30
|
+
| **Smart Diff** | Sender skips data Receiver already has. | ✅ Verified | `sync-innerspace-partial-update.respec.mts` |
|
|
31
|
+
| **Constants** | Syncing immutable stones (no TJP). | ✅ Verified | `sync-innerspace-constants.respec.mts` |
|
|
32
|
+
|
|
33
|
+
### 3. Failure & Edge Cases
|
|
34
|
+
| Feature | Description | Status | Test File |
|
|
35
|
+
| :--- | :--- | :--- | :--- |
|
|
36
|
+
| **Validation** | Verifying Integrity Checks (ib/gib). | ⏳ Pending | - |
|
|
37
|
+
| **Conflicts** | Divergent branches (Merge Strategy). | ⏳ Pending | - |
|
|
38
|
+
|
|
39
|
+
## Recent Verifications
|
|
40
|
+
### Sync Constants (2026-01-08)
|
|
41
|
+
* **Goal**: Ensure constants (ibGibs without `tjp` or `past`) are synced.
|
|
42
|
+
* **Result**: Passed.
|
|
43
|
+
* **Fixes**: Updated `handleInitFrame` to request missing stones; updated `handleAckFrame` to include "tip" stones in payload even without dependencies.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MERGE_INFO_ATOM = 'merge_info';
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Factory_V1 } from "@ibgib/ts-gib/dist/V1/factory.mjs";
|
|
2
|
+
import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
|
|
3
|
+
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
4
|
+
import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
5
|
+
|
|
6
|
+
import { MetaspaceService } from "../../witness/space/metaspace/metaspace-types.mjs";
|
|
7
|
+
import { MERGE_INFO_ATOM } from "./merge-info-constants.mjs";
|
|
8
|
+
import { MergeInfoData_V1, MergeInfoIbGib_V1, MergeInfoIb_V1 } from "./merge-info-types.mjs";
|
|
9
|
+
import { IbGibSpaceAny } from "../../witness/space/space-base-v1.mjs";
|
|
10
|
+
|
|
11
|
+
const lc = `[merge-info-helpers]`;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Validates and constructs the `ib` object for a Merge Info ibGib.
|
|
15
|
+
*/
|
|
16
|
+
export async function getMergeInfoIb({
|
|
17
|
+
data
|
|
18
|
+
}: {
|
|
19
|
+
data: MergeInfoData_V1
|
|
20
|
+
}): Promise<string> {
|
|
21
|
+
if (!data.algo) { throw new Error(`(UNEXPECTED) data.algo required for MergeInfoIb (E: 8a9b0c1d2e3f4g5h)`); }
|
|
22
|
+
|
|
23
|
+
// merge_info algo
|
|
24
|
+
return [MERGE_INFO_ATOM, data.algo].join(' ');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parses a standard Merge Info 'ib' string.
|
|
29
|
+
*/
|
|
30
|
+
export async function parseMergeInfoIb({
|
|
31
|
+
ib
|
|
32
|
+
}: {
|
|
33
|
+
ib: string
|
|
34
|
+
}): Promise<MergeInfoIb_V1> {
|
|
35
|
+
try {
|
|
36
|
+
const parts = ib.split(' ');
|
|
37
|
+
if (parts[0] !== MERGE_INFO_ATOM) { throw new Error(`Atom mismatch. Expected ${MERGE_INFO_ATOM} (E: 8f03c92a95144b618821915632599266)`); }
|
|
38
|
+
if (parts.length < 2) { throw new Error(`Invalid merge info ib. Expected at least 2 parts. (E: 9c2b4c8a6d34469f8263544710183355)`); }
|
|
39
|
+
|
|
40
|
+
const algo = parts.slice(1).join(' '); // allow spaces in algo? usually single word but safe to join.
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
atom: MERGE_INFO_ATOM,
|
|
44
|
+
algo,
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a new MergeInfo ibGib.
|
|
54
|
+
*/
|
|
55
|
+
export async function createMergeInfo({
|
|
56
|
+
algo,
|
|
57
|
+
ancestors,
|
|
58
|
+
details,
|
|
59
|
+
}: {
|
|
60
|
+
algo: string,
|
|
61
|
+
ancestors: IbGib_V1[],
|
|
62
|
+
details?: string,
|
|
63
|
+
}): Promise<MergeInfoIbGib_V1> {
|
|
64
|
+
const data: MergeInfoData_V1 = {
|
|
65
|
+
algo,
|
|
66
|
+
details,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const ib = await getMergeInfoIb({ data });
|
|
70
|
+
|
|
71
|
+
const rel8ns: any = {};
|
|
72
|
+
if (ancestors && ancestors.length > 0) {
|
|
73
|
+
rel8ns.ancestors = ancestors.map(a => getIbGibAddr({ ibGib: a }));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const res = await Factory_V1.stone({
|
|
77
|
+
ib,
|
|
78
|
+
data,
|
|
79
|
+
rel8ns,
|
|
80
|
+
parentPrimitiveIb: MERGE_INFO_ATOM,
|
|
81
|
+
uuid: true, // Should be unique per merge event? Yes.
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Unwrapping if necessary, though recent checking suggests Factory returns ibGib directly.
|
|
85
|
+
// Wait, recent check in SyncSagaCoordinator showed Factory_V1.stone returns the ibGib directly.
|
|
86
|
+
// But check the implementation plan/previous learnings.
|
|
87
|
+
// Confirmed: Factory_V1.stone returns the ibGib directly.
|
|
88
|
+
return res as MergeInfoIbGib_V1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Naive text merge.
|
|
93
|
+
* Strategies:
|
|
94
|
+
* 1. Concat (A + B)
|
|
95
|
+
* 2. structured diff (not impl)
|
|
96
|
+
*/
|
|
97
|
+
export async function mergeText({
|
|
98
|
+
textA,
|
|
99
|
+
textB,
|
|
100
|
+
}: {
|
|
101
|
+
textA: string,
|
|
102
|
+
textB: string,
|
|
103
|
+
}): Promise<string> {
|
|
104
|
+
// Very naive: Just concat with a marker for now.
|
|
105
|
+
// User wants "best effort at being non-destructive".
|
|
106
|
+
if (textA === textB) { return textA; }
|
|
107
|
+
return `${textA}\n\n>>> MERGE SEPARATOR <<<\n\n${textB}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Combines two divergent timelines by applying missing DNA from one branch to another.
|
|
112
|
+
*
|
|
113
|
+
* Logic:
|
|
114
|
+
* 1. Identify missing DNA (transforms) in `targetTip` that exist in `sourceTip` (relative to LCA).
|
|
115
|
+
* 2. Replay those transforms onto `targetTip`.
|
|
116
|
+
* 3. Create a MergeInfo stone describing this operation.
|
|
117
|
+
* 4. Relate the new tip to the MergeInfo.
|
|
118
|
+
*/
|
|
119
|
+
export async function combineDivergentTimelines({
|
|
120
|
+
targetTip,
|
|
121
|
+
sourceTip,
|
|
122
|
+
lca,
|
|
123
|
+
space,
|
|
124
|
+
metaspace,
|
|
125
|
+
}: {
|
|
126
|
+
targetTip: IbGib_V1,
|
|
127
|
+
sourceTip: IbGib_V1,
|
|
128
|
+
lca: IbGib_V1,
|
|
129
|
+
space: IbGibSpaceAny,
|
|
130
|
+
metaspace: MetaspaceService,
|
|
131
|
+
}): Promise<IbGib_V1> {
|
|
132
|
+
// stub implementation
|
|
133
|
+
return targetTip;
|
|
134
|
+
}
|