holosphere 2.0.0-alpha21 → 2.0.0-alpha23
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 +1 -2
- package/dist/cjs/holosphere.cjs +1 -1
- package/dist/esm/holosphere.js +61 -58
- package/dist/{index-B6-8KAQm.js → index-BEkCLOwI.js} +2 -2
- package/dist/{index-B6-8KAQm.js.map → index-BEkCLOwI.js.map} +1 -1
- package/dist/{index-D2WstuZJ.js → index-BEvX6DxG.js} +2 -2
- package/dist/{index-D2WstuZJ.js.map → index-BEvX6DxG.js.map} +1 -1
- package/dist/{index--QsHG_gD.cjs → index-BGTOiJ2Y.cjs} +2 -2
- package/dist/{index--QsHG_gD.cjs.map → index-BGTOiJ2Y.cjs.map} +1 -1
- package/dist/{index-COpLk9gL.cjs → index-BH1woZXL.cjs} +2 -2
- package/dist/{index-COpLk9gL.cjs.map → index-BH1woZXL.cjs.map} +1 -1
- package/dist/{index-BHptWysv.js → index-Cvxov2jv.js} +2970 -7753
- package/dist/index-Cvxov2jv.js.map +1 -0
- package/dist/index-vTKI_BAX.cjs +29 -0
- package/dist/index-vTKI_BAX.cjs.map +1 -0
- package/dist/{indexeddb-storage-wKG4mICM.cjs → indexeddb-storage-BmnCNnSg.cjs} +2 -2
- package/dist/{indexeddb-storage-wKG4mICM.cjs.map → indexeddb-storage-BmnCNnSg.cjs.map} +1 -1
- package/dist/{indexeddb-storage-kQ53UHEE.js → indexeddb-storage-MIFisaPy.js} +2 -2
- package/dist/{indexeddb-storage-kQ53UHEE.js.map → indexeddb-storage-MIFisaPy.js.map} +1 -1
- package/dist/{memory-storage-CGC8xM2G.cjs → memory-storage-BJjK3F4r.cjs} +2 -2
- package/dist/{memory-storage-CGC8xM2G.cjs.map → memory-storage-BJjK3F4r.cjs.map} +1 -1
- package/dist/{memory-storage-DnXCSbBl.js → memory-storage-DhHXdKQ-.js} +2 -2
- package/dist/{memory-storage-DnXCSbBl.js.map → memory-storage-DhHXdKQ-.js.map} +1 -1
- package/examples/demo.html +2 -29
- package/package.json +3 -8
- package/src/content/social-protocols.js +3 -59
- package/src/core/holosphere.js +16 -554
- package/src/crypto/nostr-utils.js +98 -1
- package/src/crypto/secp256k1.js +4 -393
- package/src/federation/discovery.js +7 -75
- package/src/federation/handshake.js +69 -202
- package/src/federation/hologram.js +222 -298
- package/src/federation/index.js +2 -9
- package/src/federation/registry.js +67 -1257
- package/src/federation/request-card.js +21 -35
- package/src/hierarchical/upcast.js +4 -9
- package/src/index.js +145 -296
- package/src/lib/federation-methods.js +370 -909
- package/src/storage/global-tables.js +1 -1
- package/src/storage/nostr-wrapper.js +9 -5
- package/src/subscriptions/manager.js +1 -1
- package/types/index.d.ts +145 -37
- package/bin/holosphere-activitypub.js +0 -158
- package/dist/2019-BzVkRcax.js +0 -6680
- package/dist/2019-BzVkRcax.js.map +0 -1
- package/dist/2019-C1hPR_Os.cjs +0 -8
- package/dist/2019-C1hPR_Os.cjs.map +0 -1
- package/dist/browser-BcmACE3G.js +0 -3058
- package/dist/browser-BcmACE3G.js.map +0 -1
- package/dist/browser-DaqYUTcG.cjs +0 -2
- package/dist/browser-DaqYUTcG.cjs.map +0 -1
- package/dist/index-BHptWysv.js.map +0 -1
- package/dist/index-CDlhzxT2.cjs +0 -29
- package/dist/index-CDlhzxT2.cjs.map +0 -1
- package/src/federation/capabilities.js +0 -46
- package/src/storage/backend-factory.js +0 -130
- package/src/storage/backend-interface.js +0 -161
- package/src/storage/backends/activitypub/server.js +0 -675
- package/src/storage/backends/activitypub-backend.js +0 -295
- package/src/storage/backends/gundb-backend.js +0 -875
- package/src/storage/backends/nostr-backend.js +0 -251
- package/src/storage/gun-async.js +0 -341
- package/src/storage/gun-auth.js +0 -373
- package/src/storage/gun-federation.js +0 -785
- package/src/storage/gun-references.js +0 -209
- package/src/storage/gun-schema.js +0 -306
- package/src/storage/gun-wrapper.js +0 -642
- package/src/storage/migration.js +0 -351
- package/src/storage/unified-storage.js +0 -161
package/src/index.js
CHANGED
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
* @license MIT
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
/** @constant {string} version - Package version (keep in sync with package.json) */
|
|
15
|
+
export const version = '2.0.0-alpha23';
|
|
16
|
+
|
|
14
17
|
import { HoloSphere as HoloSphereCore } from './core/holosphere.js';
|
|
15
18
|
import * as spatial from './spatial/h3-operations.js';
|
|
16
|
-
import * as storage from './storage/
|
|
17
|
-
import * as nostrStorage from './storage/nostr-wrapper.js';
|
|
19
|
+
import * as storage from './storage/nostr-wrapper.js';
|
|
18
20
|
import * as nostrAsync from './storage/nostr-async.js';
|
|
19
21
|
import * as globalTables from './storage/global-tables.js';
|
|
20
22
|
import * as schema from './schema/validator.js';
|
|
@@ -23,7 +25,6 @@ import * as federation from './federation/hologram.js';
|
|
|
23
25
|
import * as handshake from './federation/handshake.js';
|
|
24
26
|
import * as holonRegistry from './federation/holon-registry.js';
|
|
25
27
|
import * as registry from './federation/registry.js';
|
|
26
|
-
import * as capabilities from './federation/capabilities.js';
|
|
27
28
|
import * as requestCard from './federation/request-card.js';
|
|
28
29
|
import * as cardStorage from './federation/card-storage.js';
|
|
29
30
|
import * as crypto from './crypto/secp256k1.js';
|
|
@@ -78,7 +79,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
78
79
|
*
|
|
79
80
|
* @param {Object} config - Configuration options
|
|
80
81
|
* @param {string} config.appName - Application namespace for data isolation
|
|
81
|
-
* @param {string} [config.backend='nostr'] - Storage backend type ('nostr', 'gundb', 'activitypub')
|
|
82
82
|
* @param {string[]} [config.relays] - Nostr relay URLs for distributed storage
|
|
83
83
|
* @param {string} [config.openaiKey] - OpenAI API key for AI services
|
|
84
84
|
* @param {Object} [config.aiOptions] - AI service configuration
|
|
@@ -177,6 +177,25 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
177
177
|
return isNaN(parsed) ? undefined : parsed;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Extracts holon ID from a hologram soul path.
|
|
182
|
+
* Soul paths follow the format: "AppName/Holons/<holonId>/lensName/dataId"
|
|
183
|
+
*
|
|
184
|
+
* @private
|
|
185
|
+
* @param {string|undefined} soul - The soul path from a hologram
|
|
186
|
+
* @returns {string|null} The extracted holon ID or null if not found
|
|
187
|
+
*/
|
|
188
|
+
_extractHolonIdFromSoul(soul) {
|
|
189
|
+
if (!soul || typeof soul !== 'string') return null;
|
|
190
|
+
// Soul format: "AppName/Holons/<holonId>/lensName/dataId" or "Holons/<holonId>/..."
|
|
191
|
+
const parts = soul.split('/');
|
|
192
|
+
const holonsIndex = parts.indexOf('Holons');
|
|
193
|
+
if (holonsIndex !== -1 && parts.length > holonsIndex + 1) {
|
|
194
|
+
return parts[holonsIndex + 1];
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
180
199
|
/**
|
|
181
200
|
* Initializes AI services with the provided API key.
|
|
182
201
|
*
|
|
@@ -335,17 +354,13 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
335
354
|
* @param {string} lensName - Name of the lens (data category)
|
|
336
355
|
* @param {Object} data - Data object to write (must have or will be assigned an id)
|
|
337
356
|
* @param {Object} [options={}] - Write options
|
|
338
|
-
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
339
357
|
* @param {boolean} [options.validate=true] - Whether to validate against schema
|
|
340
358
|
* @param {boolean} [options.strict] - Override schema strict mode
|
|
341
|
-
* @param {boolean} [options.autoPropagate=true] - Whether to propagate to federated holons
|
|
342
|
-
* @param {Object} [options.propagationOptions] - Options for propagation
|
|
343
359
|
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
344
360
|
* @param {string} [options.signingKey] - Private key to sign with (hex format). If not provided, uses holosphere's default key.
|
|
345
361
|
* @param {boolean} [options.encrypt] - Whether to encrypt this data. Defaults to encryptByDefault config.
|
|
346
362
|
* @returns {Promise<boolean>} True if write succeeded (or queued for optimistic writes)
|
|
347
363
|
* @throws {ValidationError} If holonId, lensName, or data is invalid
|
|
348
|
-
* @throws {AuthorizationError} If capability token is invalid
|
|
349
364
|
*/
|
|
350
365
|
async write(holonId, lensName, data, options = {}) {
|
|
351
366
|
// Auto-convert to strings for convenience
|
|
@@ -362,6 +377,19 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
362
377
|
throw new ValidationError('ValidationError: data must be an object');
|
|
363
378
|
}
|
|
364
379
|
|
|
380
|
+
// HOLOGRAM ROUTING: If writing to a hologram, route to source holon
|
|
381
|
+
if (data._hologram?.isHologram) {
|
|
382
|
+
const sourceHolon = data._hologram.sourceHolon || this._extractHolonIdFromSoul(data._hologram.soul);
|
|
383
|
+
if (sourceHolon && sourceHolon !== holonId) {
|
|
384
|
+
this._log('DEBUG', '🔀 HOLOGRAM WRITE: Routing to source holon', {
|
|
385
|
+
requestedHolon: holonId?.slice(0, 12) + '...',
|
|
386
|
+
sourceHolon: sourceHolon?.slice(0, 12) + '...',
|
|
387
|
+
lensName
|
|
388
|
+
});
|
|
389
|
+
holonId = sourceHolon;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
365
393
|
// Check write authorization BEFORE optimistic caching
|
|
366
394
|
// Helper function to detect if a string is a pubkey (64-char hex)
|
|
367
395
|
const isPubkeyHolon = typeof holonId === 'string' && /^[0-9a-f]{64}$/i.test(holonId);
|
|
@@ -379,78 +407,17 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
379
407
|
// For pubkey-based holons, check if it's our own holon (matches effective writer's pubkey)
|
|
380
408
|
// For non-pubkey holons (H3 cells, UUIDs), skip authorization for backwards compatibility
|
|
381
409
|
const isOwnHolon = effectiveWriterPubKey && (holonId === effectiveWriterPubKey || !isPubkeyHolon);
|
|
382
|
-
const capToken = options.capabilityToken || options.capability;
|
|
383
|
-
const actingAsHolon = options.actingAs || options.actingAsHolon;
|
|
384
410
|
|
|
411
|
+
// SIMPLIFIED: Everyone can write everywhere (Nostr is open-write).
|
|
412
|
+
// Non-federated writes are simply invisible on read (filtered out).
|
|
413
|
+
// Only log for debugging; no capability checks needed.
|
|
385
414
|
if (options.externalWriter) {
|
|
386
|
-
|
|
387
|
-
// Skip holosphere-level auth checks entirely.
|
|
388
|
-
// The data will be visible to the holon owner because read() skips
|
|
389
|
-
// author filtering for own-holon reads (d-tag path scopes the data).
|
|
390
|
-
this._log('DEBUG', '✅ External writer — skipping auth', {
|
|
415
|
+
this._log('DEBUG', '✅ External writer', {
|
|
391
416
|
writerPubKey: effectiveWriterPubKey?.slice(0, 12) + '...',
|
|
392
417
|
holonId: holonId?.slice(0, 12) + '...'
|
|
393
418
|
});
|
|
394
|
-
// Fall through to actual write below (skip all auth checks)
|
|
395
419
|
} else if (!isOwnHolon) {
|
|
396
|
-
|
|
397
|
-
// Priority: 1. Explicit capability token, 2. Membership-based access
|
|
398
|
-
|
|
399
|
-
if (capToken) {
|
|
400
|
-
// Explicit capability token provided - verify it
|
|
401
|
-
const authorized = await this.verifyCapability(capToken, 'write', { holonId, lensName });
|
|
402
|
-
if (!authorized) {
|
|
403
|
-
this._log('WARN', '🚫 Write denied: Invalid capability token', { holonId, lensName });
|
|
404
|
-
throw new AuthorizationError(
|
|
405
|
-
'Invalid capability token for write operation',
|
|
406
|
-
'write'
|
|
407
|
-
);
|
|
408
|
-
}
|
|
409
|
-
this._log('DEBUG', '✅ Write authorized via capability token', { holonId, lensName });
|
|
410
|
-
} else {
|
|
411
|
-
// No capability token - check unified access control
|
|
412
|
-
// Uses canAccess() which checks: 1. owner, 2. membership, 3. federation grants (unified)
|
|
413
|
-
const writerPubKey = effectiveWriterPubKey;
|
|
414
|
-
|
|
415
|
-
if (!writerPubKey) {
|
|
416
|
-
this._log('WARN', '🚫 Write denied: No authenticated user', { holonId, lensName });
|
|
417
|
-
throw new AuthorizationError(
|
|
418
|
-
'Authentication required for writing to federated holons',
|
|
419
|
-
'write'
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Check unified access control
|
|
424
|
-
const accessCheck = await this.canAccess(holonId, lensName, writerPubKey, 'write', { actingAsHolon });
|
|
425
|
-
|
|
426
|
-
if (!accessCheck.allowed) {
|
|
427
|
-
this._log('WARN', '🚫 Write denied: No write access', {
|
|
428
|
-
holonId,
|
|
429
|
-
lensName,
|
|
430
|
-
reason: accessCheck.reason,
|
|
431
|
-
via: accessCheck.via,
|
|
432
|
-
writerPubKey: writerPubKey?.slice(0, 12) + '...'
|
|
433
|
-
});
|
|
434
|
-
throw new AuthorizationError(
|
|
435
|
-
`Write access denied: ${accessCheck.reason}`,
|
|
436
|
-
'write'
|
|
437
|
-
);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
this._log('DEBUG', '✅ Write authorized via unified access control', {
|
|
441
|
-
holonId,
|
|
442
|
-
lensName,
|
|
443
|
-
via: accessCheck.via,
|
|
444
|
-
reason: accessCheck.reason,
|
|
445
|
-
grant: accessCheck.grant ? 'yes' : 'no'
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
} else if (capToken) {
|
|
449
|
-
// Own holon with explicit token - validate it anyway
|
|
450
|
-
const authorized = await this.verifyCapability(capToken, 'write', { holonId, lensName });
|
|
451
|
-
if (!authorized) {
|
|
452
|
-
throw new AuthorizationError('AuthorizationError: Invalid capability token for write operation', 'write');
|
|
453
|
-
}
|
|
420
|
+
this._log('DEBUG', '📝 Writing to non-own holon (open write)', { holonId, lensName });
|
|
454
421
|
}
|
|
455
422
|
|
|
456
423
|
if (!data.id) {
|
|
@@ -578,14 +545,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
578
545
|
// Clear from write cache after successful sync (optional - keeps cache smaller)
|
|
579
546
|
// this._writeCache.delete(path);
|
|
580
547
|
|
|
581
|
-
// Handle propagation in background
|
|
582
|
-
const { autoPropagate = true } = options;
|
|
583
|
-
if (autoPropagate && !data.hologram) {
|
|
584
|
-
this._log('DEBUG', '📤 Starting propagation', { path });
|
|
585
|
-
this.propagate(holonId, lensName, data, options.propagationOptions || {})
|
|
586
|
-
.catch(err => this._log('WARN', '⚠️ Propagation failed', { path, error: err.message }));
|
|
587
|
-
}
|
|
588
|
-
|
|
589
548
|
return true;
|
|
590
549
|
} catch (error) {
|
|
591
550
|
const duration = Date.now() - startTime;
|
|
@@ -645,26 +604,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
645
604
|
await storage.write(this.client, path, localData);
|
|
646
605
|
|
|
647
606
|
if (Object.keys(sourceUpdates).length > 0) {
|
|
648
|
-
//
|
|
649
|
-
|
|
650
|
-
if (!capability) {
|
|
651
|
-
throw new Error('Hologram missing capability token for source update');
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
const hasWrite = await crypto.verifyCapability(
|
|
655
|
-
capability,
|
|
656
|
-
'write',
|
|
657
|
-
{
|
|
658
|
-
holonId: existingData.target.holonId,
|
|
659
|
-
lensName: existingData.target.lensName,
|
|
660
|
-
dataId: existingData.target.dataId
|
|
661
|
-
}
|
|
662
|
-
);
|
|
663
|
-
|
|
664
|
-
if (!hasWrite) {
|
|
665
|
-
throw new Error('Write capability required to update source data through hologram');
|
|
666
|
-
}
|
|
667
|
-
|
|
607
|
+
// SIMPLIFIED: Everyone can write everywhere (Nostr is open-write).
|
|
608
|
+
// Just update the source directly through the hologram.
|
|
668
609
|
const sourcePath = storage.buildPath(
|
|
669
610
|
existingData.target.appname || this.config.appName,
|
|
670
611
|
existingData.target.holonId,
|
|
@@ -703,15 +644,14 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
703
644
|
}
|
|
704
645
|
|
|
705
646
|
/**
|
|
706
|
-
* Check if
|
|
647
|
+
* Check if a pubkey is in our federation.
|
|
707
648
|
*
|
|
708
649
|
* @private
|
|
709
|
-
* @param {string}
|
|
710
|
-
* @
|
|
711
|
-
* @returns {Promise<Object|null>} Capability entry or null
|
|
650
|
+
* @param {string} pubKey - Public key to check
|
|
651
|
+
* @returns {Promise<boolean>} True if federated
|
|
712
652
|
*/
|
|
713
|
-
async
|
|
714
|
-
return registry.
|
|
653
|
+
async _isFederatedWith(pubKey) {
|
|
654
|
+
return registry.isFederated(this.client, this.config.appName, pubKey);
|
|
715
655
|
}
|
|
716
656
|
|
|
717
657
|
/**
|
|
@@ -890,6 +830,9 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
890
830
|
* hologram resolution (including capability verification and recursive
|
|
891
831
|
* chain resolution) to federation.resolveHologram().
|
|
892
832
|
*
|
|
833
|
+
* Lens holograms (lens: true) are expanded separately — they fetch all
|
|
834
|
+
* items from the source author's lens and merge them into the result set.
|
|
835
|
+
*
|
|
893
836
|
* @private
|
|
894
837
|
* @param {Object|Array|null} data - Data that may contain holograms
|
|
895
838
|
* @returns {Promise<Object|Array|null>} Resolved data with holograms replaced by source data
|
|
@@ -899,16 +842,71 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
899
842
|
|
|
900
843
|
if (Array.isArray(data)) {
|
|
901
844
|
const resolved = [];
|
|
845
|
+
const lensHolograms = [];
|
|
846
|
+
|
|
847
|
+
// First pass: resolve item holograms, collect lens holograms
|
|
902
848
|
for (const item of data) {
|
|
849
|
+
if (federation.isLensHologram(item)) {
|
|
850
|
+
lensHolograms.push(item);
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
903
853
|
const resolvedItem = await this._resolveHolograms(item);
|
|
904
854
|
if (resolvedItem !== null) {
|
|
905
855
|
resolved.push(resolvedItem);
|
|
906
856
|
}
|
|
907
857
|
}
|
|
858
|
+
|
|
859
|
+
// Second pass: expand lens holograms
|
|
860
|
+
if (lensHolograms.length > 0) {
|
|
861
|
+
const existingIds = new Set(resolved.map(r => r.id));
|
|
862
|
+
|
|
863
|
+
for (const lh of lensHolograms) {
|
|
864
|
+
const target = lh.target || {};
|
|
865
|
+
const sourcePath = storage.buildPath(
|
|
866
|
+
target.app || this.config.appName,
|
|
867
|
+
target.holon,
|
|
868
|
+
target.lens
|
|
869
|
+
);
|
|
870
|
+
try {
|
|
871
|
+
const items = await storage.readAll(this.client, sourcePath, {
|
|
872
|
+
authors: [target.author],
|
|
873
|
+
});
|
|
874
|
+
if (Array.isArray(items)) {
|
|
875
|
+
for (const sourceItem of items) {
|
|
876
|
+
// Skip holograms in the source lens (no transitive expansion)
|
|
877
|
+
if (sourceItem.hologram) continue;
|
|
878
|
+
// Deduplicate by id — local items take precedence
|
|
879
|
+
if (sourceItem.id && !existingIds.has(sourceItem.id)) {
|
|
880
|
+
existingIds.add(sourceItem.id);
|
|
881
|
+
// Tag with hologram metadata so consumers know the provenance
|
|
882
|
+
resolved.push({
|
|
883
|
+
...sourceItem,
|
|
884
|
+
_hologram: {
|
|
885
|
+
isHologram: true,
|
|
886
|
+
soul: lh.soul,
|
|
887
|
+
sourceHolon: target.holon,
|
|
888
|
+
sourcePubKey: target.author,
|
|
889
|
+
lensHologram: true,
|
|
890
|
+
},
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
} catch (err) {
|
|
896
|
+
this._log('WARN', '⚠️ Failed to expand lens hologram', {
|
|
897
|
+
soul: lh.soul,
|
|
898
|
+
error: err.message,
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
908
904
|
return resolved;
|
|
909
905
|
}
|
|
910
906
|
|
|
911
907
|
if (data?.hologram === true && data.target) {
|
|
908
|
+
// Lens holograms return null from resolveHologram — they're handled in array context
|
|
909
|
+
if (data.lens === true) return null;
|
|
912
910
|
return federation.resolveHologram(this.client, data, new Set(), [], {
|
|
913
911
|
appname: this.config.appName,
|
|
914
912
|
deleteCircular: false,
|
|
@@ -925,11 +923,9 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
925
923
|
* @param {string} lensName - Name of the lens (data category)
|
|
926
924
|
* @param {string|null} [dataId=null] - Specific data ID, or null to read all
|
|
927
925
|
* @param {Object} [options={}] - Read options
|
|
928
|
-
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
929
926
|
* @param {boolean} [options.resolveHolograms=true] - Whether to resolve hologram references
|
|
930
927
|
* @returns {Promise<Object|Array|null>} Data object, array of objects, or null if not found
|
|
931
928
|
* @throws {ValidationError} If holonId or lensName is invalid
|
|
932
|
-
* @throws {AuthorizationError} If capability token is invalid
|
|
933
929
|
*/
|
|
934
930
|
async read(holonId, lensName, dataId = null, options = {}) {
|
|
935
931
|
// Auto-convert to strings for convenience
|
|
@@ -990,40 +986,16 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
990
986
|
if (options.forceRelay) readOptions.forceRelay = true;
|
|
991
987
|
if (options.timeout) readOptions.timeout = options.timeout;
|
|
992
988
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
throw new AuthorizationError('AuthorizationError: Invalid capability token for read operation', 'read');
|
|
999
|
-
}
|
|
1000
|
-
// Use target author for reading
|
|
1001
|
-
if (isOtherAuthor) {
|
|
1002
|
-
readOptions.authors = [targetPubkey];
|
|
1003
|
-
}
|
|
1004
|
-
} else if (isOtherAuthor) {
|
|
1005
|
-
// Auto-check capability for other author's data
|
|
1006
|
-
this._log('DEBUG', '🔍 Looking up capability for federated author', {
|
|
1007
|
-
holonId,
|
|
1008
|
-
lensName,
|
|
1009
|
-
dataId: dataId || '*',
|
|
1010
|
-
targetPubkey: targetPubkey?.slice(0, 12) + '...',
|
|
1011
|
-
myPubkey: this.client?.publicKey?.slice(0, 12) + '...'
|
|
1012
|
-
});
|
|
1013
|
-
const capability = await this._getCapabilityForAuthor(targetPubkey, { holonId, lensName, dataId });
|
|
1014
|
-
if (!capability) {
|
|
1015
|
-
this._log('WARN', '❌ No capability found for federated author - returning empty', {
|
|
989
|
+
if (isOtherAuthor) {
|
|
990
|
+
// Reading another author's data — check federation membership
|
|
991
|
+
const federated = await this._isFederatedWith(targetPubkey);
|
|
992
|
+
if (!federated) {
|
|
993
|
+
this._log('DEBUG', '🔍 Not federated with author — returning empty', {
|
|
1016
994
|
holonId,
|
|
1017
|
-
lensName,
|
|
1018
995
|
targetPubkey: targetPubkey?.slice(0, 12) + '...'
|
|
1019
996
|
});
|
|
1020
997
|
return dataId ? null : [];
|
|
1021
998
|
}
|
|
1022
|
-
this._log('DEBUG', '✅ Capability found for federated author', {
|
|
1023
|
-
holonId,
|
|
1024
|
-
lensName,
|
|
1025
|
-
targetPubkey: targetPubkey?.slice(0, 12) + '...'
|
|
1026
|
-
});
|
|
1027
999
|
readOptions.authors = [targetPubkey];
|
|
1028
1000
|
}
|
|
1029
1001
|
|
|
@@ -1154,13 +1126,11 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1154
1126
|
* @param {string} dataId - ID of the data to update
|
|
1155
1127
|
* @param {Object} updates - Object containing fields to update
|
|
1156
1128
|
* @param {Object} [options={}] - Update options
|
|
1157
|
-
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
1158
1129
|
* @param {boolean} [options.validate=true] - Whether to validate against schema
|
|
1159
1130
|
* @param {boolean} [options.strict] - Override schema strict mode
|
|
1160
1131
|
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
1161
1132
|
* @returns {Promise<boolean>} True if update succeeded, false if data not found
|
|
1162
1133
|
* @throws {ValidationError} If holonId, lensName, dataId, or updates is invalid
|
|
1163
|
-
* @throws {AuthorizationError} If capability token is invalid
|
|
1164
1134
|
*/
|
|
1165
1135
|
async update(holonId, lensName, dataId, updates, options = {}) {
|
|
1166
1136
|
if (!holonId || typeof holonId !== 'string') {
|
|
@@ -1176,38 +1146,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1176
1146
|
throw new ValidationError('ValidationError: updates must be an object');
|
|
1177
1147
|
}
|
|
1178
1148
|
|
|
1179
|
-
//
|
|
1180
|
-
//
|
|
1181
|
-
const isPubkeyHolon = typeof holonId === 'string' && /^[0-9a-f]{64}$/i.test(holonId);
|
|
1182
|
-
// For pubkey-based holons, check if it's our own holon (our pubkey)
|
|
1183
|
-
// For non-pubkey holons (H3 cells, UUIDs), skip authorization for backwards compatibility
|
|
1184
|
-
const isOwnHolon = this.client && (holonId === this.client.publicKey || !isPubkeyHolon);
|
|
1185
|
-
const capToken = options.capabilityToken || options.capability;
|
|
1186
|
-
|
|
1187
|
-
if (!isOwnHolon) {
|
|
1188
|
-
// Updating federated holon - require capability token
|
|
1189
|
-
if (!capToken) {
|
|
1190
|
-
this._log('WARN', '🚫 Update denied: No capability token for federated holon', { holonId, lensName, dataId });
|
|
1191
|
-
throw new AuthorizationError(
|
|
1192
|
-
'Capability token required for writing to federated holons',
|
|
1193
|
-
'write'
|
|
1194
|
-
);
|
|
1195
|
-
}
|
|
1196
|
-
const authorized = await this.verifyCapability(capToken, 'write', { holonId, lensName, dataId });
|
|
1197
|
-
if (!authorized) {
|
|
1198
|
-
this._log('WARN', '🚫 Update denied: Invalid capability token', { holonId, lensName, dataId });
|
|
1199
|
-
throw new AuthorizationError(
|
|
1200
|
-
'Invalid capability token for update operation',
|
|
1201
|
-
'write'
|
|
1202
|
-
);
|
|
1203
|
-
}
|
|
1204
|
-
} else if (capToken) {
|
|
1205
|
-
// Own holon with explicit token - validate it anyway
|
|
1206
|
-
const authorized = await this.verifyCapability(capToken, 'write', { holonId, lensName, dataId });
|
|
1207
|
-
if (!authorized) {
|
|
1208
|
-
throw new AuthorizationError('AuthorizationError: Invalid capability token for update operation', 'write');
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1149
|
+
// SIMPLIFIED: Everyone can write everywhere (Nostr is open-write).
|
|
1150
|
+
// No capability checks needed — non-federated updates are invisible on read.
|
|
1211
1151
|
|
|
1212
1152
|
const path = storage.buildPath(this.config.appName, holonId, lensName, dataId);
|
|
1213
1153
|
|
|
@@ -1320,11 +1260,9 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1320
1260
|
* @param {string} lensName - Name of the lens (data category)
|
|
1321
1261
|
* @param {string} dataId - ID of the data to delete
|
|
1322
1262
|
* @param {Object} [options={}] - Delete options
|
|
1323
|
-
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
1324
1263
|
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
1325
1264
|
* @returns {Promise<boolean>} True if deletion succeeded, false if data not found
|
|
1326
1265
|
* @throws {ValidationError} If holonId, lensName, or dataId is invalid
|
|
1327
|
-
* @throws {AuthorizationError} If not owner and no valid capability token
|
|
1328
1266
|
*/
|
|
1329
1267
|
async delete(holonId, lensName, dataId, options = {}) {
|
|
1330
1268
|
// Auto-convert to strings for convenience
|
|
@@ -1357,16 +1295,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1357
1295
|
// For pubkey-based holons, check if it's our own holon (matches effective deleter's pubkey)
|
|
1358
1296
|
// For non-pubkey holons (H3 cells, UUIDs), skip authorization for backwards compatibility
|
|
1359
1297
|
const isOwnHolon = effectiveDeleterPubKey && (holonId === effectiveDeleterPubKey || !isPubkeyHolon);
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
if (!isOwnHolon && !capToken) {
|
|
1363
|
-
// Deleting from federated holon without capability token - fail fast
|
|
1364
|
-
this._log('WARN', '🚫 Delete denied: No capability token for federated holon', { holonId, lensName, dataId });
|
|
1365
|
-
throw new AuthorizationError(
|
|
1366
|
-
'Capability token required for writing to federated holons',
|
|
1367
|
-
'delete'
|
|
1368
|
-
);
|
|
1369
|
-
}
|
|
1298
|
+
// SIMPLIFIED: Everyone can write/delete everywhere (Nostr is open-write).
|
|
1299
|
+
// Non-federated deletes are effectively invisible on read.
|
|
1370
1300
|
|
|
1371
1301
|
const path = storage.buildPath(this.config.appName, holonId, lensName, dataId);
|
|
1372
1302
|
|
|
@@ -1376,7 +1306,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1376
1306
|
let dataSource = cached ? 'write-cache' : null;
|
|
1377
1307
|
|
|
1378
1308
|
if (!existingData) {
|
|
1379
|
-
// When signingKey is provided, read using the signer's pubkey as author
|
|
1380
1309
|
const readOptions = {};
|
|
1381
1310
|
if (effectiveDeleterPubKey && effectiveDeleterPubKey !== this.client?.publicKey) {
|
|
1382
1311
|
readOptions.authors = [effectiveDeleterPubKey];
|
|
@@ -1385,7 +1314,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1385
1314
|
dataSource = existingData ? 'storage' : null;
|
|
1386
1315
|
}
|
|
1387
1316
|
|
|
1388
|
-
// Return false if data doesn't exist in cache or storage
|
|
1389
1317
|
if (!existingData) {
|
|
1390
1318
|
this._log('DEBUG', '🗑️ DELETE: Data not found', { path });
|
|
1391
1319
|
return false;
|
|
@@ -1393,27 +1321,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1393
1321
|
|
|
1394
1322
|
this._log('DEBUG', '🗑️ DELETE: Found data', { path, source: dataSource });
|
|
1395
1323
|
|
|
1396
|
-
// Check authorization: data owner can delete, others need capability token
|
|
1397
|
-
const dataOwner = existingData.owner || existingData._creator;
|
|
1398
|
-
const isDataOwner = !dataOwner || dataOwner === this.client.publicKey || dataOwner === effectiveDeleterPubKey;
|
|
1399
|
-
|
|
1400
|
-
if (!isDataOwner) {
|
|
1401
|
-
// Non-data-owner must provide a valid capability token
|
|
1402
|
-
if (!capToken) {
|
|
1403
|
-
throw new AuthorizationError('AuthorizationError: Capability token required for delete operation', 'delete');
|
|
1404
|
-
}
|
|
1405
|
-
const authorized = await this.verifyCapability(capToken, 'delete', { holonId, lensName, dataId });
|
|
1406
|
-
if (!authorized) {
|
|
1407
|
-
throw new AuthorizationError('AuthorizationError: Invalid capability token for delete operation', 'delete');
|
|
1408
|
-
}
|
|
1409
|
-
} else if (capToken) {
|
|
1410
|
-
// Data owner provided a token - validate it anyway
|
|
1411
|
-
const authorized = await this.verifyCapability(capToken, 'delete', { holonId, lensName, dataId });
|
|
1412
|
-
if (!authorized) {
|
|
1413
|
-
throw new AuthorizationError('AuthorizationError: Invalid capability token for delete operation', 'delete');
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
1324
|
// OPTIMISTIC DELETE: Remove from write cache and add to delete cache
|
|
1418
1325
|
this._writeCache.delete(path);
|
|
1419
1326
|
this._deleteCache.add(path);
|
|
@@ -1779,7 +1686,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1779
1686
|
*/
|
|
1780
1687
|
async createHologram(sourceHolon, lensName, data, targetHolon = null) {
|
|
1781
1688
|
const target = targetHolon || sourceHolon;
|
|
1782
|
-
// Use createHologramWithCapability which auto-generates capability for self-operations
|
|
1783
1689
|
return federation.createHologramWithCapability(
|
|
1784
1690
|
this.client,
|
|
1785
1691
|
sourceHolon,
|
|
@@ -1787,11 +1693,7 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1787
1693
|
lensName,
|
|
1788
1694
|
data.id,
|
|
1789
1695
|
this.config.appName,
|
|
1790
|
-
{
|
|
1791
|
-
sourceAuthorPubKey: this.client.publicKey,
|
|
1792
|
-
targetAuthorPubKey: this.client.publicKey,
|
|
1793
|
-
permissions: ['read'],
|
|
1794
|
-
}
|
|
1696
|
+
{ sourceAuthorPubKey: this.client.publicKey }
|
|
1795
1697
|
);
|
|
1796
1698
|
}
|
|
1797
1699
|
|
|
@@ -1867,7 +1769,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1867
1769
|
async propagateData(data, sourceHolon, targetHolon, lensName, options = {}) {
|
|
1868
1770
|
// Extract mode from options, default to 'reference' for hologram creation
|
|
1869
1771
|
const mode = options.mode || 'reference';
|
|
1870
|
-
// UNIFIED MODEL: Pass sourceAuthorPubKey for capability-based federation
|
|
1871
1772
|
return federation.propagateData(
|
|
1872
1773
|
this.client,
|
|
1873
1774
|
this.config.appName,
|
|
@@ -1878,7 +1779,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1878
1779
|
mode,
|
|
1879
1780
|
{
|
|
1880
1781
|
sourceAuthorPubKey: options.sourceAuthorPubKey || this.client.publicKey,
|
|
1881
|
-
capability: options.capability,
|
|
1882
1782
|
}
|
|
1883
1783
|
);
|
|
1884
1784
|
}
|
|
@@ -2078,12 +1978,11 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2078
1978
|
* @param {string[]} [options.lensConfig.inbound] - Lenses for inbound federation
|
|
2079
1979
|
* @param {string[]} [options.lensConfig.outbound] - Lenses for outbound federation
|
|
2080
1980
|
* @param {string} [options.partnerName] - Human-readable name for the partner holon
|
|
2081
|
-
* @param {boolean} [options.skipPropagation=false] - Skip propagating existing data
|
|
2082
1981
|
* @returns {Promise<boolean>} True if federation was established
|
|
2083
1982
|
* @throws {Error} If trying to federate a holon with itself
|
|
2084
1983
|
*/
|
|
2085
1984
|
async federateHolon(sourceHolon, targetHolon, options = {}) {
|
|
2086
|
-
const { lensConfig = { inbound: [], outbound: [] }, partnerName = null
|
|
1985
|
+
const { lensConfig = { inbound: [], outbound: [] }, partnerName = null } = options;
|
|
2087
1986
|
|
|
2088
1987
|
if (sourceHolon === targetHolon) {
|
|
2089
1988
|
throw new Error('Cannot federate a holon with itself');
|
|
@@ -2142,16 +2041,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2142
2041
|
const success = await this.writeGlobal('federation', federationData);
|
|
2143
2042
|
this.clearCache('federation');
|
|
2144
2043
|
|
|
2145
|
-
// Only propagate existing data if skipPropagation is false
|
|
2146
|
-
if (success && !skipPropagation) {
|
|
2147
|
-
for (const lens of (lensConfig.inbound || [])) {
|
|
2148
|
-
await this.federate(targetHolon, sourceHolon, lens, { direction: 'outbound', mode: 'reference' });
|
|
2149
|
-
}
|
|
2150
|
-
for (const lens of (lensConfig.outbound || [])) {
|
|
2151
|
-
await this.federate(sourceHolon, targetHolon, lens, { direction: 'outbound', mode: 'reference' });
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
2044
|
return success;
|
|
2156
2045
|
}
|
|
2157
2046
|
|
|
@@ -2176,9 +2065,15 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2176
2065
|
delete federationData.lensConfig[targetHolon];
|
|
2177
2066
|
}
|
|
2178
2067
|
|
|
2068
|
+
if (federationData.partnerNames) {
|
|
2069
|
+
delete federationData.partnerNames[targetHolon];
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2179
2072
|
const success = await this.writeGlobal('federation', federationData);
|
|
2180
2073
|
this.clearCache('federation');
|
|
2181
2074
|
|
|
2075
|
+
await registry.removeFederatedPartner(this.client, this.config.appName, targetHolon);
|
|
2076
|
+
|
|
2182
2077
|
if (success && lensConfig) {
|
|
2183
2078
|
for (const lens of (lensConfig.inbound || [])) {
|
|
2184
2079
|
await this.unfederate(targetHolon, sourceHolon, lens);
|
|
@@ -2248,26 +2143,14 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2248
2143
|
const isPubkeyHolon = typeof holonId === 'string' && /^[0-9a-f]{64}$/i.test(holonId);
|
|
2249
2144
|
const isOtherAuthor = isPubkeyHolon && holonId !== this.client.publicKey;
|
|
2250
2145
|
|
|
2251
|
-
// For other-author subscriptions,
|
|
2146
|
+
// For other-author subscriptions, check federation membership
|
|
2252
2147
|
if (isOtherAuthor) {
|
|
2253
|
-
const
|
|
2254
|
-
if (
|
|
2255
|
-
|
|
2256
|
-
if (!authorized) {
|
|
2257
|
-
this._log('WARN', '❌ Subscribe: invalid capability token - skipping', { holonId, lensName });
|
|
2258
|
-
return;
|
|
2259
|
-
}
|
|
2260
|
-
} else {
|
|
2261
|
-
const capability = await this._getCapabilityForAuthor(holonId, { holonId, lensName });
|
|
2262
|
-
if (!capability) {
|
|
2263
|
-
this._log('WARN', '❌ Subscribe: no capability for federated author - skipping', {
|
|
2264
|
-
holonId, lensName, targetPubkey: holonId?.slice(0, 12) + '...'
|
|
2265
|
-
});
|
|
2266
|
-
return;
|
|
2267
|
-
}
|
|
2268
|
-
this._log('DEBUG', '✅ Subscribe: capability found for federated author', {
|
|
2148
|
+
const federated = await this._isFederatedWith(holonId);
|
|
2149
|
+
if (!federated) {
|
|
2150
|
+
this._log('DEBUG', '🔍 Subscribe: not federated with author - skipping', {
|
|
2269
2151
|
holonId, lensName, targetPubkey: holonId?.slice(0, 12) + '...'
|
|
2270
2152
|
});
|
|
2153
|
+
return;
|
|
2271
2154
|
}
|
|
2272
2155
|
}
|
|
2273
2156
|
|
|
@@ -2288,6 +2171,7 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2288
2171
|
|
|
2289
2172
|
// Post-setup enhancement: include federated authors
|
|
2290
2173
|
// Own-data subscription is already active; this adds partner visibility
|
|
2174
|
+
// Create a second subscription for federated author updates
|
|
2291
2175
|
if (!isOtherAuthor) {
|
|
2292
2176
|
try {
|
|
2293
2177
|
const fedAuthorsPromise = registry.getFederatedAuthors(
|
|
@@ -2299,7 +2183,17 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2299
2183
|
);
|
|
2300
2184
|
const federatedAuthors = await Promise.race([fedAuthorsPromise, fedAuthorsTimeout]);
|
|
2301
2185
|
if (federatedAuthors.length > 0) {
|
|
2302
|
-
this._log('DEBUG', 'Subscribe: federated authors
|
|
2186
|
+
this._log('DEBUG', 'Subscribe: creating subscription for federated authors', { count: federatedAuthors.length });
|
|
2187
|
+
// Create additional subscription for federated authors' updates
|
|
2188
|
+
const fedSubscription = await subscriptions.createSubscription(
|
|
2189
|
+
this.client, path, callback, { ...subscriptionOptions, authors: federatedAuthors }
|
|
2190
|
+
);
|
|
2191
|
+
// Store reference so it can be unsubscribed later
|
|
2192
|
+
if (!unsubscribeCalled) {
|
|
2193
|
+
this.subscriptionRegistry.register(`${subscriptionId}-fed`, fedSubscription);
|
|
2194
|
+
} else {
|
|
2195
|
+
fedSubscription.unsubscribe();
|
|
2196
|
+
}
|
|
2303
2197
|
}
|
|
2304
2198
|
} catch (err) {
|
|
2305
2199
|
this._log('WARN', 'Failed to get federated authors for subscription', { error: err.message });
|
|
@@ -2316,6 +2210,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2316
2210
|
unsubscribeCalled = true;
|
|
2317
2211
|
if (innerSubscription) {
|
|
2318
2212
|
this.subscriptionRegistry.unregister(subscriptionId);
|
|
2213
|
+
// Also unsubscribe federated authors subscription if it exists
|
|
2214
|
+
this.subscriptionRegistry.unregister(`${subscriptionId}-fed`);
|
|
2319
2215
|
}
|
|
2320
2216
|
}
|
|
2321
2217
|
};
|
|
@@ -2376,31 +2272,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2376
2272
|
return crypto.verify(content, signature, publicKey);
|
|
2377
2273
|
}
|
|
2378
2274
|
|
|
2379
|
-
/**
|
|
2380
|
-
* Issues a capability token for authorization.
|
|
2381
|
-
*
|
|
2382
|
-
* @param {string[]} permissions - Array of permissions ('read', 'write', 'delete')
|
|
2383
|
-
* @param {Object} scope - Scope of the capability (holonId, lensName, etc.)
|
|
2384
|
-
* @param {string} recipient - Public key of the recipient
|
|
2385
|
-
* @param {Object} [options] - Additional options
|
|
2386
|
-
* @returns {Promise<string>} Signed capability token
|
|
2387
|
-
*/
|
|
2388
|
-
async issueCapability(permissions, scope, recipient, options) {
|
|
2389
|
-
return crypto.issueCapability(permissions, scope, recipient, options);
|
|
2390
|
-
}
|
|
2391
|
-
|
|
2392
|
-
/**
|
|
2393
|
-
* Verifies a capability token.
|
|
2394
|
-
*
|
|
2395
|
-
* @param {string} token - Capability token to verify
|
|
2396
|
-
* @param {string} requiredPermission - Required permission to check
|
|
2397
|
-
* @param {Object} scope - Scope to verify against
|
|
2398
|
-
* @returns {Promise<boolean>} True if token is valid and has required permission
|
|
2399
|
-
*/
|
|
2400
|
-
async verifyCapability(token, requiredPermission, scope) {
|
|
2401
|
-
return crypto.verifyCapability(token, requiredPermission, scope);
|
|
2402
|
-
}
|
|
2403
|
-
|
|
2404
2275
|
// === Social Protocol Operations ===
|
|
2405
2276
|
|
|
2406
2277
|
/**
|
|
@@ -2435,34 +2306,13 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2435
2306
|
return true;
|
|
2436
2307
|
}
|
|
2437
2308
|
|
|
2438
|
-
/**
|
|
2439
|
-
* Publishes an ActivityPub object to a holon.
|
|
2440
|
-
*
|
|
2441
|
-
* @param {Object} object - ActivityPub object
|
|
2442
|
-
* @param {string} object.type - ActivityPub type (Note, Article, etc.)
|
|
2443
|
-
* @param {string} [object.actor] - Actor ID (defaults to client public key)
|
|
2444
|
-
* @param {string} holonId - H3 cell ID for the holon
|
|
2445
|
-
* @param {string} [lensName='social'] - Name of the lens
|
|
2446
|
-
* @returns {Promise<boolean>} True if publish succeeded
|
|
2447
|
-
*/
|
|
2448
|
-
async publishActivityPub(object, holonId, lensName = 'social') {
|
|
2449
|
-
// Validate ActivityPub object format
|
|
2450
|
-
social.validateActivityPubObject(object, true); // throwOnError=true
|
|
2451
|
-
|
|
2452
|
-
const activity = social.transformActivityPubObject({
|
|
2453
|
-
...object,
|
|
2454
|
-
actor: object.actor || this.client.publicKey,
|
|
2455
|
-
});
|
|
2456
|
-
return this.write(holonId, lensName, activity);
|
|
2457
|
-
}
|
|
2458
|
-
|
|
2459
2309
|
/**
|
|
2460
2310
|
* Queries social protocol data from a holon.
|
|
2461
2311
|
*
|
|
2462
2312
|
* @param {string} holonId - H3 cell ID for the holon
|
|
2463
2313
|
* @param {Object} [options={}] - Query options
|
|
2464
2314
|
* @param {string} [options.lens='social'] - Name of the lens
|
|
2465
|
-
* @param {string} [options.protocol] - Filter by protocol ('nostr'
|
|
2315
|
+
* @param {string} [options.protocol] - Filter by protocol ('nostr' or 'all')
|
|
2466
2316
|
* @param {string} [options.type] - Filter by content type
|
|
2467
2317
|
* @param {number} [options.since] - Filter events after this timestamp
|
|
2468
2318
|
* @param {number} [options.until] - Filter events before this timestamp
|
|
@@ -2785,11 +2635,10 @@ export {
|
|
|
2785
2635
|
export { spatial, storage, schema, federation, handshake, crypto, nostrUtils, social, subscriptions, hierarchical };
|
|
2786
2636
|
|
|
2787
2637
|
// Re-export federation submodules
|
|
2788
|
-
export {
|
|
2638
|
+
export { requestCard, cardStorage, holonRegistry, registry };
|
|
2789
2639
|
|
|
2790
2640
|
// Re-export specific utilities used in tests
|
|
2791
|
-
export {
|
|
2792
|
-
export { createHologram } from './federation/hologram.js';
|
|
2641
|
+
export { createHologram, createLensHologram, isLensHologram, sendShare, sendAck } from './federation/hologram.js';
|
|
2793
2642
|
|
|
2794
2643
|
// Re-export lens encryption utilities
|
|
2795
2644
|
export { LensKeyStore, buildLensPath, parseLensPath } from './crypto/key-store.js';
|