holosphere 2.0.0-alpha11 → 2.0.0-alpha12

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.
Files changed (60) hide show
  1. package/dist/{2019-D2OG2idw.js → 2019-DQdDE6DG.js} +2 -2
  2. package/dist/{2019-D2OG2idw.js.map → 2019-DQdDE6DG.js.map} +1 -1
  3. package/dist/{2019-EION3wKo.cjs → 2019-Ew-DTDlI.cjs} +2 -2
  4. package/dist/{2019-EION3wKo.cjs.map → 2019-Ew-DTDlI.cjs.map} +1 -1
  5. package/dist/{browser-BSniCNqO.js → browser-CckFyRI9.js} +2 -2
  6. package/dist/{browser-BSniCNqO.js.map → browser-CckFyRI9.js.map} +1 -1
  7. package/dist/browser-D2qtVhH5.cjs +2 -0
  8. package/dist/{browser-Cq59Ij19.cjs.map → browser-D2qtVhH5.cjs.map} +1 -1
  9. package/dist/cjs/holosphere.cjs +1 -1
  10. package/dist/esm/holosphere.js +67 -50
  11. package/dist/index-9sqetkAn.cjs +29 -0
  12. package/dist/index-9sqetkAn.cjs.map +1 -0
  13. package/dist/index-C0ITDyFo.cjs +13 -0
  14. package/dist/index-C0ITDyFo.cjs.map +1 -0
  15. package/dist/{index-Bl6rM1NW.js → index-DrYM1LOY.js} +2 -2
  16. package/dist/{index-Bl6rM1NW.js.map → index-DrYM1LOY.js.map} +1 -1
  17. package/dist/{index-D-jZhliX.js → index-TDDyakLc.js} +19827 -720
  18. package/dist/index-TDDyakLc.js.map +1 -0
  19. package/dist/{index-Bwg3OzRM.cjs → index-kyf1sjaC.cjs} +3 -3
  20. package/dist/{index-Bwg3OzRM.cjs.map → index-kyf1sjaC.cjs.map} +1 -1
  21. package/dist/index-lbSQUoRz.js +11663 -0
  22. package/dist/index-lbSQUoRz.js.map +1 -0
  23. package/dist/{indexeddb-storage-5eiUNsHC.js → indexeddb-storage-CXhjqwhA.js} +2 -2
  24. package/dist/{indexeddb-storage-5eiUNsHC.js.map → indexeddb-storage-CXhjqwhA.js.map} +1 -1
  25. package/dist/{indexeddb-storage-FNFUVvTJ.cjs → indexeddb-storage-DFESDYIj.cjs} +2 -2
  26. package/dist/{indexeddb-storage-FNFUVvTJ.cjs.map → indexeddb-storage-DFESDYIj.cjs.map} +1 -1
  27. package/dist/{memory-storage-DMt36uZO.cjs → memory-storage-D1tc1bjk.cjs} +2 -2
  28. package/dist/{memory-storage-DMt36uZO.cjs.map → memory-storage-D1tc1bjk.cjs.map} +1 -1
  29. package/dist/{memory-storage-CI-gfmuG.js → memory-storage-DkewsdcM.js} +2 -2
  30. package/dist/{memory-storage-CI-gfmuG.js.map → memory-storage-DkewsdcM.js.map} +1 -1
  31. package/docs/FEDERATION.md +474 -0
  32. package/package.json +3 -1
  33. package/src/crypto/nostr-utils.js +7 -0
  34. package/src/crypto/secp256k1.js +46 -29
  35. package/src/federation/capabilities.js +162 -0
  36. package/src/federation/card-storage.js +304 -0
  37. package/src/federation/handshake.js +204 -7
  38. package/src/federation/hologram.js +167 -58
  39. package/src/federation/holon-registry.js +166 -0
  40. package/src/federation/index.js +68 -0
  41. package/src/federation/registry.js +110 -2
  42. package/src/federation/request-card.js +373 -0
  43. package/src/hierarchical/upcast.js +19 -3
  44. package/src/index.js +162 -76
  45. package/src/lib/federation-methods.js +527 -5
  46. package/src/storage/nostr-async.js +2 -2
  47. package/src/storage/nostr-client.js +370 -155
  48. package/src/storage/nostr-wrapper.js +1 -1
  49. package/dist/_commonjsHelpers-C37NGDzP.cjs +0 -2
  50. package/dist/_commonjsHelpers-C37NGDzP.cjs.map +0 -1
  51. package/dist/_commonjsHelpers-CUmg6egw.js +0 -7
  52. package/dist/_commonjsHelpers-CUmg6egw.js.map +0 -1
  53. package/dist/browser-Cq59Ij19.cjs +0 -2
  54. package/dist/index-D-jZhliX.js.map +0 -1
  55. package/dist/index-Dc6Z8Aob.cjs +0 -18
  56. package/dist/index-Dc6Z8Aob.cjs.map +0 -1
  57. package/dist/secp256k1-CEwJNcfV.js +0 -1890
  58. package/dist/secp256k1-CEwJNcfV.js.map +0 -1
  59. package/dist/secp256k1-CiEONUnj.cjs +0 -12
  60. package/dist/secp256k1-CiEONUnj.cjs.map +0 -1
