hyperspace-sdk-ts 3.1.2 → 3.1.4

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # HyperspaceDB TypeScript SDK
2
2
 
3
- Official TypeScript client for HyperspaceDB gRPC API v3.1.0.
3
+ Official TypeScript client for HyperspaceDB gRPC API v3.1.1.
4
4
 
5
5
  Use this SDK for:
6
6
  - collection lifecycle management
@@ -144,9 +144,9 @@ const results = await client.searchText("How to use HyperspaceDB?", 10, "coll",
144
144
  });
145
145
  ```
146
146
 
147
- ### Geometric Filters (New in v3.0)
147
+ ### Geometric Filters
148
148
 
149
- HyperspaceDB v3.0 introduces advanced spatial filters that run on the engine level:
149
+ HyperspaceDB introduces advanced spatial filters that run on the engine level:
150
150
 
151
151
  ```ts
152
152
  // 1. Proximity Search (Ball)
@@ -323,7 +323,7 @@ const syncedThought = CognitiveMath.contextResonance(thought, globalContext, 0.5
323
323
  const relation = await client.predictRelation(1, 2);
324
324
  ```
325
325
 
326
- ## Implicit Graph Engine (v3.2)
326
+ ## Implicit Graph Engine (v3.1.1)
327
327
 
328
328
  HyperspaceDB treats your vectors as nodes in a dynamic graph. Relationships are inferred from the geometry:
329
329
  - **Lorentz / Poincare**: Hierarchy and subsumption (light cones).
@@ -420,3 +420,58 @@ const vector = await embedder.encode("my text");
420
420
  All methods reject on transport/protocol errors. Targets gRPC data plane operations.
421
421
  For control plane endpoints (`/api/*`), use regular HTTP requests to the server's HTTP port.
422
422
 
423
+ ## Zero-Knowledge Client-Side Encryption (ZK-Privacy)
424
+
425
+ HyperspaceDB v3.1.1 introduces Zero-Knowledge client-side encryption (ZK-Privacy). All private data (vectors, metadata, payloads) are encrypted/obfuscated *before* they leave the client. The database server never sees the raw vectors or plaintext data, ensuring maximum security even in public or untrusted DePIN environments.
426
+
427
+ ### Key Features
428
+ 1. **Vector Projection**: High-dimensional vectors are projected using a deterministic orthogonal matrix (or Lorentz boost matrix for hyperbolic spaces) generated from the collection key. This preserves distances (L2, Cosine, Lorentz) while hiding the vector coordinates.
429
+ 2. **Anisotropic Noise Injection**: Injecting subtle deterministic noise into the vectors to prevent reconstruction attacks.
430
+ 3. **Payload Encryption**: Sidecar payloads are encrypted client-side using AES-256-GCM before being sent to the database.
431
+ 4. **Metadata Hashing**: Metadata keys and values are obfuscated using HMAC-SHA256.
432
+
433
+ ### Usage Example
434
+
435
+ ```ts
436
+ import { HyperspaceClient } from "hyperspace-sdk-ts";
437
+
438
+ async function main() {
439
+ const client = new HyperspaceClient("localhost:50051", "I_LOVE_HYPERSPACEDB");
440
+
441
+ const collection = "encrypted_docs";
442
+ const secretKey = "my-super-secret-key";
443
+
444
+ // Register collection key to enable automatic client-side encryption/decryption
445
+ // noiseSigma defaults to 0.02 (2% anisotropic noise)
446
+ client.registerCollectionKey(collection, secretKey, "cosine", 0.02);
447
+
448
+ // 1. Insert vector (will be projected, noise injected, payload encrypted, metadata hashed)
449
+ await client.insert(
450
+ 1,
451
+ [0.1, 0.2, 0.3],
452
+ { category: "confidential" },
453
+ collection,
454
+ undefined,
455
+ undefined,
456
+ Buffer.from("This is a highly secret document payload", "utf-8")
457
+ );
458
+
459
+ // 2. Search (search vector is projected and noise-injected; results are decrypted locally)
460
+ const results = await client.search([0.1, 0.2, 0.3], 5, collection, {
461
+ // Filters are automatically hashed client-side
462
+ filter: { category: "confidential" }
463
+ });
464
+
465
+ for (const res of results) {
466
+ console.log(`ID: ${res.id}, Distance: ${res.distance}`);
467
+ if (res.payload) {
468
+ console.log(`Decrypted Payload: ${Buffer.from(res.payload).toString("utf-8")}`);
469
+ }
470
+ }
471
+
472
+ client.close();
473
+ }
474
+
475
+ main().catch(console.error);
476
+ ```
477
+
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as grpc from '@grpc/grpc-js';
2
2
  import { DurabilityLevel, EventMessage } from './proto/hyperspace_pb';
3
3
  import * as hyperspace_pb from './proto/hyperspace_pb';
4
- export * as CognitiveMath from './math';
4
+ export * as CognitiveMathExport from './math';
5
5
  export { TribunalContext } from './agents';
6
6
  export { DurabilityLevel };
7
7
  export type TypedMetadataValue = string | number | boolean;
@@ -140,12 +140,30 @@ export declare class HyperspaceClient {
140
140
  private host;
141
141
  private apiKey?;
142
142
  private userId?;
143
+ embedder?: {
144
+ encode: (text: string) => Promise<number[]> | number[];
145
+ };
146
+ private collectionKeys;
147
+ private encryptionContexts;
148
+ private collectionMetrics;
149
+ private collectionNoiseSigmas;
150
+ private collectionSchemas;
143
151
  private static toVectorList;
144
152
  private static toProtoMetadataValue;
145
153
  private static parseTypedMetadata;
146
154
  private toProtoFilter;
147
155
  constructor(host?: string, apiKey?: string, userId?: string);
148
- createCollection(name: string, schema: CollectionSchema): Promise<boolean>;
156
+ registerCollectionKey(collectionName: string, key: string, metric?: string, noiseSigma?: number, schema?: CollectionSchema): void;
157
+ private deriveKeys;
158
+ private encryptPayload;
159
+ private decryptPayload;
160
+ private hashMetadataKey;
161
+ private hashMetadataValue;
162
+ private _getEncryptionContext;
163
+ private _projectSingleBlock;
164
+ private _projectCollectionVector;
165
+ private _encryptFilters;
166
+ createCollection(name: string, schema: CollectionSchema, encryptionKey?: string, noiseSigma?: number): Promise<boolean>;
149
167
  deleteCollection(name: string): Promise<boolean>;
150
168
  freezeCollection(name: string): Promise<string>;
151
169
  unfreezeCollection(name: string): Promise<string>;
package/dist/client.js CHANGED
@@ -33,13 +33,14 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.HyperspaceClient = exports.HyperbolicMath = exports.DurabilityLevel = exports.TribunalContext = exports.CognitiveMath = void 0;
36
+ exports.HyperspaceClient = exports.HyperbolicMath = exports.DurabilityLevel = exports.TribunalContext = exports.CognitiveMathExport = void 0;
37
37
  const grpc = __importStar(require("@grpc/grpc-js"));
38
38
  const hyperspace_grpc_pb_1 = require("./proto/hyperspace_grpc_pb");
39
39
  const hyperspace_pb_1 = require("./proto/hyperspace_pb");
40
40
  Object.defineProperty(exports, "DurabilityLevel", { enumerable: true, get: function () { return hyperspace_pb_1.DurabilityLevel; } });
41
41
  const hyperspace_pb = __importStar(require("./proto/hyperspace_pb")); // New, for direct access to types
42
- exports.CognitiveMath = __importStar(require("./math"));
42
+ const CognitiveMath = __importStar(require("./math"));
43
+ exports.CognitiveMathExport = __importStar(require("./math"));
43
44
  var agents_1 = require("./agents");
44
45
  Object.defineProperty(exports, "TribunalContext", { enumerable: true, get: function () { return agents_1.TribunalContext; } });
45
46
  exports.HyperbolicMath = {
@@ -292,6 +293,11 @@ class HyperspaceClient {
292
293
  return pf;
293
294
  }
294
295
  constructor(host = 'localhost:50051', apiKey, userId) {
296
+ this.collectionKeys = {};
297
+ this.encryptionContexts = {};
298
+ this.collectionMetrics = {};
299
+ this.collectionNoiseSigmas = {};
300
+ this.collectionSchemas = {};
295
301
  this.host = host;
296
302
  this.apiKey = apiKey;
297
303
  this.userId = userId;
@@ -313,8 +319,223 @@ class HyperspaceClient {
313
319
  this.metadata.add('x-hyperspace-user-id', userId);
314
320
  }
315
321
  }
322
+ registerCollectionKey(collectionName, key, metric = "l2", noiseSigma = 0.02, schema) {
323
+ this.collectionKeys[collectionName] = key;
324
+ this.collectionMetrics[collectionName] = metric;
325
+ this.collectionNoiseSigmas[collectionName] = noiseSigma;
326
+ if (schema) {
327
+ this.collectionSchemas[collectionName] = schema;
328
+ }
329
+ if (this.encryptionContexts[collectionName]) {
330
+ delete this.encryptionContexts[collectionName];
331
+ }
332
+ }
333
+ deriveKeys(password, collectionName) {
334
+ const crypto = require('crypto');
335
+ const salt = crypto.createHash('sha256').update(collectionName).digest();
336
+ const aesKey = crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
337
+ const hmacKey = crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
338
+ return { aesKey, hmacKey };
339
+ }
340
+ encryptPayload(plaintext, aesKey) {
341
+ const crypto = require('crypto');
342
+ const iv = crypto.randomBytes(12);
343
+ const pbkdf2Salt = crypto.randomBytes(16);
344
+ const derivedAesKey = crypto.pbkdf2Sync(aesKey, pbkdf2Salt, 100000, 32, 'sha256');
345
+ const cipherPayload = crypto.createCipheriv('aes-256-gcm', derivedAesKey, iv);
346
+ const encryptedPayload = Buffer.concat([cipherPayload.update(plaintext), cipherPayload.final()]);
347
+ const payloadTag = cipherPayload.getAuthTag();
348
+ return Buffer.concat([pbkdf2Salt, iv, encryptedPayload, payloadTag]);
349
+ }
350
+ decryptPayload(data, aesKey) {
351
+ const crypto = require('crypto');
352
+ if (data.length < 16 + 12 + 16) {
353
+ throw new Error("Invalid encrypted payload size");
354
+ }
355
+ const pbkdf2Salt = data.subarray(0, 16);
356
+ const iv = data.subarray(16, 28);
357
+ const ciphertext = data.subarray(28, data.length - 16);
358
+ const tag = data.subarray(data.length - 16);
359
+ const derivedAesKey = crypto.pbkdf2Sync(aesKey, pbkdf2Salt, 100000, 32, 'sha256');
360
+ const decipher = crypto.createDecipheriv('aes-256-gcm', derivedAesKey, iv);
361
+ decipher.setAuthTag(tag);
362
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
363
+ }
364
+ hashMetadataKey(key, hmacKey) {
365
+ const crypto = require('crypto');
366
+ const hash = crypto.createHmac('sha256', hmacKey).update(key).digest('hex');
367
+ return "tag_" + hash.slice(0, 16);
368
+ }
369
+ hashMetadataValue(value, hmacKey) {
370
+ const crypto = require('crypto');
371
+ const hash = crypto.createHmac('sha256', hmacKey).update(value).digest('hex');
372
+ return "val_" + hash;
373
+ }
374
+ async _getEncryptionContext(collection, vectorDim, metric = "l2") {
375
+ if (!collection)
376
+ return null;
377
+ const key = this.collectionKeys[collection];
378
+ if (!key)
379
+ return null;
380
+ if (!this.collectionSchemas[collection]) {
381
+ try {
382
+ const stats = await this.getCollectionStats(collection);
383
+ if (stats && stats.schema) {
384
+ this.collectionSchemas[collection] = stats.schema;
385
+ }
386
+ }
387
+ catch (e) { }
388
+ }
389
+ if (!this.encryptionContexts[collection]) {
390
+ const { aesKey, hmacKey } = this.deriveKeys(key, collection);
391
+ this.encryptionContexts[collection] = {
392
+ aesKey,
393
+ hmacKey,
394
+ projectionMatrices: {}
395
+ };
396
+ }
397
+ const context = this.encryptionContexts[collection];
398
+ if (vectorDim !== undefined) {
399
+ if (!context.projectionMatrices[vectorDim]) {
400
+ const isLorentz = ["lorentz", "poincare"].includes(metric.toLowerCase());
401
+ const matrixDim = metric.toLowerCase() === "poincare" ? vectorDim + 1 : vectorDim;
402
+ if (isLorentz) {
403
+ context.projectionMatrices[vectorDim] = CognitiveMath.generateLorentzMatrix(matrixDim, context.hmacKey);
404
+ }
405
+ else {
406
+ context.projectionMatrices[vectorDim] = CognitiveMath.generateOrthogonalMatrix(matrixDim, context.hmacKey);
407
+ }
408
+ }
409
+ }
410
+ return context;
411
+ }
412
+ _projectSingleBlock(subVec, metric, context, blockId) {
413
+ const dim = subVec.length;
414
+ if (dim === 0)
415
+ return [];
416
+ const cacheKey = blockId ? `${dim}_${blockId}` : `${dim}`;
417
+ if (!context.projectionMatrices[cacheKey]) {
418
+ const isLorentz = ["lorentz", "poincare"].includes(metric.toLowerCase());
419
+ const matrixDim = metric.toLowerCase() === "poincare" ? dim + 1 : dim;
420
+ const crypto = require('crypto');
421
+ let seed = context.hmacKey;
422
+ if (blockId) {
423
+ seed = crypto.createHash('sha256').update(seed).update(blockId).digest();
424
+ }
425
+ if (isLorentz) {
426
+ context.projectionMatrices[cacheKey] = CognitiveMath.generateLorentzMatrix(matrixDim, seed);
427
+ }
428
+ else {
429
+ context.projectionMatrices[cacheKey] = CognitiveMath.generateOrthogonalMatrix(matrixDim, seed);
430
+ }
431
+ }
432
+ const matrix = context.projectionMatrices[cacheKey];
433
+ if (metric.toLowerCase() === "poincare") {
434
+ const lorentzVec = CognitiveMath.poincareToLorentz(subVec);
435
+ const projLorentz = CognitiveMath.projectVector(lorentzVec, matrix);
436
+ return CognitiveMath.lorentzToPoincare(projLorentz);
437
+ }
438
+ else {
439
+ return CognitiveMath.projectVector(subVec, matrix);
440
+ }
441
+ }
442
+ _projectCollectionVector(collection, vector, context, metric = "l2") {
443
+ const schema = this.collectionSchemas[collection];
444
+ if (!schema || !schema.components || schema.components.length === 0) {
445
+ return this._projectSingleBlock(vector, metric, context);
446
+ }
447
+ const components = schema.components;
448
+ const cascade = schema.cascadePipeline || [];
449
+ const componentCutoffs = {};
450
+ for (const layer of cascade) {
451
+ const compName = layer.componentName;
452
+ const cutoff = layer.cutoffDimension;
453
+ if (compName && cutoff) {
454
+ if (!componentCutoffs[compName]) {
455
+ componentCutoffs[compName] = [];
456
+ }
457
+ componentCutoffs[compName].push(cutoff);
458
+ }
459
+ }
460
+ for (const compName in componentCutoffs) {
461
+ componentCutoffs[compName] = Array.from(new Set(componentCutoffs[compName])).sort((a, b) => a - b);
462
+ }
463
+ const projectedParts = [];
464
+ let currentOffset = 0;
465
+ for (const comp of components) {
466
+ const compName = comp.name;
467
+ const compMetric = comp.metric;
468
+ const compDim = comp.fullDimension;
469
+ if (currentOffset >= vector.length) {
470
+ break;
471
+ }
472
+ let subVec = vector.slice(currentOffset, currentOffset + compDim);
473
+ if (subVec.length < compDim) {
474
+ subVec = subVec.concat(new Array(compDim - subVec.length).fill(0));
475
+ }
476
+ const cutoffs = componentCutoffs[compName] || [];
477
+ const validCutoffs = cutoffs.filter(c => c < compDim);
478
+ let projSub = [];
479
+ if (validCutoffs.length === 0) {
480
+ projSub = this._projectSingleBlock(subVec, compMetric, context);
481
+ }
482
+ else {
483
+ let blockStart = 0;
484
+ for (const cutoff of validCutoffs) {
485
+ const blockData = subVec.slice(blockStart, cutoff);
486
+ const projBlock = this._projectSingleBlock(blockData, compMetric, context, `${compName}_block_${blockStart}_${cutoff}`);
487
+ projSub = projSub.concat(projBlock);
488
+ blockStart = cutoff;
489
+ }
490
+ if (blockStart < compDim) {
491
+ const blockData = subVec.slice(blockStart, compDim);
492
+ const projBlock = this._projectSingleBlock(blockData, compMetric, context, `${compName}_block_${blockStart}_${compDim}`);
493
+ projSub = projSub.concat(projBlock);
494
+ }
495
+ }
496
+ projectedParts.push(...projSub);
497
+ currentOffset += compDim;
498
+ }
499
+ if (currentOffset < vector.length) {
500
+ projectedParts.push(...vector.slice(currentOffset));
501
+ }
502
+ return projectedParts;
503
+ }
504
+ _encryptFilters(filters, context) {
505
+ if (!filters)
506
+ return filters;
507
+ return filters.map(f => {
508
+ const nf = { ...f };
509
+ if (nf.match) {
510
+ nf.match = {
511
+ key: this.hashMetadataKey(nf.match.key, context.hmacKey),
512
+ value: this.hashMetadataValue(nf.match.value, context.hmacKey)
513
+ };
514
+ }
515
+ if (nf.prefix) {
516
+ nf.prefix = {
517
+ key: this.hashMetadataKey(nf.prefix.key, context.hmacKey),
518
+ prefix: this.hashMetadataValue(nf.prefix.prefix, context.hmacKey)
519
+ };
520
+ }
521
+ if (nf.and) {
522
+ nf.and = this._encryptFilters(nf.and, context);
523
+ }
524
+ if (nf.or) {
525
+ nf.or = this._encryptFilters(nf.or, context);
526
+ }
527
+ if (nf.not) {
528
+ nf.not = this._encryptFilters([nf.not], context)[0];
529
+ }
530
+ return nf;
531
+ });
532
+ }
316
533
  // ... (create/delete unchanged) ...
317
- createCollection(name, schema) {
534
+ createCollection(name, schema, encryptionKey = '', noiseSigma = 0.02) {
535
+ const metric = (schema.components && schema.components[0]) ? schema.components[0].metric : "l2";
536
+ if (encryptionKey) {
537
+ this.registerCollectionKey(name, encryptionKey, metric, noiseSigma, schema);
538
+ }
318
539
  return new Promise((resolve, reject) => {
319
540
  const req = new hyperspace_pb_1.CreateCollectionRequest();
320
541
  req.setName(name);
@@ -443,35 +664,85 @@ class HyperspaceClient {
443
664
  }
444
665
  insert(id, vector, meta, collection = '', durability = hyperspace_pb_1.DurabilityLevel.DEFAULT_LEVEL, typedMetadata, payload // Sidecar Payload Storage (v3.2)
445
666
  ) {
446
- return new Promise((resolve, reject) => {
447
- const req = new hyperspace_pb_1.InsertRequest();
448
- req.setVectorList(HyperspaceClient.toVectorList(vector));
449
- req.setId(id);
450
- if (meta) {
451
- const map = req.getMetadataMap();
452
- for (const k in meta)
453
- map.set(k, meta[k]);
454
- }
455
- if (typedMetadata) {
456
- const map = req.getTypedMetadataMap();
457
- for (const k in typedMetadata)
458
- map.set(k, HyperspaceClient.toProtoMetadataValue(typedMetadata[k]));
667
+ return new Promise(async (resolve, reject) => {
668
+ var _a;
669
+ try {
670
+ const req = new hyperspace_pb_1.InsertRequest();
671
+ let vectorList = HyperspaceClient.toVectorList(vector);
672
+ const metric = this.collectionMetrics[collection] || "l2";
673
+ const context = await this._getEncryptionContext(collection, vectorList.length, metric);
674
+ if (context) {
675
+ const sigma = (_a = this.collectionNoiseSigmas[collection]) !== null && _a !== void 0 ? _a : 0.02;
676
+ if (sigma > 0.0) {
677
+ vectorList = CognitiveMath.injectAnisotropicNoise(vectorList, context.hmacKey, sigma);
678
+ }
679
+ vectorList = this._projectCollectionVector(collection, vectorList, context, metric);
680
+ let rawPayload = null;
681
+ if (payload) {
682
+ rawPayload = Buffer.from(payload);
683
+ }
684
+ if (rawPayload) {
685
+ const encrypted = this.encryptPayload(rawPayload, context.aesKey);
686
+ req.setPayload(new Uint8Array(encrypted));
687
+ }
688
+ if (meta) {
689
+ const map = req.getMetadataMap();
690
+ for (const k in meta) {
691
+ const ek = this.hashMetadataKey(k, context.hmacKey);
692
+ const ev = this.hashMetadataValue(meta[k], context.hmacKey);
693
+ map.set(ek, ev);
694
+ }
695
+ }
696
+ if (typedMetadata) {
697
+ const map = req.getTypedMetadataMap();
698
+ for (const k in typedMetadata) {
699
+ const ek = this.hashMetadataKey(k, context.hmacKey);
700
+ const ev = this.hashMetadataValue(String(typedMetadata[k]), context.hmacKey);
701
+ map.set(ek, HyperspaceClient.toProtoMetadataValue(ev));
702
+ }
703
+ }
704
+ }
705
+ else {
706
+ if (meta) {
707
+ const map = req.getMetadataMap();
708
+ for (const k in meta)
709
+ map.set(k, meta[k]);
710
+ }
711
+ if (typedMetadata) {
712
+ const map = req.getTypedMetadataMap();
713
+ for (const k in typedMetadata)
714
+ map.set(k, HyperspaceClient.toProtoMetadataValue(typedMetadata[k]));
715
+ }
716
+ if (payload) {
717
+ req.setPayload(payload);
718
+ }
719
+ }
720
+ req.setVectorList(vectorList);
721
+ req.setId(id);
722
+ req.setCollection(collection);
723
+ req.setOriginNodeId('');
724
+ req.setLogicalClock(0);
725
+ req.setDurability(durability);
726
+ this.client.insert(req, this.metadata, (err, resp) => {
727
+ if (err)
728
+ return reject(err);
729
+ resolve(resp.getSuccess());
730
+ });
459
731
  }
460
- req.setCollection(collection);
461
- req.setOriginNodeId('');
462
- req.setLogicalClock(0);
463
- req.setDurability(durability);
464
- if (payload) {
465
- req.setPayload(payload);
732
+ catch (e) {
733
+ reject(e);
466
734
  }
467
- this.client.insert(req, this.metadata, (err, resp) => {
468
- if (err)
469
- return reject(err);
470
- resolve(resp.getSuccess());
471
- });
472
735
  });
473
736
  }
474
737
  insertText(id, text, meta, collection = '', durability = hyperspace_pb_1.DurabilityLevel.DEFAULT_LEVEL) {
738
+ if (this.collectionKeys[collection]) {
739
+ if (!this.embedder) {
740
+ return Promise.reject(new Error("An embedder must be configured to use insertText on encrypted collections."));
741
+ }
742
+ return Promise.resolve(this.embedder.encode(text)).then(vector => {
743
+ return this.insert(id, vector, meta, collection, durability, undefined, Buffer.from(text, 'utf-8'));
744
+ });
745
+ }
475
746
  return new Promise((resolve, reject) => {
476
747
  const req = new hyperspace_pb_1.InsertTextRequest();
477
748
  req.setText(text);
@@ -532,85 +803,135 @@ class HyperspaceClient {
532
803
  });
533
804
  }
534
805
  search(vector, topK, collection = '', options) {
535
- return new Promise((resolve, reject) => {
536
- const req = new hyperspace_pb_1.SearchRequest();
537
- req.setVectorList(HyperspaceClient.toVectorList(vector));
538
- req.setTopK(topK);
539
- req.setCollection(collection);
540
- if (options === null || options === void 0 ? void 0 : options.filter) {
541
- const map = req.getFilterMap();
542
- for (const k in options.filter) {
543
- map.set(k, options.filter[k]);
806
+ return new Promise(async (resolve, reject) => {
807
+ var _a;
808
+ try {
809
+ const req = new hyperspace_pb_1.SearchRequest();
810
+ let vectorList = HyperspaceClient.toVectorList(vector);
811
+ req.setTopK(topK);
812
+ req.setCollection(collection);
813
+ const metric = this.collectionMetrics[collection] || "l2";
814
+ const context = await this._getEncryptionContext(collection, vectorList.length, metric);
815
+ let optFilters = options === null || options === void 0 ? void 0 : options.filters;
816
+ let optFilter = options === null || options === void 0 ? void 0 : options.filter;
817
+ let includePayload = options === null || options === void 0 ? void 0 : options.includePayload;
818
+ if (context) {
819
+ const sigma = (_a = this.collectionNoiseSigmas[collection]) !== null && _a !== void 0 ? _a : 0.02;
820
+ if (sigma > 0.0) {
821
+ vectorList = CognitiveMath.injectAnisotropicNoise(vectorList, context.hmacKey, sigma);
822
+ }
823
+ vectorList = this._projectCollectionVector(collection, vectorList, context, metric);
824
+ if (optFilter) {
825
+ const hashedFilter = {};
826
+ for (const k in optFilter) {
827
+ const ek = this.hashMetadataKey(k, context.hmacKey);
828
+ const ev = this.hashMetadataValue(optFilter[k], context.hmacKey);
829
+ hashedFilter[ek] = ev;
830
+ }
831
+ optFilter = hashedFilter;
832
+ }
833
+ if (optFilters) {
834
+ optFilters = this._encryptFilters(optFilters, context);
835
+ }
836
+ includePayload = true;
544
837
  }
545
- }
546
- if ((options === null || options === void 0 ? void 0 : options.restartFactor) !== undefined) {
547
- const map = req.getFilterMap();
548
- map.set('wave_restart_factor', options.restartFactor.toString());
549
- }
550
- if ((options === null || options === void 0 ? void 0 : options.useWave) !== undefined) {
551
- req.setUseWave(options.useWave);
552
- }
553
- if (options === null || options === void 0 ? void 0 : options.filters) {
554
- req.setFiltersList(options.filters.map(f => this.toProtoFilter(f)));
555
- }
556
- if (options === null || options === void 0 ? void 0 : options.hybridQuery)
557
- req.setHybridQuery(options.hybridQuery);
558
- if ((options === null || options === void 0 ? void 0 : options.hybridAlpha) !== undefined)
559
- req.setHybridAlpha(options.hybridAlpha);
560
- if ((options === null || options === void 0 ? void 0 : options.mrlDimension) !== undefined)
561
- req.setMrlDimension(options.mrlDimension);
562
- if ((options === null || options === void 0 ? void 0 : options.useWasserstein) !== undefined)
563
- req.setUseWasserstein(options.useWasserstein);
564
- if ((options === null || options === void 0 ? void 0 : options.includePayload) !== undefined)
565
- req.setIncludePayload(options.includePayload);
566
- if (options === null || options === void 0 ? void 0 : options.componentWeights) {
567
- const map = req.getComponentWeightsMap();
568
- for (const k in options.componentWeights) {
569
- map.set(k, options.componentWeights[k]);
838
+ req.setVectorList(vectorList);
839
+ if (optFilter) {
840
+ const map = req.getFilterMap();
841
+ for (const k in optFilter) {
842
+ map.set(k, optFilter[k]);
843
+ }
570
844
  }
571
- }
572
- if (options === null || options === void 0 ? void 0 : options.bm25) {
573
- const bm25Msg = new hyperspace_pb_1.Bm25Options();
574
- if (options.bm25.method !== undefined)
575
- bm25Msg.setMethod(options.bm25.method);
576
- if (options.bm25.k1 !== undefined)
577
- bm25Msg.setK1(options.bm25.k1);
578
- if (options.bm25.b !== undefined)
579
- bm25Msg.setB(options.bm25.b);
580
- if (options.bm25.delta !== undefined)
581
- bm25Msg.setDelta(options.bm25.delta);
582
- if (options.bm25.language !== undefined)
583
- bm25Msg.setLanguage(options.bm25.language);
584
- if (options.bm25.ngrams !== undefined)
585
- bm25Msg.setNgrams(options.bm25.ngrams);
586
- if (options.bm25.fusionMethod !== undefined)
587
- bm25Msg.setFusionMethod(options.bm25.fusionMethod);
588
- req.setBm25Options(bm25Msg);
589
- }
590
- this.client.search(req, this.metadata, (err, resp) => {
591
- if (err)
592
- return reject(err);
593
- const results = resp.getResultsList().map(r => {
594
- const metaMap = r.getMetadataMap();
595
- const meta = {};
596
- if (metaMap.getLength() > 0) {
597
- metaMap.forEach((entry, key) => {
598
- meta[key] = entry;
599
- });
845
+ if ((options === null || options === void 0 ? void 0 : options.restartFactor) !== undefined) {
846
+ const map = req.getFilterMap();
847
+ map.set('wave_restart_factor', options.restartFactor.toString());
848
+ }
849
+ if ((options === null || options === void 0 ? void 0 : options.useWave) !== undefined) {
850
+ req.setUseWave(options.useWave);
851
+ }
852
+ if (optFilters) {
853
+ req.setFiltersList(optFilters.map(f => this.toProtoFilter(f)));
854
+ }
855
+ if (options === null || options === void 0 ? void 0 : options.hybridQuery)
856
+ req.setHybridQuery(options.hybridQuery);
857
+ if ((options === null || options === void 0 ? void 0 : options.hybridAlpha) !== undefined)
858
+ req.setHybridAlpha(options.hybridAlpha);
859
+ if ((options === null || options === void 0 ? void 0 : options.mrlDimension) !== undefined)
860
+ req.setMrlDimension(options.mrlDimension);
861
+ if ((options === null || options === void 0 ? void 0 : options.useWasserstein) !== undefined)
862
+ req.setUseWasserstein(options.useWasserstein);
863
+ if (includePayload !== undefined)
864
+ req.setIncludePayload(includePayload);
865
+ if (options === null || options === void 0 ? void 0 : options.componentWeights) {
866
+ const map = req.getComponentWeightsMap();
867
+ for (const k in options.componentWeights) {
868
+ map.set(k, options.componentWeights[k]);
600
869
  }
601
- return {
602
- id: r.getId(),
603
- distance: r.getDistance(),
604
- metadata: meta,
605
- typedMetadata: HyperspaceClient.parseTypedMetadata(r.getTypedMetadataMap()),
606
- payload: r.getPayload_asU8()
607
- };
870
+ }
871
+ if (options === null || options === void 0 ? void 0 : options.bm25) {
872
+ const bm25Msg = new hyperspace_pb_1.Bm25Options();
873
+ if (options.bm25.method !== undefined)
874
+ bm25Msg.setMethod(options.bm25.method);
875
+ if (options.bm25.k1 !== undefined)
876
+ bm25Msg.setK1(options.bm25.k1);
877
+ if (options.bm25.b !== undefined)
878
+ bm25Msg.setB(options.bm25.b);
879
+ if (options.bm25.delta !== undefined)
880
+ bm25Msg.setDelta(options.bm25.delta);
881
+ if (options.bm25.language !== undefined)
882
+ bm25Msg.setLanguage(options.bm25.language);
883
+ if (options.bm25.ngrams !== undefined)
884
+ bm25Msg.setNgrams(options.bm25.ngrams);
885
+ if (options.bm25.fusionMethod !== undefined)
886
+ bm25Msg.setFusionMethod(options.bm25.fusionMethod);
887
+ req.setBm25Options(bm25Msg);
888
+ }
889
+ this.client.search(req, this.metadata, (err, resp) => {
890
+ if (err)
891
+ return reject(err);
892
+ const results = resp.getResultsList().map(r => {
893
+ const metaMap = r.getMetadataMap();
894
+ const meta = {};
895
+ if (metaMap.getLength() > 0) {
896
+ metaMap.forEach((entry, key) => {
897
+ meta[key] = entry;
898
+ });
899
+ }
900
+ let payloadBytes = r.getPayload_asU8();
901
+ if (context && payloadBytes && payloadBytes.length > 0) {
902
+ try {
903
+ const decrypted = this.decryptPayload(Buffer.from(payloadBytes), context.aesKey);
904
+ payloadBytes = new Uint8Array(decrypted);
905
+ }
906
+ catch (e) {
907
+ // Decryption error
908
+ }
909
+ }
910
+ return {
911
+ id: r.getId(),
912
+ distance: r.getDistance(),
913
+ metadata: meta,
914
+ typedMetadata: HyperspaceClient.parseTypedMetadata(r.getTypedMetadataMap()),
915
+ payload: payloadBytes
916
+ };
917
+ });
918
+ resolve(results);
608
919
  });
609
- resolve(results);
610
- });
920
+ }
921
+ catch (e) {
922
+ reject(e);
923
+ }
611
924
  });
612
925
  }
613
926
  searchText(text, topK, collection = '', options) {
927
+ if (this.collectionKeys[collection]) {
928
+ if (!this.embedder) {
929
+ return Promise.reject(new Error("An embedder must be configured to use searchText on encrypted collections."));
930
+ }
931
+ return Promise.resolve(this.embedder.encode(text)).then(vector => {
932
+ return this.search(vector, topK, collection, options);
933
+ });
934
+ }
614
935
  return new Promise((resolve, reject) => {
615
936
  const req = new hyperspace_pb_1.SearchTextRequest();
616
937
  req.setText(text);
package/dist/math.d.ts CHANGED
@@ -42,3 +42,7 @@ export declare function koopmanExtrapolate(past: number[], current: number[], st
42
42
  * Pulls the thought towards the context along the geodesic by `resonanceFactor` [0, 1].
43
43
  */
44
44
  export declare function contextResonance(thought: number[], globalContext: number[], resonanceFactor: number, c?: number): number[];
45
+ export declare function generateOrthogonalMatrix(dimension: number, seedBytes: Uint8Array): number[][];
46
+ export declare function generateLorentzMatrix(dimension: number, seedBytes: Uint8Array): number[][];
47
+ export declare function projectVector(v: number[], projectionMatrix: number[][]): number[];
48
+ export declare function injectAnisotropicNoise(vector: number[], noiseSeed: Uint8Array, sigma?: number): number[];
package/dist/math.js CHANGED
@@ -21,6 +21,10 @@ exports.localEntropy = localEntropy;
21
21
  exports.lyapunovConvergence = lyapunovConvergence;
22
22
  exports.koopmanExtrapolate = koopmanExtrapolate;
23
23
  exports.contextResonance = contextResonance;
24
+ exports.generateOrthogonalMatrix = generateOrthogonalMatrix;
25
+ exports.generateLorentzMatrix = generateLorentzMatrix;
26
+ exports.projectVector = projectVector;
27
+ exports.injectAnisotropicNoise = injectAnisotropicNoise;
24
28
  function dot(a, b) {
25
29
  let sum = 0;
26
30
  for (let i = 0; i < a.length; i++)
@@ -232,3 +236,149 @@ function contextResonance(thought, globalContext, resonanceFactor, c = 1.0) {
232
236
  const appliedPull = pullDir.map(v => v * factor);
233
237
  return expMap(thought, appliedPull, c);
234
238
  }
239
+ function generateOrthogonalMatrix(dimension, seedBytes) {
240
+ const crypto = require('crypto');
241
+ const matrix = [];
242
+ let currentHash = crypto.createHash('sha256').update(seedBytes).digest();
243
+ let hashOffset = 0;
244
+ for (let i = 0; i < dimension; i++) {
245
+ const row = [];
246
+ for (let j = 0; j < dimension; j++) {
247
+ if (hashOffset >= 32) {
248
+ currentHash = crypto.createHash('sha256').update(currentHash).digest();
249
+ hashOffset = 0;
250
+ }
251
+ const val = (currentHash.readUInt32LE(hashOffset) / 4294967296.0) * 2.0 - 1.0;
252
+ row.push(val);
253
+ hashOffset += 4;
254
+ }
255
+ matrix.push(row);
256
+ }
257
+ // Gram-Schmidt Orthonormalization with Reorthogonalization (twice is enough)
258
+ for (let i = 0; i < dimension; i++) {
259
+ let v = matrix[i];
260
+ for (let j = 0; j < i; j++) {
261
+ const u = matrix[j];
262
+ let uDotV = dot(u, v);
263
+ v = v.map((vi, k) => vi - uDotV * u[k]);
264
+ // Re-project to eliminate floating-point leakage
265
+ uDotV = dot(u, v);
266
+ v = v.map((vi, k) => vi - uDotV * u[k]);
267
+ }
268
+ const vNorm = norm(v);
269
+ if (vNorm > 1e-15) {
270
+ matrix[i] = v.map(vi => vi / vNorm);
271
+ }
272
+ else {
273
+ matrix[i] = new Array(dimension).fill(0);
274
+ matrix[i][i] = 1.0;
275
+ }
276
+ }
277
+ return matrix;
278
+ }
279
+ function generateLorentzMatrix(dimension, seedBytes) {
280
+ const d = dimension - 1;
281
+ const crypto = require('crypto');
282
+ const rSeed = crypto.createHash('sha256').update(seedBytes).update("spatial").digest();
283
+ const R = generateOrthogonalMatrix(d, rSeed);
284
+ const bSeed = crypto.createHash('sha256').update(seedBytes).update("boost").digest();
285
+ let beta = [];
286
+ let currentHash = bSeed;
287
+ let hashOffset = 0;
288
+ for (let i = 0; i < d; i++) {
289
+ if (hashOffset >= 32) {
290
+ currentHash = crypto.createHash('sha256').update(currentHash).digest();
291
+ hashOffset = 0;
292
+ }
293
+ const val = (currentHash.readUInt32LE(hashOffset) / 4294967296.0) * 2.0 - 1.0;
294
+ beta.push(val);
295
+ hashOffset += 4;
296
+ }
297
+ const betaNorm = norm(beta);
298
+ if (betaNorm > 1e-15) {
299
+ beta = beta.map(v => (v / betaNorm) * 0.1);
300
+ }
301
+ else {
302
+ beta = new Array(d).fill(0);
303
+ beta[0] = 0.1;
304
+ }
305
+ const betaSq = dot(beta, beta);
306
+ const gamma = 1.0 / Math.sqrt(1.0 - betaSq);
307
+ const Lambda_B = [];
308
+ for (let i = 0; i < dimension; i++) {
309
+ Lambda_B.push(new Array(dimension).fill(0));
310
+ }
311
+ Lambda_B[0][0] = gamma;
312
+ for (let j = 1; j < dimension; j++) {
313
+ Lambda_B[0][j] = -gamma * beta[j - 1];
314
+ Lambda_B[j][0] = -gamma * beta[j - 1];
315
+ }
316
+ const factor = (gamma - 1.0) / betaSq;
317
+ for (let i = 1; i < dimension; i++) {
318
+ for (let j = 1; j < dimension; j++) {
319
+ const delta = i === j ? 1.0 : 0.0;
320
+ Lambda_B[i][j] = delta + factor * beta[i - 1] * beta[j - 1];
321
+ }
322
+ }
323
+ const Lambda_R = [];
324
+ for (let i = 0; i < dimension; i++) {
325
+ Lambda_R.push(new Array(dimension).fill(0));
326
+ }
327
+ Lambda_R[0][0] = 1.0;
328
+ for (let i = 1; i < dimension; i++) {
329
+ for (let j = 1; j < dimension; j++) {
330
+ Lambda_R[i][j] = R[i - 1][j - 1];
331
+ }
332
+ }
333
+ const Lambda = [];
334
+ for (let i = 0; i < dimension; i++) {
335
+ const row = [];
336
+ for (let j = 0; j < dimension; j++) {
337
+ let sum = 0;
338
+ for (let k = 0; k < dimension; k++) {
339
+ sum += Lambda_R[i][k] * Lambda_B[k][j];
340
+ }
341
+ row.push(sum);
342
+ }
343
+ Lambda.push(row);
344
+ }
345
+ return Lambda;
346
+ }
347
+ function projectVector(v, projectionMatrix) {
348
+ const dimension = v.length;
349
+ const projected = new Array(dimension).fill(0);
350
+ for (let j = 0; j < dimension; j++) {
351
+ let sum = 0;
352
+ for (let i = 0; i < dimension; i++) {
353
+ sum += v[i] * projectionMatrix[i][j];
354
+ }
355
+ projected[j] = sum;
356
+ }
357
+ return projected;
358
+ }
359
+ function injectAnisotropicNoise(vector, noiseSeed, sigma = 0.02) {
360
+ if (sigma <= 0.0)
361
+ return [...vector];
362
+ const crypto = require('crypto');
363
+ const vecBytes = Buffer.from(vector.toString());
364
+ const hash = crypto.createHash('sha256').update(noiseSeed).update(vecBytes).digest();
365
+ let currentHash = hash;
366
+ const noise = [];
367
+ for (let i = 0; i < vector.length; i++) {
368
+ if (currentHash.length < 16) {
369
+ currentHash = crypto.createHash('sha256').update(currentHash).digest();
370
+ }
371
+ const u1 = Math.max(1e-15, (currentHash.readUInt32LE(0) + 1) / 4294967296);
372
+ const u2 = Math.max(1e-15, (currentHash.readUInt32LE(4) + 1) / 4294967296);
373
+ const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
374
+ noise.push(z0 * sigma);
375
+ currentHash = currentHash.subarray(8);
376
+ }
377
+ const noisy = vector.map((v, i) => v + noise[i]);
378
+ const origNorm = norm(vector);
379
+ const noisyNorm = norm(noisy);
380
+ if (noisyNorm > 1e-15) {
381
+ return noisy.map(v => (v / noisyNorm) * origNorm);
382
+ }
383
+ return noisy;
384
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperspace-sdk-ts",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "Official TypeScript SDK for HyperspaceDB gRPC API",
5
5
  "main": "dist/client.js",
6
6
  "types": "dist/client.d.ts",