@private.me/xcontinuity 2.1.0
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/AGENTS.md +123 -0
- package/LICENSE.md +26 -0
- package/MIGRATING.md +77 -0
- package/README.md +601 -0
- package/dist/adjudicator.d.ts +75 -0
- package/dist/adjudicator.js +184 -0
- package/dist/cascade.d.ts +157 -0
- package/dist/cascade.js +323 -0
- package/dist/chronicle.d.ts +76 -0
- package/dist/chronicle.js +173 -0
- package/dist/cjs/adjudicator.js +189 -0
- package/dist/cjs/cascade.js +328 -0
- package/dist/cjs/chronicle.js +178 -0
- package/dist/cjs/enforcement.js +108 -0
- package/dist/cjs/errors.js +72 -0
- package/dist/cjs/index.js +108 -0
- package/dist/cjs/memory-runtime.js +129 -0
- package/dist/cjs/memory-session.js +134 -0
- package/dist/cjs/mission.js +178 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/provenance.js +192 -0
- package/dist/cjs/ratification.js +322 -0
- package/dist/cjs/reverse-xorida.js +506 -0
- package/dist/cjs/session.js +273 -0
- package/dist/cjs/state-serializer.js +300 -0
- package/dist/cjs/store-memory.js +33 -0
- package/dist/cjs/trust.js +133 -0
- package/dist/cjs/types.js +59 -0
- package/dist/enforcement.d.ts +40 -0
- package/dist/enforcement.js +104 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.js +68 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +43 -0
- package/dist/memory-runtime.d.ts +36 -0
- package/dist/memory-runtime.js +125 -0
- package/dist/memory-session.d.ts +38 -0
- package/dist/memory-session.js +97 -0
- package/dist/mission.d.ts +68 -0
- package/dist/mission.js +172 -0
- package/dist/provenance.d.ts +54 -0
- package/dist/provenance.js +182 -0
- package/dist/ratification.d.ts +113 -0
- package/dist/ratification.js +317 -0
- package/dist/reverse-xorida.d.ts +174 -0
- package/dist/reverse-xorida.js +490 -0
- package/dist/session.d.ts +102 -0
- package/dist/session.js +269 -0
- package/dist/state-serializer.d.ts +37 -0
- package/dist/state-serializer.js +294 -0
- package/dist/store-memory.d.ts +18 -0
- package/dist/store-memory.js +29 -0
- package/dist/trust.d.ts +76 -0
- package/dist/trust.js +121 -0
- package/dist/types.d.ts +320 -0
- package/dist/types.js +56 -0
- package/llms.txt +43 -0
- package/package.json +125 -0
- package/share1.dat +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @private.me/xcontinuity — Ratification (TrustStore)
|
|
3
|
+
*
|
|
4
|
+
* Central store for trust-annotated memory entries.
|
|
5
|
+
* Integrates provenance (signing), trust tiers, chronicle (history),
|
|
6
|
+
* adjudicator (conflict resolution), and event hooks.
|
|
7
|
+
*
|
|
8
|
+
* Key flows:
|
|
9
|
+
* write() — store an entry with automatic trust tier assignment
|
|
10
|
+
* ratify() — sign an entry, promoting it to 'ratified' tier
|
|
11
|
+
* restore() — rebuild trust state from a chronicle
|
|
12
|
+
* hypothesisMode() — sandbox for speculative writes
|
|
13
|
+
*/
|
|
14
|
+
import type { Result } from '@private.me/shared';
|
|
15
|
+
import type { MemoryEntry, ProvenanceRecord, StateValue, TrustTier, TrustStoreEventType, TrustStoreEvent, TrustStoreListener } from './types.js';
|
|
16
|
+
import type { ContinuityError } from './errors.js';
|
|
17
|
+
import { Chronicle } from './chronicle.js';
|
|
18
|
+
import type { Adjudicator } from './adjudicator.js';
|
|
19
|
+
/** Configuration for creating a TrustStore. */
|
|
20
|
+
export interface TrustStoreConfig {
|
|
21
|
+
/** Default adjudicator for conflict resolution. */
|
|
22
|
+
readonly adjudicator?: Adjudicator;
|
|
23
|
+
/** Default maxAge (TTL) for new entries (ms). Undefined = no decay. */
|
|
24
|
+
readonly defaultMaxAge?: number;
|
|
25
|
+
}
|
|
26
|
+
export declare class TrustStore {
|
|
27
|
+
private readonly entries;
|
|
28
|
+
private readonly chronicle;
|
|
29
|
+
private readonly adjudicator;
|
|
30
|
+
private readonly defaultMaxAge?;
|
|
31
|
+
private readonly listeners;
|
|
32
|
+
private sequenceCounter;
|
|
33
|
+
constructor(config?: TrustStoreConfig);
|
|
34
|
+
/**
|
|
35
|
+
* Write an entry to the trust store.
|
|
36
|
+
*
|
|
37
|
+
* Assigns trust tier based on provenance, checks for contradictions
|
|
38
|
+
* against the chronicle, and emits events.
|
|
39
|
+
*/
|
|
40
|
+
write(key: string, value: StateValue, provenance?: ProvenanceRecord, signatureVerified?: boolean): Result<MemoryEntry, ContinuityError>;
|
|
41
|
+
/**
|
|
42
|
+
* Ratify an entry by signing it with Ed25519, promoting to 'ratified' tier.
|
|
43
|
+
*/
|
|
44
|
+
ratify(key: string, privateKey: CryptoKey, publicKey: CryptoKey): Promise<Result<MemoryEntry, ContinuityError>>;
|
|
45
|
+
/**
|
|
46
|
+
* Read an entry, applying effective tier (decay + contradictions).
|
|
47
|
+
*/
|
|
48
|
+
read(key: string): MemoryEntry | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Read an entry only if it meets a minimum trust tier.
|
|
51
|
+
*/
|
|
52
|
+
readAtTier(key: string, minTier: TrustTier): MemoryEntry | undefined;
|
|
53
|
+
/** Check if a key exists in the store. */
|
|
54
|
+
has(key: string): boolean;
|
|
55
|
+
/** Delete an entry. */
|
|
56
|
+
delete(key: string): boolean;
|
|
57
|
+
/** Get all keys. */
|
|
58
|
+
keys(): string[];
|
|
59
|
+
/** Get all entries. */
|
|
60
|
+
all(): readonly MemoryEntry[];
|
|
61
|
+
/** Get the chronicle (read-only access). */
|
|
62
|
+
getChronicle(): Chronicle;
|
|
63
|
+
/** Get the entry count. */
|
|
64
|
+
get size(): number;
|
|
65
|
+
/**
|
|
66
|
+
* Restore trust state from an array of memory entries.
|
|
67
|
+
* Used to rebuild state after loading from persistence.
|
|
68
|
+
*/
|
|
69
|
+
restore(entries: readonly MemoryEntry[]): void;
|
|
70
|
+
/**
|
|
71
|
+
* Create a hypothesis (sandbox) that can be committed or discarded.
|
|
72
|
+
*
|
|
73
|
+
* All writes in the hypothesis are isolated from the main store.
|
|
74
|
+
* Call commit() to merge into the main store, or discard() to abandon.
|
|
75
|
+
*/
|
|
76
|
+
hypothesisMode(): Hypothesis;
|
|
77
|
+
/** Subscribe to trust store events. */
|
|
78
|
+
on<T extends TrustStoreEvent>(event: TrustStoreEventType, listener: TrustStoreListener<T>): void;
|
|
79
|
+
/** Unsubscribe from trust store events. */
|
|
80
|
+
off<T extends TrustStoreEvent>(event: TrustStoreEventType, listener: TrustStoreListener<T>): void;
|
|
81
|
+
/** Remove all event listeners. */
|
|
82
|
+
removeAllListeners(): void;
|
|
83
|
+
/** Emit an event to all registered listeners. */
|
|
84
|
+
private emit;
|
|
85
|
+
/** @internal Merge entries from a hypothesis into the main store. */
|
|
86
|
+
_mergeHypothesis(entries: Map<string, MemoryEntry>): void;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A sandboxed hypothesis for speculative writes.
|
|
90
|
+
*
|
|
91
|
+
* Changes are isolated until commit() is called.
|
|
92
|
+
* Memory is bounded — the hypothesis shares the parent's maxAge config.
|
|
93
|
+
*/
|
|
94
|
+
export declare class Hypothesis {
|
|
95
|
+
private readonly parent;
|
|
96
|
+
private readonly baseSnapshot;
|
|
97
|
+
private readonly changes;
|
|
98
|
+
private committed;
|
|
99
|
+
private discarded;
|
|
100
|
+
constructor(parent: TrustStore, baseSnapshot: Map<string, MemoryEntry>);
|
|
101
|
+
/** Write a speculative entry (isolated from main store). */
|
|
102
|
+
write(key: string, value: StateValue, tier?: TrustTier): MemoryEntry;
|
|
103
|
+
/** Read from hypothesis (falls back to base snapshot). */
|
|
104
|
+
read(key: string): MemoryEntry | undefined;
|
|
105
|
+
/** Commit all hypothesis changes into the main store. */
|
|
106
|
+
commit(): void;
|
|
107
|
+
/** Discard all hypothesis changes. */
|
|
108
|
+
discard(): void;
|
|
109
|
+
/** Check if the hypothesis has been finalized. */
|
|
110
|
+
get isFinalized(): boolean;
|
|
111
|
+
/** Get the number of speculative changes. */
|
|
112
|
+
get changeCount(): number;
|
|
113
|
+
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @private.me/xcontinuity — Ratification (TrustStore)
|
|
3
|
+
*
|
|
4
|
+
* Central store for trust-annotated memory entries.
|
|
5
|
+
* Integrates provenance (signing), trust tiers, chronicle (history),
|
|
6
|
+
* adjudicator (conflict resolution), and event hooks.
|
|
7
|
+
*
|
|
8
|
+
* Key flows:
|
|
9
|
+
* write() — store an entry with automatic trust tier assignment
|
|
10
|
+
* ratify() — sign an entry, promoting it to 'ratified' tier
|
|
11
|
+
* restore() — rebuild trust state from a chronicle
|
|
12
|
+
* hypothesisMode() — sandbox for speculative writes
|
|
13
|
+
*/
|
|
14
|
+
import { ok, err } from '@private.me/shared';
|
|
15
|
+
import { continuityError } from './errors.js';
|
|
16
|
+
import { baselineTier, effectiveTier } from './trust.js';
|
|
17
|
+
import { signEntry } from './provenance.js';
|
|
18
|
+
import { Chronicle, detectContradiction } from './chronicle.js';
|
|
19
|
+
import { PolicyAdjudicator } from './adjudicator.js';
|
|
20
|
+
export class TrustStore {
|
|
21
|
+
entries = new Map();
|
|
22
|
+
chronicle = new Chronicle();
|
|
23
|
+
adjudicator;
|
|
24
|
+
defaultMaxAge;
|
|
25
|
+
listeners = new Map();
|
|
26
|
+
sequenceCounter = 0;
|
|
27
|
+
constructor(config = {}) {
|
|
28
|
+
this.adjudicator = config.adjudicator ?? new PolicyAdjudicator();
|
|
29
|
+
this.defaultMaxAge = config.defaultMaxAge;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Write an entry to the trust store.
|
|
33
|
+
*
|
|
34
|
+
* Assigns trust tier based on provenance, checks for contradictions
|
|
35
|
+
* against the chronicle, and emits events.
|
|
36
|
+
*/
|
|
37
|
+
write(key, value, provenance, signatureVerified = false) {
|
|
38
|
+
const tier = baselineTier(provenance, signatureVerified);
|
|
39
|
+
const oldEntry = this.entries.get(key);
|
|
40
|
+
// Check for contradictions
|
|
41
|
+
const contradictions = [];
|
|
42
|
+
if (oldEntry !== undefined) {
|
|
43
|
+
if (detectContradiction(value, oldEntry.value)) {
|
|
44
|
+
contradictions.push(oldEntry.key);
|
|
45
|
+
this.emit('contradiction', {
|
|
46
|
+
key,
|
|
47
|
+
existing: oldEntry,
|
|
48
|
+
incoming: { key, value, provenance, tier, contradictions, maxAge: this.defaultMaxAge },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const entry = {
|
|
53
|
+
key,
|
|
54
|
+
value,
|
|
55
|
+
provenance,
|
|
56
|
+
tier,
|
|
57
|
+
contradictions,
|
|
58
|
+
maxAge: this.defaultMaxAge,
|
|
59
|
+
ratifiedAt: tier === 'ratified' ? Date.now() : undefined,
|
|
60
|
+
};
|
|
61
|
+
this.entries.set(key, entry);
|
|
62
|
+
// Record in chronicle
|
|
63
|
+
const stateId = `entry-${this.sequenceCounter}`;
|
|
64
|
+
this.chronicle.appendWithContradictionCheck({
|
|
65
|
+
stateId,
|
|
66
|
+
sessionId: 'trust-store',
|
|
67
|
+
sequence: this.sequenceCounter++,
|
|
68
|
+
timestamp: Date.now(),
|
|
69
|
+
tags: [tier],
|
|
70
|
+
parentStateId: oldEntry ? `entry-${this.sequenceCounter - 2}` : undefined,
|
|
71
|
+
}, key, value, oldEntry?.value);
|
|
72
|
+
// Emit change event
|
|
73
|
+
this.emit('change', { key, oldEntry, newEntry: entry });
|
|
74
|
+
// Emit tier change if applicable
|
|
75
|
+
if (oldEntry && oldEntry.tier !== tier) {
|
|
76
|
+
this.emit('tierChange', {
|
|
77
|
+
key,
|
|
78
|
+
oldTier: oldEntry.tier,
|
|
79
|
+
newTier: tier,
|
|
80
|
+
reason: provenance ? 'provenance changed' : 'new write',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return ok(entry);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Ratify an entry by signing it with Ed25519, promoting to 'ratified' tier.
|
|
87
|
+
*/
|
|
88
|
+
async ratify(key, privateKey, publicKey) {
|
|
89
|
+
const existing = this.entries.get(key);
|
|
90
|
+
if (!existing) {
|
|
91
|
+
return err(continuityError('ENTRY_NOT_FOUND', `No entry for key "${key}"`));
|
|
92
|
+
}
|
|
93
|
+
// Sign the entry
|
|
94
|
+
const signResult = await signEntry(key, existing.value, privateKey, publicKey, existing.provenance?.parentHash);
|
|
95
|
+
if (!signResult.ok)
|
|
96
|
+
return signResult;
|
|
97
|
+
const oldTier = existing.tier;
|
|
98
|
+
const ratified = {
|
|
99
|
+
...existing,
|
|
100
|
+
provenance: signResult.value,
|
|
101
|
+
tier: 'ratified',
|
|
102
|
+
ratifiedAt: Date.now(),
|
|
103
|
+
};
|
|
104
|
+
this.entries.set(key, ratified);
|
|
105
|
+
if (oldTier !== 'ratified') {
|
|
106
|
+
this.emit('tierChange', {
|
|
107
|
+
key,
|
|
108
|
+
oldTier,
|
|
109
|
+
newTier: 'ratified',
|
|
110
|
+
reason: 'ratified by signing',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
this.emit('change', { key, oldEntry: existing, newEntry: ratified });
|
|
114
|
+
return ok(ratified);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Read an entry, applying effective tier (decay + contradictions).
|
|
118
|
+
*/
|
|
119
|
+
read(key) {
|
|
120
|
+
const entry = this.entries.get(key);
|
|
121
|
+
if (!entry)
|
|
122
|
+
return undefined;
|
|
123
|
+
const effective = effectiveTier(entry);
|
|
124
|
+
if (effective !== entry.tier) {
|
|
125
|
+
// Update stored tier to reflect decay/contradictions
|
|
126
|
+
const updated = { ...entry, tier: effective };
|
|
127
|
+
this.entries.set(key, updated);
|
|
128
|
+
this.emit('tierChange', {
|
|
129
|
+
key,
|
|
130
|
+
oldTier: entry.tier,
|
|
131
|
+
newTier: effective,
|
|
132
|
+
reason: effective === 'inherited' ? 'TTL decay' : 'contradiction downgrade',
|
|
133
|
+
});
|
|
134
|
+
return updated;
|
|
135
|
+
}
|
|
136
|
+
return entry;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Read an entry only if it meets a minimum trust tier.
|
|
140
|
+
*/
|
|
141
|
+
readAtTier(key, minTier) {
|
|
142
|
+
const entry = this.read(key);
|
|
143
|
+
if (!entry)
|
|
144
|
+
return undefined;
|
|
145
|
+
const tierRank = { ratified: 3, inherited: 2, quarantined: 1 };
|
|
146
|
+
if (tierRank[entry.tier] < tierRank[minTier])
|
|
147
|
+
return undefined;
|
|
148
|
+
return entry;
|
|
149
|
+
}
|
|
150
|
+
/** Check if a key exists in the store. */
|
|
151
|
+
has(key) {
|
|
152
|
+
return this.entries.has(key);
|
|
153
|
+
}
|
|
154
|
+
/** Delete an entry. */
|
|
155
|
+
delete(key) {
|
|
156
|
+
const existing = this.entries.get(key);
|
|
157
|
+
if (!existing)
|
|
158
|
+
return false;
|
|
159
|
+
this.entries.delete(key);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
/** Get all keys. */
|
|
163
|
+
keys() {
|
|
164
|
+
return Array.from(this.entries.keys());
|
|
165
|
+
}
|
|
166
|
+
/** Get all entries. */
|
|
167
|
+
all() {
|
|
168
|
+
return Array.from(this.entries.values());
|
|
169
|
+
}
|
|
170
|
+
/** Get the chronicle (read-only access). */
|
|
171
|
+
getChronicle() {
|
|
172
|
+
return this.chronicle;
|
|
173
|
+
}
|
|
174
|
+
/** Get the entry count. */
|
|
175
|
+
get size() {
|
|
176
|
+
return this.entries.size;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Restore trust state from an array of memory entries.
|
|
180
|
+
* Used to rebuild state after loading from persistence.
|
|
181
|
+
*/
|
|
182
|
+
restore(entries) {
|
|
183
|
+
this.entries.clear();
|
|
184
|
+
this.chronicle.clear();
|
|
185
|
+
this.sequenceCounter = 0;
|
|
186
|
+
for (const entry of entries) {
|
|
187
|
+
this.entries.set(entry.key, entry);
|
|
188
|
+
this.chronicle.append({
|
|
189
|
+
stateId: `entry-${this.sequenceCounter}`,
|
|
190
|
+
sessionId: 'trust-store',
|
|
191
|
+
sequence: this.sequenceCounter++,
|
|
192
|
+
timestamp: entry.provenance?.timestamp ?? Date.now(),
|
|
193
|
+
tags: [entry.tier],
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Create a hypothesis (sandbox) that can be committed or discarded.
|
|
199
|
+
*
|
|
200
|
+
* All writes in the hypothesis are isolated from the main store.
|
|
201
|
+
* Call commit() to merge into the main store, or discard() to abandon.
|
|
202
|
+
*/
|
|
203
|
+
hypothesisMode() {
|
|
204
|
+
// Snapshot current entries
|
|
205
|
+
const snapshot = new Map();
|
|
206
|
+
for (const [key, entry] of this.entries) {
|
|
207
|
+
snapshot.set(key, entry);
|
|
208
|
+
}
|
|
209
|
+
return new Hypothesis(this, snapshot);
|
|
210
|
+
}
|
|
211
|
+
/* ── Event System ── */
|
|
212
|
+
/** Subscribe to trust store events. */
|
|
213
|
+
on(event, listener) {
|
|
214
|
+
let set = this.listeners.get(event);
|
|
215
|
+
if (!set) {
|
|
216
|
+
set = new Set();
|
|
217
|
+
this.listeners.set(event, set);
|
|
218
|
+
}
|
|
219
|
+
set.add(listener);
|
|
220
|
+
}
|
|
221
|
+
/** Unsubscribe from trust store events. */
|
|
222
|
+
off(event, listener) {
|
|
223
|
+
const set = this.listeners.get(event);
|
|
224
|
+
if (set) {
|
|
225
|
+
set.delete(listener);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/** Remove all event listeners. */
|
|
229
|
+
removeAllListeners() {
|
|
230
|
+
this.listeners.clear();
|
|
231
|
+
}
|
|
232
|
+
/** Emit an event to all registered listeners. */
|
|
233
|
+
emit(event, payload) {
|
|
234
|
+
const set = this.listeners.get(event);
|
|
235
|
+
if (set) {
|
|
236
|
+
for (const listener of set) {
|
|
237
|
+
listener(payload);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/* ── Internal: used by Hypothesis ── */
|
|
242
|
+
/** @internal Merge entries from a hypothesis into the main store. */
|
|
243
|
+
_mergeHypothesis(entries) {
|
|
244
|
+
for (const [key, entry] of entries) {
|
|
245
|
+
const existing = this.entries.get(key);
|
|
246
|
+
this.entries.set(key, entry);
|
|
247
|
+
this.emit('change', { key, oldEntry: existing, newEntry: entry });
|
|
248
|
+
if (existing && existing.tier !== entry.tier) {
|
|
249
|
+
this.emit('tierChange', {
|
|
250
|
+
key,
|
|
251
|
+
oldTier: existing.tier,
|
|
252
|
+
newTier: entry.tier,
|
|
253
|
+
reason: 'hypothesis committed',
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* A sandboxed hypothesis for speculative writes.
|
|
261
|
+
*
|
|
262
|
+
* Changes are isolated until commit() is called.
|
|
263
|
+
* Memory is bounded — the hypothesis shares the parent's maxAge config.
|
|
264
|
+
*/
|
|
265
|
+
export class Hypothesis {
|
|
266
|
+
parent;
|
|
267
|
+
baseSnapshot;
|
|
268
|
+
changes = new Map();
|
|
269
|
+
committed = false;
|
|
270
|
+
discarded = false;
|
|
271
|
+
constructor(parent, baseSnapshot) {
|
|
272
|
+
this.parent = parent;
|
|
273
|
+
this.baseSnapshot = baseSnapshot;
|
|
274
|
+
}
|
|
275
|
+
/** Write a speculative entry (isolated from main store). */
|
|
276
|
+
write(key, value, tier = 'inherited') {
|
|
277
|
+
if (this.committed || this.discarded) {
|
|
278
|
+
throw new Error('Hypothesis already finalized');
|
|
279
|
+
}
|
|
280
|
+
const entry = {
|
|
281
|
+
key,
|
|
282
|
+
value,
|
|
283
|
+
tier,
|
|
284
|
+
contradictions: [],
|
|
285
|
+
};
|
|
286
|
+
this.changes.set(key, entry);
|
|
287
|
+
return entry;
|
|
288
|
+
}
|
|
289
|
+
/** Read from hypothesis (falls back to base snapshot). */
|
|
290
|
+
read(key) {
|
|
291
|
+
return this.changes.get(key) ?? this.baseSnapshot.get(key);
|
|
292
|
+
}
|
|
293
|
+
/** Commit all hypothesis changes into the main store. */
|
|
294
|
+
commit() {
|
|
295
|
+
if (this.committed || this.discarded) {
|
|
296
|
+
throw new Error('Hypothesis already finalized');
|
|
297
|
+
}
|
|
298
|
+
this.committed = true;
|
|
299
|
+
this.parent._mergeHypothesis(this.changes);
|
|
300
|
+
}
|
|
301
|
+
/** Discard all hypothesis changes. */
|
|
302
|
+
discard() {
|
|
303
|
+
if (this.committed || this.discarded) {
|
|
304
|
+
throw new Error('Hypothesis already finalized');
|
|
305
|
+
}
|
|
306
|
+
this.discarded = true;
|
|
307
|
+
this.changes.clear();
|
|
308
|
+
}
|
|
309
|
+
/** Check if the hypothesis has been finalized. */
|
|
310
|
+
get isFinalized() {
|
|
311
|
+
return this.committed || this.discarded;
|
|
312
|
+
}
|
|
313
|
+
/** Get the number of speculative changes. */
|
|
314
|
+
get changeCount() {
|
|
315
|
+
return this.changes.size;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @private.me/xcontinuity — Reverse-XorIDA Core Crypto Layer
|
|
3
|
+
*
|
|
4
|
+
* The novel primitive: exploits XorIDA's linearity over GF(2) to perform
|
|
5
|
+
* incremental state updates at O(delta) cost instead of re-splitting entire state.
|
|
6
|
+
*
|
|
7
|
+
* Key insight: split(A XOR B) = split(A) XOR split(B)
|
|
8
|
+
*
|
|
9
|
+
* Therefore: newShares = oldShares XOR split(oldPadded XOR newPadded)
|
|
10
|
+
*
|
|
11
|
+
* HMAC must be recomputed fresh for the new state (not derived from delta).
|
|
12
|
+
*/
|
|
13
|
+
import type { Result } from '@private.me/shared';
|
|
14
|
+
import type { StateSnapshot, StateShare, SplitState, SplitConfig, StateDelta } from './types.js';
|
|
15
|
+
import type { ContinuityError } from './errors.js';
|
|
16
|
+
/**
|
|
17
|
+
* Pad data for XorIDA splitting.
|
|
18
|
+
* Block size = nextOddPrime(n) - 1.
|
|
19
|
+
*/
|
|
20
|
+
export declare function padForSplit(data: Uint8Array, n: number): Uint8Array;
|
|
21
|
+
/**
|
|
22
|
+
* Split a state snapshot into XorIDA threshold shares.
|
|
23
|
+
*
|
|
24
|
+
* Pipeline: pad -> HMAC -> split -> package as StateShare[]
|
|
25
|
+
*
|
|
26
|
+
* @param snapshot - Serialized state with checksum
|
|
27
|
+
* @param config - Split configuration (n, k)
|
|
28
|
+
* @returns SplitState containing all shares
|
|
29
|
+
*/
|
|
30
|
+
export declare function splitState(snapshot: StateSnapshot, config?: SplitConfig): Promise<Result<SplitState, ContinuityError>>;
|
|
31
|
+
/**
|
|
32
|
+
* Reconstruct a state snapshot from threshold shares.
|
|
33
|
+
*
|
|
34
|
+
* Pipeline: validate -> reconstruct -> verify HMAC -> unpad -> return snapshot bytes
|
|
35
|
+
*
|
|
36
|
+
* @param shares - Array of k or more StateShares (uses first k)
|
|
37
|
+
* @returns Reconstructed StateSnapshot serialized bytes and checksum
|
|
38
|
+
*/
|
|
39
|
+
export declare function reconstructState(shares: readonly StateShare[]): Promise<Result<{
|
|
40
|
+
serializedBytes: Uint8Array;
|
|
41
|
+
stateId: string;
|
|
42
|
+
}, ContinuityError>>;
|
|
43
|
+
/**
|
|
44
|
+
* Compute the byte-level delta between two padded state arrays.
|
|
45
|
+
* Both arrays must be the same length.
|
|
46
|
+
*
|
|
47
|
+
* delta[i] = oldPadded[i] XOR newPadded[i]
|
|
48
|
+
*/
|
|
49
|
+
export declare function computeDelta(oldPadded: Uint8Array, newPadded: Uint8Array, fromStateId: string, toStateId: string, fromChecksum: Uint8Array, toChecksum: Uint8Array): Result<StateDelta, ContinuityError>;
|
|
50
|
+
/**
|
|
51
|
+
* Apply delta shares to old split state via XOR.
|
|
52
|
+
*
|
|
53
|
+
* newShares[i].data = oldShares[i].data XOR deltaShares[i].data
|
|
54
|
+
*
|
|
55
|
+
* @param oldShares - Original shares
|
|
56
|
+
* @param deltaShares - Delta shares (from splitting the delta)
|
|
57
|
+
* @param newStateId - State ID for the new split state
|
|
58
|
+
* @param newMetadata - Metadata for the new state
|
|
59
|
+
* @param newHmacKey - Fresh HMAC key for the new state
|
|
60
|
+
* @param newHmacSignature - Fresh HMAC signature for the new padded state
|
|
61
|
+
*/
|
|
62
|
+
export declare function applyDeltaShares(oldShares: readonly StateShare[], deltaShares: Uint8Array[], newStateId: string, newHmacKey: Uint8Array, newHmacSignature: Uint8Array): Result<StateShare[], ContinuityError>;
|
|
63
|
+
/**
|
|
64
|
+
* Perform an incremental state update using Reverse-XorIDA.
|
|
65
|
+
*
|
|
66
|
+
* Instead of re-splitting the entire new state:
|
|
67
|
+
* 1. Pad both old and new to the same block-aligned size
|
|
68
|
+
* 2. Compute delta = oldPadded XOR newPadded
|
|
69
|
+
* 3. Split the delta
|
|
70
|
+
* 4. XOR each old share with the corresponding delta share
|
|
71
|
+
* 5. Recompute HMAC fresh for the new padded state
|
|
72
|
+
*
|
|
73
|
+
* Falls back to fresh split when sizes differ significantly
|
|
74
|
+
* (incremental update only works when padded lengths match).
|
|
75
|
+
*
|
|
76
|
+
* @param oldSplitState - Previous split state
|
|
77
|
+
* @param oldSnapshot - Previous state snapshot
|
|
78
|
+
* @param newSnapshot - New state snapshot
|
|
79
|
+
* @returns Updated SplitState with new shares
|
|
80
|
+
*/
|
|
81
|
+
export declare function incrementalUpdate(oldSplitState: SplitState, oldSnapshot: StateSnapshot, newSnapshot: StateSnapshot): Promise<Result<SplitState, ContinuityError>>;
|
|
82
|
+
/**
|
|
83
|
+
* Extension 1: Undo / Time-Travel
|
|
84
|
+
*
|
|
85
|
+
* Reverse a delta by re-applying it (XOR is its own inverse in GF(2)).
|
|
86
|
+
* Given shares at state B and the delta A→B, produces shares at state A.
|
|
87
|
+
*
|
|
88
|
+
* Math: shares_A = shares_B ⊕ deltaShares(A→B)
|
|
89
|
+
* because: (A ⊕ Δ) ⊕ Δ = A (XOR self-inverse)
|
|
90
|
+
*/
|
|
91
|
+
export declare function undoDelta(currentShares: readonly StateShare[], deltaShares: Uint8Array[], previousStateId: string, hmacKey: Uint8Array, hmacSignature: Uint8Array): Result<StateShare[], ContinuityError>;
|
|
92
|
+
/**
|
|
93
|
+
* Extension 2: Branch
|
|
94
|
+
*
|
|
95
|
+
* Fork a split state into an independent branch by copying shares.
|
|
96
|
+
* The branch can diverge independently from the original.
|
|
97
|
+
*
|
|
98
|
+
* @param source - The split state to branch from
|
|
99
|
+
* @param branchStateId - New stateId for the branch
|
|
100
|
+
* @returns Independent copy of the split state
|
|
101
|
+
*/
|
|
102
|
+
export declare function branchState(source: SplitState, branchStateId: string): SplitState;
|
|
103
|
+
/**
|
|
104
|
+
* Extension 3: Delta Squashing
|
|
105
|
+
*
|
|
106
|
+
* Combine multiple sequential deltas into a single delta.
|
|
107
|
+
* Exploits GF(2) associativity: Δ_total = Δ₁ ⊕ Δ₂ ⊕ ... ⊕ Δₙ
|
|
108
|
+
*
|
|
109
|
+
* @param deltas - Ordered array of deltas to squash (must form a chain)
|
|
110
|
+
* @returns Single squashed delta from first.fromStateId to last.toStateId
|
|
111
|
+
*/
|
|
112
|
+
export declare function squashDeltas(deltas: readonly StateDelta[]): Result<StateDelta, ContinuityError>;
|
|
113
|
+
/**
|
|
114
|
+
* Extension 4: Blind Update
|
|
115
|
+
*
|
|
116
|
+
* Apply a delta to shares without seeing the plaintext state.
|
|
117
|
+
* The share holder never reconstructs the full state.
|
|
118
|
+
*
|
|
119
|
+
* Math: newShare_i = oldShare_i ⊕ deltaShare_i
|
|
120
|
+
*
|
|
121
|
+
* @param share - A single share to update
|
|
122
|
+
* @param deltaShareData - The corresponding delta share data
|
|
123
|
+
* @param newStateId - New state identifier
|
|
124
|
+
* @param newHmacKey - Fresh HMAC key
|
|
125
|
+
* @param newHmacSignature - Fresh HMAC signature
|
|
126
|
+
*/
|
|
127
|
+
export declare function blindUpdateShare(share: StateShare, deltaShareData: Uint8Array, newStateId: string, newHmacKey: Uint8Array, newHmacSignature: Uint8Array): Result<StateShare, ContinuityError>;
|
|
128
|
+
/**
|
|
129
|
+
* Extension 5: Proactive Share Refresh
|
|
130
|
+
*
|
|
131
|
+
* Re-randomize shares while preserving reconstruction correctness.
|
|
132
|
+
* Generates a random mask, splits it, XORs with existing shares.
|
|
133
|
+
* The mask XORs to zero upon reconstruction, so the plaintext is unchanged.
|
|
134
|
+
*
|
|
135
|
+
* @param splitState - Current split state
|
|
136
|
+
* @returns Refreshed split state with re-randomized shares (same plaintext)
|
|
137
|
+
*/
|
|
138
|
+
export declare function refreshShares(state: SplitState): Promise<Result<SplitState, ContinuityError>>;
|
|
139
|
+
/**
|
|
140
|
+
* Extension 6: Blind Equality Check
|
|
141
|
+
*
|
|
142
|
+
* Compare two split states for equality without reconstructing plaintext.
|
|
143
|
+
* Computes delta between corresponding shares — if all delta bytes are zero,
|
|
144
|
+
* the states are equal.
|
|
145
|
+
*
|
|
146
|
+
* @param a - First split state
|
|
147
|
+
* @param b - Second split state
|
|
148
|
+
* @returns true if both states encode the same plaintext
|
|
149
|
+
*/
|
|
150
|
+
export declare function blindEqual(a: SplitState, b: SplitState): Result<boolean, ContinuityError>;
|
|
151
|
+
/**
|
|
152
|
+
* Extension 7: Network-Coded Sync
|
|
153
|
+
*
|
|
154
|
+
* Combine multiple shares during transit using XOR for bandwidth efficiency.
|
|
155
|
+
* The receiver can extract individual shares if they hold the complementary shares.
|
|
156
|
+
*
|
|
157
|
+
* coded = share_i ⊕ share_j
|
|
158
|
+
* share_i = coded ⊕ share_j (if receiver already holds share_j)
|
|
159
|
+
*
|
|
160
|
+
* @param shares - Array of shares to combine
|
|
161
|
+
* @returns Single coded block (XOR of all input shares)
|
|
162
|
+
*/
|
|
163
|
+
export declare function networkCodeShares(shares: readonly StateShare[]): Result<Uint8Array, ContinuityError>;
|
|
164
|
+
/**
|
|
165
|
+
* Decode a network-coded block using a held share.
|
|
166
|
+
*
|
|
167
|
+
* If coded = share_i ⊕ share_j, and you hold share_j:
|
|
168
|
+
* share_i = coded ⊕ share_j
|
|
169
|
+
*
|
|
170
|
+
* @param codedBlock - The network-coded block
|
|
171
|
+
* @param heldShare - The share the receiver already holds
|
|
172
|
+
* @returns The extracted share data
|
|
173
|
+
*/
|
|
174
|
+
export declare function networkDecodeShare(codedBlock: Uint8Array, heldShare: StateShare): Result<Uint8Array, ContinuityError>;
|