@aleph-ai/tinyaleph 1.3.0 → 1.4.1

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/core/beacon.js ADDED
@@ -0,0 +1,735 @@
1
+ /**
2
+ * Beacon System for Prime-Resonant Data Transfer
3
+ *
4
+ * From "How Data Summoning Works" paper:
5
+ * Instead of sending files directly, the system encodes data into
6
+ * resonance beacons that can be used to reconstruct the original
7
+ * information through non-local quantum-like processes.
8
+ *
9
+ * Key concepts:
10
+ * - ResonantFragment: Prime coefficient mappings for holographic encoding
11
+ * - Beacon: Compact pointer for file discovery and reconstruction
12
+ * - Chinese Remainder Theorem for reconstruction
13
+ * - Hilbert space mapping
14
+ */
15
+
16
+ const crypto = require('crypto');
17
+ const { isPrime, primesUpTo, factorize } = require('./prime');
18
+ const { Complex, PrimeState } = require('./hilbert');
19
+
20
+ /**
21
+ * Resonant Fragment
22
+ *
23
+ * A fragment of resonant information encoded using prime coefficients.
24
+ *
25
+ * interface ResonantFragment {
26
+ * coeffs: Map<number, number>; // Prime coefficient mappings
27
+ * center: [number, number]; // Hilbert space coordinates
28
+ * entropy: number; // Information density measure
29
+ * index: number[]; // Prime indices for reconstruction
30
+ * epoch: number; // Temporal versioning
31
+ * fingerprint: Uint8Array; // Cryptographic hash
32
+ * signature: Uint8Array; // Authentication signature
33
+ * }
34
+ */
35
+ class ResonantFragment {
36
+ /**
37
+ * Create a resonant fragment
38
+ * @param {Object} options - Configuration
39
+ */
40
+ constructor(options = {}) {
41
+ // Prime coefficient mappings
42
+ this.coeffs = options.coeffs || new Map();
43
+
44
+ // Hilbert space coordinates (2D projection)
45
+ this.center = options.center || [0, 0];
46
+
47
+ // Information density (entropy)
48
+ this.entropy = options.entropy || 0;
49
+
50
+ // Prime indices for reconstruction
51
+ this.index = options.index || [];
52
+
53
+ // Version/epoch
54
+ this.epoch = options.epoch || Date.now();
55
+
56
+ // Cryptographic fingerprint
57
+ this.fingerprint = options.fingerprint || null;
58
+
59
+ // Authentication signature
60
+ this.signature = options.signature || null;
61
+
62
+ // Source metadata
63
+ this.metadata = options.metadata || {};
64
+ }
65
+
66
+ /**
67
+ * Encode data into a resonant fragment using prime decomposition
68
+ *
69
+ * @param {Buffer|string} data - Data to encode
70
+ * @param {Array<number>} primes - Prime basis to use
71
+ * @returns {ResonantFragment} Encoded fragment
72
+ */
73
+ static fromData(data, primes = null) {
74
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
75
+ const defaultPrimes = primes || primesUpTo(256).slice(0, 64);
76
+
77
+ const fragment = new ResonantFragment();
78
+ fragment.index = defaultPrimes;
79
+
80
+ // Compute prime coefficients using modular arithmetic
81
+ // c_i = Σ_j data[j] * p_i^j mod M
82
+ const M = BigInt('1000000007'); // Large prime modulus
83
+
84
+ for (const p of defaultPrimes) {
85
+ let coeff = 0n;
86
+ let power = 1n;
87
+ const bp = BigInt(p);
88
+
89
+ for (let j = 0; j < buffer.length; j++) {
90
+ coeff = (coeff + BigInt(buffer[j]) * power) % M;
91
+ power = (power * bp) % M;
92
+ }
93
+
94
+ fragment.coeffs.set(p, Number(coeff));
95
+ }
96
+
97
+ // Compute Hilbert space center from first two dominant coefficients
98
+ const sortedCoeffs = [...fragment.coeffs.entries()]
99
+ .sort((a, b) => b[1] - a[1]);
100
+
101
+ if (sortedCoeffs.length >= 2) {
102
+ const maxCoeff = Math.max(...sortedCoeffs.map(c => c[1])) || 1;
103
+ fragment.center = [
104
+ sortedCoeffs[0][1] / maxCoeff,
105
+ sortedCoeffs[1][1] / maxCoeff
106
+ ];
107
+ }
108
+
109
+ // Compute entropy from coefficient distribution
110
+ const totalCoeff = [...fragment.coeffs.values()].reduce((s, c) => s + c, 0);
111
+ if (totalCoeff > 0) {
112
+ let h = 0;
113
+ for (const coeff of fragment.coeffs.values()) {
114
+ const p = coeff / totalCoeff;
115
+ if (p > 0) {
116
+ h -= p * Math.log2(p);
117
+ }
118
+ }
119
+ fragment.entropy = h;
120
+ }
121
+
122
+ // Compute fingerprint
123
+ fragment.fingerprint = crypto.createHash('sha256').update(buffer).digest();
124
+
125
+ return fragment;
126
+ }
127
+
128
+ /**
129
+ * Convert fragment to PrimeState representation
130
+ */
131
+ toPrimeState() {
132
+ const state = new PrimeState(this.index);
133
+
134
+ const maxCoeff = Math.max(...this.coeffs.values()) || 1;
135
+
136
+ for (const [p, coeff] of this.coeffs) {
137
+ // Normalize coefficient to amplitude
138
+ const amplitude = coeff / maxCoeff;
139
+ // Phase from coefficient mod 2π
140
+ const phase = (2 * Math.PI * coeff) / 1000000007;
141
+
142
+ state.set(p, Complex.fromPolar(amplitude, phase));
143
+ }
144
+
145
+ return state.normalize();
146
+ }
147
+
148
+ /**
149
+ * Compute resonance strength with another fragment
150
+ * @param {ResonantFragment} other - Other fragment
151
+ */
152
+ resonanceWith(other) {
153
+ let dot = 0;
154
+ let norm1 = 0;
155
+ let norm2 = 0;
156
+
157
+ for (const p of this.index) {
158
+ const c1 = this.coeffs.get(p) || 0;
159
+ const c2 = other.coeffs.get(p) || 0;
160
+ dot += c1 * c2;
161
+ norm1 += c1 * c1;
162
+ norm2 += c2 * c2;
163
+ }
164
+
165
+ const denom = Math.sqrt(norm1) * Math.sqrt(norm2);
166
+ return denom > 0 ? dot / denom : 0;
167
+ }
168
+
169
+ /**
170
+ * Serialize fragment
171
+ */
172
+ toJSON() {
173
+ return {
174
+ coeffs: Array.from(this.coeffs.entries()),
175
+ center: this.center,
176
+ entropy: this.entropy,
177
+ index: this.index,
178
+ epoch: this.epoch,
179
+ fingerprint: this.fingerprint ? this.fingerprint.toString('hex') : null,
180
+ signature: this.signature ? this.signature.toString('hex') : null,
181
+ metadata: this.metadata
182
+ };
183
+ }
184
+
185
+ /**
186
+ * Deserialize fragment
187
+ */
188
+ static fromJSON(data) {
189
+ const fragment = new ResonantFragment({
190
+ center: data.center,
191
+ entropy: data.entropy,
192
+ index: data.index,
193
+ epoch: data.epoch,
194
+ metadata: data.metadata
195
+ });
196
+
197
+ fragment.coeffs = new Map(data.coeffs);
198
+
199
+ if (data.fingerprint) {
200
+ fragment.fingerprint = Buffer.from(data.fingerprint, 'hex');
201
+ }
202
+ if (data.signature) {
203
+ fragment.signature = Buffer.from(data.signature, 'hex');
204
+ }
205
+
206
+ return fragment;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Beacon
212
+ *
213
+ * Compact pointer that enables file discovery and reconstruction.
214
+ * Contains minimal information needed to summon the original data.
215
+ */
216
+ class Beacon {
217
+ /**
218
+ * Create a beacon
219
+ * @param {Object} options - Configuration
220
+ */
221
+ constructor(options = {}) {
222
+ this.id = options.id || Beacon.generateId();
223
+ this.type = options.type || 'fragment';
224
+ this.authorId = options.authorId || null;
225
+
226
+ // Core resonance data
227
+ this.primeIndices = options.primeIndices || ''; // Encoded prime sequence
228
+ this.epoch = options.epoch || Date.now();
229
+
230
+ // Verification
231
+ this.fingerprint = options.fingerprint || null;
232
+ this.signature = options.signature || null;
233
+
234
+ // Metadata
235
+ this.metadata = options.metadata || {};
236
+ this.createdAt = options.createdAt || new Date().toISOString();
237
+ }
238
+
239
+ /**
240
+ * Generate unique beacon ID
241
+ */
242
+ static generateId() {
243
+ return `bcn_${Date.now().toString(36)}_${crypto.randomBytes(8).toString('hex')}`;
244
+ }
245
+
246
+ /**
247
+ * Create beacon from resonant fragment
248
+ * @param {ResonantFragment} fragment - Source fragment
249
+ * @param {Object} options - Additional options
250
+ */
251
+ static fromFragment(fragment, options = {}) {
252
+ const beacon = new Beacon({
253
+ type: options.type || 'fragment',
254
+ authorId: options.authorId,
255
+ epoch: fragment.epoch,
256
+ fingerprint: fragment.fingerprint,
257
+ metadata: options.metadata
258
+ });
259
+
260
+ // Encode prime indices compactly
261
+ // Use top-k coefficients by magnitude
262
+ const topK = options.topK || 16;
263
+ const sortedPrimes = [...fragment.coeffs.entries()]
264
+ .sort((a, b) => b[1] - a[1])
265
+ .slice(0, topK)
266
+ .map(([p]) => p);
267
+
268
+ // Delta encoding for compression
269
+ beacon.primeIndices = Beacon.encodePrimes(sortedPrimes);
270
+
271
+ return beacon;
272
+ }
273
+
274
+ /**
275
+ * Encode primes using delta + base64
276
+ */
277
+ static encodePrimes(primes) {
278
+ if (primes.length === 0) return '';
279
+
280
+ const deltas = [primes[0]];
281
+ for (let i = 1; i < primes.length; i++) {
282
+ deltas.push(primes[i] - primes[i - 1]);
283
+ }
284
+
285
+ // Pack as varints
286
+ const buffer = Buffer.alloc(primes.length * 4);
287
+ let offset = 0;
288
+
289
+ for (const d of deltas) {
290
+ buffer.writeInt32LE(d, offset);
291
+ offset += 4;
292
+ }
293
+
294
+ return buffer.slice(0, offset).toString('base64');
295
+ }
296
+
297
+ /**
298
+ * Decode primes from encoded string
299
+ */
300
+ static decodePrimes(encoded) {
301
+ if (!encoded) return [];
302
+
303
+ const buffer = Buffer.from(encoded, 'base64');
304
+ const deltas = [];
305
+
306
+ for (let i = 0; i < buffer.length; i += 4) {
307
+ deltas.push(buffer.readInt32LE(i));
308
+ }
309
+
310
+ // Reconstruct from deltas
311
+ const primes = [];
312
+ let current = 0;
313
+
314
+ for (const d of deltas) {
315
+ current += d;
316
+ primes.push(current);
317
+ }
318
+
319
+ return primes;
320
+ }
321
+
322
+ /**
323
+ * Get decoded prime indices
324
+ */
325
+ getPrimes() {
326
+ return Beacon.decodePrimes(this.primeIndices);
327
+ }
328
+
329
+ /**
330
+ * Verify fingerprint matches
331
+ * @param {Buffer} data - Original data to verify
332
+ */
333
+ verify(data) {
334
+ if (!this.fingerprint) return false;
335
+
336
+ const hash = crypto.createHash('sha256').update(data).digest();
337
+ return hash.equals(this.fingerprint);
338
+ }
339
+
340
+ /**
341
+ * Serialize beacon
342
+ */
343
+ toJSON() {
344
+ return {
345
+ id: this.id,
346
+ type: this.type,
347
+ authorId: this.authorId,
348
+ primeIndices: this.primeIndices,
349
+ epoch: this.epoch,
350
+ fingerprint: this.fingerprint ? this.fingerprint.toString('hex') : null,
351
+ signature: this.signature ? this.signature.toString('hex') : null,
352
+ metadata: this.metadata,
353
+ createdAt: this.createdAt
354
+ };
355
+ }
356
+
357
+ /**
358
+ * Deserialize beacon
359
+ */
360
+ static fromJSON(data) {
361
+ const beacon = new Beacon({
362
+ id: data.id,
363
+ type: data.type,
364
+ authorId: data.authorId,
365
+ primeIndices: data.primeIndices,
366
+ epoch: data.epoch,
367
+ metadata: data.metadata,
368
+ createdAt: data.createdAt
369
+ });
370
+
371
+ if (data.fingerprint) {
372
+ beacon.fingerprint = Buffer.from(data.fingerprint, 'hex');
373
+ }
374
+ if (data.signature) {
375
+ beacon.signature = Buffer.from(data.signature, 'hex');
376
+ }
377
+
378
+ return beacon;
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Beacon Cache Manager
384
+ *
385
+ * Client-side caching system for beacons.
386
+ */
387
+ class BeaconCache {
388
+ constructor(options = {}) {
389
+ // L1: In-memory cache
390
+ this.cache = new Map();
391
+
392
+ // L2: User-indexed beacons
393
+ this.userIndex = new Map();
394
+
395
+ // L3: Type-indexed beacons
396
+ this.typeIndex = new Map();
397
+
398
+ // Cache limits
399
+ this.maxSize = options.maxSize || 1000;
400
+
401
+ // LRU tracking
402
+ this.accessOrder = [];
403
+ }
404
+
405
+ /**
406
+ * Store a beacon
407
+ * @param {Beacon} beacon - Beacon to store
408
+ */
409
+ set(beacon) {
410
+ // Update access order
411
+ this.touch(beacon.id);
412
+
413
+ // Store in main cache
414
+ this.cache.set(beacon.id, beacon);
415
+
416
+ // Index by user
417
+ if (beacon.authorId) {
418
+ if (!this.userIndex.has(beacon.authorId)) {
419
+ this.userIndex.set(beacon.authorId, new Set());
420
+ }
421
+ this.userIndex.get(beacon.authorId).add(beacon.id);
422
+ }
423
+
424
+ // Index by type
425
+ if (!this.typeIndex.has(beacon.type)) {
426
+ this.typeIndex.set(beacon.type, new Set());
427
+ }
428
+ this.typeIndex.get(beacon.type).add(beacon.id);
429
+
430
+ // Evict if over capacity
431
+ this.evict();
432
+ }
433
+
434
+ /**
435
+ * Get beacon by ID
436
+ * @param {string} id - Beacon ID
437
+ */
438
+ get(id) {
439
+ const beacon = this.cache.get(id);
440
+ if (beacon) {
441
+ this.touch(id);
442
+ }
443
+ return beacon;
444
+ }
445
+
446
+ /**
447
+ * Get beacons by user
448
+ * @param {string} userId - User ID
449
+ */
450
+ getByUser(userId) {
451
+ const ids = this.userIndex.get(userId);
452
+ if (!ids) return [];
453
+
454
+ return [...ids].map(id => this.get(id)).filter(Boolean);
455
+ }
456
+
457
+ /**
458
+ * Get beacons by type
459
+ * @param {string} type - Beacon type
460
+ */
461
+ getByType(type) {
462
+ const ids = this.typeIndex.get(type);
463
+ if (!ids) return [];
464
+
465
+ return [...ids].map(id => this.get(id)).filter(Boolean);
466
+ }
467
+
468
+ /**
469
+ * Touch ID for LRU
470
+ */
471
+ touch(id) {
472
+ const idx = this.accessOrder.indexOf(id);
473
+ if (idx !== -1) {
474
+ this.accessOrder.splice(idx, 1);
475
+ }
476
+ this.accessOrder.push(id);
477
+ }
478
+
479
+ /**
480
+ * Evict oldest entries if over capacity
481
+ */
482
+ evict() {
483
+ while (this.cache.size > this.maxSize && this.accessOrder.length > 0) {
484
+ const oldId = this.accessOrder.shift();
485
+ const beacon = this.cache.get(oldId);
486
+
487
+ if (beacon) {
488
+ // Remove from indices
489
+ if (beacon.authorId && this.userIndex.has(beacon.authorId)) {
490
+ this.userIndex.get(beacon.authorId).delete(oldId);
491
+ }
492
+ if (this.typeIndex.has(beacon.type)) {
493
+ this.typeIndex.get(beacon.type).delete(oldId);
494
+ }
495
+
496
+ this.cache.delete(oldId);
497
+ }
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Clear all cached beacons
503
+ */
504
+ clear() {
505
+ this.cache.clear();
506
+ this.userIndex.clear();
507
+ this.typeIndex.clear();
508
+ this.accessOrder = [];
509
+ }
510
+
511
+ /**
512
+ * Get cache stats
513
+ */
514
+ stats() {
515
+ return {
516
+ size: this.cache.size,
517
+ maxSize: this.maxSize,
518
+ userCount: this.userIndex.size,
519
+ typeCount: this.typeIndex.size
520
+ };
521
+ }
522
+ }
523
+
524
+ /**
525
+ * Chinese Remainder Theorem Reconstructor
526
+ *
527
+ * Uses CRT to reconstruct original data from prime coefficients.
528
+ * F(x) = Σ(i=0 to n) c_i * p_i^k mod M
529
+ * R(F) = CRT(F, P) → D
530
+ */
531
+ class CRTReconstructor {
532
+ /**
533
+ * Extended Euclidean Algorithm
534
+ * Returns [gcd, x, y] such that ax + by = gcd(a, b)
535
+ */
536
+ static extendedGcd(a, b) {
537
+ if (b === 0n) {
538
+ return [a, 1n, 0n];
539
+ }
540
+
541
+ const [g, x, y] = CRTReconstructor.extendedGcd(b, a % b);
542
+ return [g, y, x - (a / b) * y];
543
+ }
544
+
545
+ /**
546
+ * Modular inverse: a^(-1) mod m
547
+ */
548
+ static modInverse(a, m) {
549
+ const [g, x] = CRTReconstructor.extendedGcd(a % m, m);
550
+
551
+ if (g !== 1n) {
552
+ throw new Error(`No modular inverse exists for ${a} mod ${m}`);
553
+ }
554
+
555
+ return ((x % m) + m) % m;
556
+ }
557
+
558
+ /**
559
+ * Reconstruct using CRT
560
+ *
561
+ * Given remainders r_i for moduli m_i, find x such that:
562
+ * x ≡ r_1 (mod m_1)
563
+ * x ≡ r_2 (mod m_2)
564
+ * ...
565
+ *
566
+ * @param {Array<[bigint, bigint]>} congruences - Array of [remainder, modulus] pairs
567
+ * @returns {bigint} Reconstructed value
568
+ */
569
+ static reconstruct(congruences) {
570
+ if (congruences.length === 0) return 0n;
571
+
572
+ // Compute product of all moduli
573
+ const M = congruences.reduce((prod, [, m]) => prod * m, 1n);
574
+
575
+ let result = 0n;
576
+
577
+ for (const [r, m] of congruences) {
578
+ const Mi = M / m;
579
+ const yi = CRTReconstructor.modInverse(Mi, m);
580
+ result = (result + r * Mi * yi) % M;
581
+ }
582
+
583
+ return result;
584
+ }
585
+
586
+ /**
587
+ * Reconstruct data from fragment coefficients
588
+ *
589
+ * @param {ResonantFragment} fragment - Source fragment
590
+ * @param {number} length - Expected output length
591
+ * @returns {Buffer} Reconstructed data
592
+ */
593
+ static fromFragment(fragment, length = 256) {
594
+ // Build congruence system from coefficients
595
+ const congruences = [];
596
+
597
+ for (const [p, coeff] of fragment.coeffs) {
598
+ congruences.push([BigInt(coeff), BigInt(p)]);
599
+ }
600
+
601
+ if (congruences.length === 0) {
602
+ return Buffer.alloc(0);
603
+ }
604
+
605
+ // Use CRT to reconstruct combined value
606
+ const reconstructed = CRTReconstructor.reconstruct(congruences);
607
+
608
+ // Convert to bytes
609
+ const hexStr = reconstructed.toString(16).padStart(length * 2, '0');
610
+ const buffer = Buffer.from(hexStr.slice(-length * 2), 'hex');
611
+
612
+ return buffer;
613
+ }
614
+ }
615
+
616
+ /**
617
+ * Data Summoner
618
+ *
619
+ * Orchestrates the summoning process:
620
+ * 1. Parse beacon to get prime indices
621
+ * 2. Query for matching fragments
622
+ * 3. Use CRT to reconstruct data
623
+ * 4. Verify integrity with fingerprint
624
+ */
625
+ class DataSummoner {
626
+ constructor(options = {}) {
627
+ this.fragmentStore = options.fragmentStore || new Map();
628
+ this.beaconCache = options.beaconCache || new BeaconCache();
629
+ }
630
+
631
+ /**
632
+ * Store a fragment and create beacon
633
+ * @param {Buffer|string} data - Data to store
634
+ * @param {Object} options - Storage options
635
+ */
636
+ store(data, options = {}) {
637
+ // Create fragment from data
638
+ const fragment = ResonantFragment.fromData(data);
639
+
640
+ // Store fragment
641
+ const fragmentId = `frag_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
642
+ this.fragmentStore.set(fragmentId, fragment);
643
+
644
+ // Create beacon
645
+ const beacon = Beacon.fromFragment(fragment, {
646
+ authorId: options.authorId,
647
+ metadata: {
648
+ ...options.metadata,
649
+ fragmentId,
650
+ originalLength: Buffer.isBuffer(data) ? data.length : Buffer.from(data).length
651
+ }
652
+ });
653
+
654
+ // Cache beacon
655
+ this.beaconCache.set(beacon);
656
+
657
+ return { beacon, fragment, fragmentId };
658
+ }
659
+
660
+ /**
661
+ * Summon data from beacon
662
+ * @param {Beacon|string} beaconOrId - Beacon or beacon ID
663
+ */
664
+ summon(beaconOrId) {
665
+ // Resolve beacon
666
+ let beacon;
667
+ if (typeof beaconOrId === 'string') {
668
+ beacon = this.beaconCache.get(beaconOrId);
669
+ if (!beacon) {
670
+ throw new Error(`Beacon not found: ${beaconOrId}`);
671
+ }
672
+ } else {
673
+ beacon = beaconOrId;
674
+ }
675
+
676
+ // Get fragment
677
+ const fragmentId = beacon.metadata?.fragmentId;
678
+ if (!fragmentId) {
679
+ throw new Error('Beacon has no fragmentId');
680
+ }
681
+
682
+ const fragment = this.fragmentStore.get(fragmentId);
683
+ if (!fragment) {
684
+ throw new Error(`Fragment not found: ${fragmentId}`);
685
+ }
686
+
687
+ // Reconstruct data
688
+ const length = beacon.metadata?.originalLength || 256;
689
+ const data = CRTReconstructor.fromFragment(fragment, length);
690
+
691
+ // Verify integrity
692
+ if (beacon.fingerprint && !beacon.verify(data)) {
693
+ return {
694
+ success: false,
695
+ error: 'Fingerprint verification failed',
696
+ data: null
697
+ };
698
+ }
699
+
700
+ return {
701
+ success: true,
702
+ data,
703
+ fragment,
704
+ beacon
705
+ };
706
+ }
707
+
708
+ /**
709
+ * Find fragments matching a query state
710
+ * @param {PrimeState} queryState - Query state
711
+ * @param {number} threshold - Minimum resonance
712
+ */
713
+ findSimilar(queryState, threshold = 0.5) {
714
+ const results = [];
715
+
716
+ for (const [id, fragment] of this.fragmentStore) {
717
+ const fragState = fragment.toPrimeState();
718
+ const resonance = queryState.coherence(fragState);
719
+
720
+ if (resonance >= threshold) {
721
+ results.push({ id, fragment, resonance });
722
+ }
723
+ }
724
+
725
+ return results.sort((a, b) => b.resonance - a.resonance);
726
+ }
727
+ }
728
+
729
+ module.exports = {
730
+ ResonantFragment,
731
+ Beacon,
732
+ BeaconCache,
733
+ CRTReconstructor,
734
+ DataSummoner
735
+ };