package/src/index.js CHANGED
@@ -21,6 +21,11 @@ import * as schema from './schema/validator.js';
21
21
  import { ValidationError } from './schema/validator.js';
22
22
  import * as federation from './federation/hologram.js';
23
23
  import * as handshake from './federation/handshake.js';
24
+ import * as holonRegistry from './federation/holon-registry.js';
25
+ import * as registry from './federation/registry.js';
26
+ import * as capabilities from './federation/capabilities.js';
27
+ import * as requestCard from './federation/request-card.js';
28
+ import * as cardStorage from './federation/card-storage.js';
24
29
  import * as crypto from './crypto/secp256k1.js';
25
30
  import * as nostrUtils from './crypto/nostr-utils.js';
26
31
  import * as social from './content/social-protocols.js';
@@ -254,6 +259,51 @@ class HoloSphereBase extends HoloSphereCore {
254
259
  return spatial.isValidH3(holonId);
255
260
  }
256
261
 
262
+ // === Holon Registry Operations ===
263
+
264
+ /**
265
+ * Register a holon with a public key.
266
+ * Maps a holonId (H3, chatId, concept) to a keypair owner.
267
+ *
268
+ * @param {string} holonId - Holon identifier to register
269
+ * @param {string} publicKey - 64-char hex public key (owner)
270
+ * @param {Object} [options={}] - Registration options
271
+ * @param {string} [options.alias] - Human-readable name
272
+ * @returns {Promise<boolean>} Success indicator
273
+ */
274
+ async registerHolon(holonId, publicKey, options = {}) {
275
+ return holonRegistry.registerHolon(this.client, this.config.appName, holonId, publicKey, options);
276
+ }
277
+
278
+ /**
279
+ * Look up a holon's registration.
280
+ *
281
+ * @param {string} holonId - Holon identifier
282
+ * @returns {Promise<Object|null>} Registry entry { holonId, publicKey, alias } or null
283
+ */
284
+ async lookupHolon(holonId) {
285
+ return holonRegistry.lookupHolon(this.client, this.config.appName, holonId);
286
+ }
287
+
288
+ /**
289
+ * Unregister a holon.
290
+ *
291
+ * @param {string} holonId - Holon identifier
292
+ * @returns {Promise<boolean>} Success indicator
293
+ */
294
+ async unregisterHolon(holonId) {
295
+ return holonRegistry.unregisterHolon(this.client, this.config.appName, holonId);
296
+ }
297
+
298
+ /**
299
+ * List all registered holons.
300
+ *
301
+ * @returns {Promise<Object[]>} Array of registry entries
302
+ */
303
+ async getRegisteredHolons() {
304
+ return holonRegistry.listRegisteredHolons(this.client, this.config.appName);
305
+ }
306
+
257
307
  // === Data Operations ===
258
308
 
259
309
  /**
@@ -486,6 +536,31 @@ class HoloSphereBase extends HoloSphereCore {
486
536
  return true;
487
537
  }
488
538
 
539
+ /**
540
+ * Resolve holonId to public key.
541
+ * If holonId is already a 64-char hex pubkey, returns it directly.
542
+ * Otherwise, looks up the holon registry.
543
+ *
544
+ * @private
545
+ * @param {string} holonId - Holon identifier
546
+ * @returns {Promise<string|null>} Public key or null if not found
547
+ */
548
+ async _resolveHolonToPubkey(holonId) {
549
+ return holonRegistry.resolveHolonToPubkey(this.client, this.config.appName, holonId);
550
+ }
551
+
552
+ /**
553
+ * Check if we have a stored capability for a given author and scope.
554
+ *
555
+ * @private
556
+ * @param {string} authorPubKey - Author's public key
557
+ * @param {Object} scope - Scope to check (holonId, lensName, dataId)
558
+ * @returns {Promise<Object|null>} Capability entry or null
559
+ */
560
+ async _getCapabilityForAuthor(authorPubKey, scope) {
561
+ return registry.getCapabilityForAuthor(this.client, this.config.appName, authorPubKey, scope);
562
+ }
563
+
489
564
  /**
490
565
  * Recursively resolves holograms (references) to their source data.
491
566
  * Handles circular reference detection and local overrides.
@@ -613,12 +688,54 @@ class HoloSphereBase extends HoloSphereCore {
613
688
  throw new ValidationError('ValidationError: lensName must be a non-empty string');
614
689
  }
615
690
 
691
+ // Resolve holonId to public key (if registered)
692
+ const targetPubkey = await this._resolveHolonToPubkey(holonId);
693
+
694
+ // Determine if reading another author's data
695
+ // If holonId can't be resolved, treat as own data (backwards compatible for H3 holons)
696
+ const isOtherAuthor = targetPubkey && targetPubkey !== this.client.publicKey;
697
+ let readOptions = {};
698
+
699
+ // Explicit capability token takes precedence
616
700
  const capToken = options.capabilityToken || options.capability;
617
701
  if (capToken) {
618
702
  const authorized = await this.verifyCapability(capToken, 'read', { holonId, lensName, dataId });
619
703
  if (!authorized) {
620
704
  throw new AuthorizationError('AuthorizationError: Invalid capability token for read operation', 'read');
621
705
  }
706
+ // Use target author for reading
707
+ if (isOtherAuthor) {
708
+ readOptions.authors = [targetPubkey];
709
+ }
710
+ } else if (isOtherAuthor) {
711
+ // Auto-check capability for other author's data
712
+ const capability = await this._getCapabilityForAuthor(targetPubkey, { holonId, lensName, dataId });
713
+ if (!capability) {
714
+ this._log('DEBUG', 'No capability for author', { holonId, targetPubkey });
715
+ return dataId ? null : [];
716
+ }
717
+ readOptions.authors = [targetPubkey];
718
+ }
719
+
720
+ // Include federated authors to see holograms written by federation partners
721
+ // This enables visibility of holograms created during federation
722
+ if (!isOtherAuthor) {
723
+ try {
724
+ const federatedAuthors = await registry.getFederatedAuthorsForScope(
725
+ this.client,
726
+ this.config.appName,
727
+ { holonId, lensName, dataId },
728
+ 'read'
729
+ );
730
+ if (federatedAuthors.length > 0) {
731
+ const partnerPubkeys = federatedAuthors.map(f => f.pubKey);
732
+ // Include self + all federated partners
733
+ readOptions.authors = [this.client.publicKey, ...partnerPubkeys];
734
+ this._log('DEBUG', 'Including federated authors', { count: partnerPubkeys.length });
735
+ }
736
+ } catch (err) {
737
+ this._log('WARN', 'Failed to get federated authors', { error: err.message });
738
+ }
622
739
  }
623
740
 
624
741
  const startTime = Date.now();
@@ -636,8 +753,8 @@ class HoloSphereBase extends HoloSphereCore {
636
753
  });
637
754
  result = null;
638
755
  } else {
639
- // Check write cache for optimistic reads
640
- const cached = this._writeCache.get(path);
756
+ // Check write cache for optimistic reads (only for own data)
757
+ const cached = !isOtherAuthor ? this._writeCache.get(path) : null;
641
758
  if (cached) {
642
759
  const cacheAge = Date.now() - cached.timestamp;
643
760
  this._log('DEBUG', '⚡ CACHE HIT: Write cache', {
@@ -648,8 +765,8 @@ class HoloSphereBase extends HoloSphereCore {
648
765
  });
649
766
  result = cached.data;
650
767
  } else {
651
- this._log('DEBUG', '📖 CACHE MISS: Reading from storage', { path });
652
- result = await storage.read(this.client, path);
768
+ this._log('DEBUG', '📖 CACHE MISS: Reading from storage', { path, authors: readOptions.authors });
769
+ result = await storage.read(this.client, path, readOptions);
653
770
  this._log('DEBUG', '💾 STORAGE READ', {
654
771
  path,
655
772
  source: 'storage',
@@ -660,11 +777,11 @@ class HoloSphereBase extends HoloSphereCore {
660
777
  }
661
778
  } else {
662
779
  const path = storage.buildPath(this.config.appName, holonId, lensName);
663
- this._log('DEBUG', 'readAll', { holonId, lensName, path });
780
+ this._log('DEBUG', 'readAll', { holonId, lensName, path, authors: readOptions.authors });
664
781
 
665
782
  // For readAll, merge cached writes with storage results and filter deleted
666
- const storageResult = await storage.readAll(this.client, path);
667
- result = this._mergeWithWriteCache(storageResult, path);
783
+ const storageResult = await storage.readAll(this.client, path, readOptions);
784
+ result = isOtherAuthor ? storageResult : this._mergeWithWriteCache(storageResult, path);
668
785
  this._log('DEBUG', 'readAll result', { count: Array.isArray(result) ? result.length : 0 });
669
786
  }
670
787
 
@@ -1179,56 +1296,11 @@ class HoloSphereBase extends HoloSphereCore {
1179
1296
  }
1180
1297
 
1181
1298
  // === Federation Operations ===
1182
-
1183
- /**
1184
- * Sets up federation between two holons for a specific lens.
1185
- * Federation enables data sharing and synchronization between holons.
1186
- *
1187
- * @param {string} sourceHolon - Source holon H3 cell ID
1188
- * @param {string} targetHolon - Target holon H3 cell ID
1189
- * @param {string} lensName - Name of the lens to federate
1190
- * @param {Object} [options={}] - Federation options
1191
- * @param {string} [options.direction='outbound'] - Direction: 'inbound', 'outbound', or 'bidirectional'
1192
- * @param {string} [options.mode='reference'] - Mode: 'reference' (hologram) or 'copy'
1193
- * @param {Function} [options.filter] - Filter function to select which data to federate
1194
- * @returns {Promise<boolean>} True if federation was set up successfully
1195
- * @throws {Error} If trying to federate a holon with itself or invalid direction
1196
- */
1197
- async federate(sourceHolon, targetHolon, lensName, options = {}) {
1198
- const { direction = 'outbound', mode = 'reference', filter = null } = options;
1199
-
1200
- // Validation
1201
- if (sourceHolon === targetHolon) {
1202
- throw new Error('Cannot federate a holon with itself');
1203
- }
1204
- if (!['inbound', 'outbound', 'bidirectional'].includes(direction)) {
1205
- throw new Error(`Invalid direction: ${direction}. Must be 'inbound', 'outbound', or 'bidirectional'`);
1206
- }
1207
-
1208
- // Store federation config
1209
- await federation.setupFederation(
1210
- this.client,
1211
- this.config.appName,
1212
- sourceHolon,
1213
- targetHolon,
1214
- lensName,
1215
- options
1216
- );
1217
-
1218
- // Actually propagate existing data based on direction
1219
- if (direction === 'outbound' || direction === 'bidirectional') {
1220
- await this._propagateExistingData(sourceHolon, targetHolon, lensName, { mode, filter });
1221
- }
1222
- if (direction === 'inbound' || direction === 'bidirectional') {
1223
- await this._propagateExistingData(targetHolon, sourceHolon, lensName, { mode, filter });
1224
- }
1225
-
1226
- this._metrics.federations = (this._metrics.federations || 0) + 1;
1227
- return true;
1228
- }
1299
+ // Note: federate() and unfederate() methods are provided by withFederationMethods mixin
1229
1300
 
1230
1301
  /**
1231
1302
  * Propagates existing data from one holon to another.
1303
+ * UNIFIED MODEL: Passes sourceAuthorPubKey for capability-based federation.
1232
1304
  *
1233
1305
  * @private
1234
1306
  * @param {string} fromHolon - Source holon H3 cell ID
@@ -1237,10 +1309,11 @@ class HoloSphereBase extends HoloSphereCore {
1237
1309
  * @param {Object} [options={}] - Propagation options
1238
1310
  * @param {string} [options.mode='reference'] - Mode: 'reference' or 'copy'
1239
1311
  * @param {Function} [options.filter] - Filter function to select data
1312
+ * @param {string} [options.sourceAuthorPubKey] - Source author's public key (defaults to client.publicKey)
1240
1313
  * @returns {Promise<void>}
1241
1314
  */
1242
1315
  async _propagateExistingData(fromHolon, toHolon, lensName, options = {}) {
1243
- const { mode = 'reference', filter = null } = options;
1316
+ const { mode = 'reference', filter = null, sourceAuthorPubKey } = options;
1244
1317
  const existingData = await this.read(fromHolon, lensName, null, { resolveHolograms: false });
1245
1318
  if (!existingData) return;
1246
1319
 
@@ -1250,6 +1323,7 @@ class HoloSphereBase extends HoloSphereCore {
1250
1323
  if (item.hologram === true) continue;
1251
1324
  // Apply filter if provided
1252
1325
  if (filter && !filter(item)) continue;
1326
+ // UNIFIED MODEL: Pass sourceAuthorPubKey for capability-based federation
1253
1327
  await federation.propagateData(
1254
1328
  this.client,
1255
1329
  this.config.appName,
@@ -1257,7 +1331,10 @@ class HoloSphereBase extends HoloSphereCore {
1257
1331
  fromHolon,
1258
1332
  toHolon,
1259
1333
  lensName,
1260
- mode
1334
+ mode,
1335
+ {
1336
+ sourceAuthorPubKey: sourceAuthorPubKey || this.client.publicKey,
1337
+ }
1261
1338
  );
1262
1339
  }
1263
1340
  }
@@ -1282,25 +1359,6 @@ class HoloSphereBase extends HoloSphereCore {
1282
1359
  return this._resolveHolograms(data);
1283
1360
  }
1284
1361
 
1285
- /**
1286
- * Removes federation between two holons for a specific lens.
1287
- *
1288
- * @param {string} sourceHolon - Source holon H3 cell ID
1289
- * @param {string} targetHolon - Target holon H3 cell ID
1290
- * @param {string} lensName - Name of the lens
1291
- * @returns {Promise<boolean>} Always returns true (idempotent operation)
1292
- */
1293
- async unfederate(sourceHolon, targetHolon, lensName) {
1294
- // Remove federation config for this relationship - idempotent
1295
- const configPath = storage.buildPath(this.config.appName, sourceHolon, lensName, '_federation');
1296
- try {
1297
- await storage.deleteData(this.client, configPath);
1298
- } catch (e) {
1299
- // Ignore errors - already unfederated or doesn't exist
1300
- }
1301
- return true;
1302
- }
1303
-
1304
1362
  /**
1305
1363
  * Updates local override values on a hologram.
1306
1364
  *
@@ -1412,6 +1470,7 @@ class HoloSphereBase extends HoloSphereCore {
1412
1470
  async propagateData(data, sourceHolon, targetHolon, lensName, options = {}) {
1413
1471
  // Extract mode from options, default to 'reference' for hologram creation
1414
1472
  const mode = options.mode || 'reference';
1473
+ // UNIFIED MODEL: Pass sourceAuthorPubKey for capability-based federation
1415
1474
  return federation.propagateData(
1416
1475
  this.client,
1417
1476
  this.config.appName,
@@ -1419,7 +1478,11 @@ class HoloSphereBase extends HoloSphereCore {
1419
1478
  sourceHolon,
1420
1479
  targetHolon,
1421
1480
  lensName,
1422
- mode
1481
+ mode,
1482
+ {
1483
+ sourceAuthorPubKey: options.sourceAuthorPubKey || this.client.publicKey,
1484
+ capability: options.capability,
1485
+ }
1423
1486
  );
1424
1487
  }
1425
1488
 
@@ -2267,10 +2330,33 @@ export {
2267
2330
  // Re-export types and utilities
2268
2331
  export { spatial, storage, schema, federation, handshake, crypto, nostrUtils, social, subscriptions, hierarchical };
2269
2332
 
2333
+ // Re-export federation submodules
2334
+ export { capabilities, requestCard, cardStorage, holonRegistry, registry };
2335
+
2270
2336
  // Re-export specific utilities used in tests
2271
2337
  export { matchScope } from './crypto/secp256k1.js';
2272
2338
  export { createHologram } from './federation/hologram.js';
2273
2339
 
2340
+ // Re-export federation card functions
2341
+ export {
2342
+ createFederationCard,
2343
+ getVisibleLenses,
2344
+ getLensConfigForHandshake,
2345
+ toggleLens,
2346
+ toggleCardExpansion,
2347
+ dismissCard,
2348
+ } from './federation/request-card.js';
2349
+
2350
+ // Re-export card storage functions
2351
+ export {
2352
+ saveCard,
2353
+ getCard,
2354
+ getDisplayableCards,
2355
+ dismissRequest,
2356
+ markResponseProcessed,
2357
+ isResponseProcessed,
2358
+ } from './federation/card-storage.js';
2359
+
2274
2360
  // Export AI factory function
2275
2361
  export { createAIServices } from './ai/index.js';
2276
2362