holosphere 2.0.0-alpha21 → 2.0.0-alpha22
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 +43 -41
- package/dist/{index-COpLk9gL.cjs → index-B4xe-N5-.cjs} +2 -2
- package/dist/{index-COpLk9gL.cjs.map → index-B4xe-N5-.cjs.map} +1 -1
- package/dist/{index-D2WstuZJ.js → index-Bug_CGNq.js} +2 -2
- package/dist/{index-D2WstuZJ.js.map → index-Bug_CGNq.js.map} +1 -1
- package/dist/index-CaCPzdlv.cjs +29 -0
- package/dist/index-CaCPzdlv.cjs.map +1 -0
- package/dist/{index-B6-8KAQm.js → index-D76zMgwU.js} +2 -2
- package/dist/{index-B6-8KAQm.js.map → index-D76zMgwU.js.map} +1 -1
- package/dist/{index--QsHG_gD.cjs → index-DuOuk96g.cjs} +2 -2
- package/dist/{index--QsHG_gD.cjs.map → index-DuOuk96g.cjs.map} +1 -1
- package/dist/{index-BHptWysv.js → index-bYHRpACA.js} +2951 -7736
- package/dist/index-bYHRpACA.js.map +1 -0
- package/dist/{indexeddb-storage-kQ53UHEE.js → indexeddb-storage-BrIwr42m.js} +2 -2
- package/dist/{indexeddb-storage-kQ53UHEE.js.map → indexeddb-storage-BrIwr42m.js.map} +1 -1
- package/dist/{indexeddb-storage-wKG4mICM.cjs → indexeddb-storage-CFWfkdX9.cjs} +2 -2
- package/dist/{indexeddb-storage-wKG4mICM.cjs.map → indexeddb-storage-CFWfkdX9.cjs.map} +1 -1
- package/dist/{memory-storage-DnXCSbBl.js → memory-storage-BDQRj-2j.js} +2 -2
- package/dist/{memory-storage-DnXCSbBl.js.map → memory-storage-BDQRj-2j.js.map} +1 -1
- package/dist/{memory-storage-CGC8xM2G.cjs → memory-storage-bkatDnuR.cjs} +2 -2
- package/dist/{memory-storage-CGC8xM2G.cjs.map → memory-storage-bkatDnuR.cjs.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 +142 -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
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { HoloSphere as HoloSphereCore } from './core/holosphere.js';
|
|
15
15
|
import * as spatial from './spatial/h3-operations.js';
|
|
16
|
-
import * as storage from './storage/
|
|
17
|
-
import * as nostrStorage from './storage/nostr-wrapper.js';
|
|
16
|
+
import * as storage from './storage/nostr-wrapper.js';
|
|
18
17
|
import * as nostrAsync from './storage/nostr-async.js';
|
|
19
18
|
import * as globalTables from './storage/global-tables.js';
|
|
20
19
|
import * as schema from './schema/validator.js';
|
|
@@ -23,7 +22,6 @@ import * as federation from './federation/hologram.js';
|
|
|
23
22
|
import * as handshake from './federation/handshake.js';
|
|
24
23
|
import * as holonRegistry from './federation/holon-registry.js';
|
|
25
24
|
import * as registry from './federation/registry.js';
|
|
26
|
-
import * as capabilities from './federation/capabilities.js';
|
|
27
25
|
import * as requestCard from './federation/request-card.js';
|
|
28
26
|
import * as cardStorage from './federation/card-storage.js';
|
|
29
27
|
import * as crypto from './crypto/secp256k1.js';
|
|
@@ -78,7 +76,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
78
76
|
*
|
|
79
77
|
* @param {Object} config - Configuration options
|
|
80
78
|
* @param {string} config.appName - Application namespace for data isolation
|
|
81
|
-
* @param {string} [config.backend='nostr'] - Storage backend type ('nostr', 'gundb', 'activitypub')
|
|
82
79
|
* @param {string[]} [config.relays] - Nostr relay URLs for distributed storage
|
|
83
80
|
* @param {string} [config.openaiKey] - OpenAI API key for AI services
|
|
84
81
|
* @param {Object} [config.aiOptions] - AI service configuration
|
|
@@ -177,6 +174,25 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
177
174
|
return isNaN(parsed) ? undefined : parsed;
|
|
178
175
|
}
|
|
179
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Extracts holon ID from a hologram soul path.
|
|
179
|
+
* Soul paths follow the format: "AppName/Holons/<holonId>/lensName/dataId"
|
|
180
|
+
*
|
|
181
|
+
* @private
|
|
182
|
+
* @param {string|undefined} soul - The soul path from a hologram
|
|
183
|
+
* @returns {string|null} The extracted holon ID or null if not found
|
|
184
|
+
*/
|
|
185
|
+
_extractHolonIdFromSoul(soul) {
|
|
186
|
+
if (!soul || typeof soul !== 'string') return null;
|
|
187
|
+
// Soul format: "AppName/Holons/<holonId>/lensName/dataId" or "Holons/<holonId>/..."
|
|
188
|
+
const parts = soul.split('/');
|
|
189
|
+
const holonsIndex = parts.indexOf('Holons');
|
|
190
|
+
if (holonsIndex !== -1 && parts.length > holonsIndex + 1) {
|
|
191
|
+
return parts[holonsIndex + 1];
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
180
196
|
/**
|
|
181
197
|
* Initializes AI services with the provided API key.
|
|
182
198
|
*
|
|
@@ -335,17 +351,13 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
335
351
|
* @param {string} lensName - Name of the lens (data category)
|
|
336
352
|
* @param {Object} data - Data object to write (must have or will be assigned an id)
|
|
337
353
|
* @param {Object} [options={}] - Write options
|
|
338
|
-
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
339
354
|
* @param {boolean} [options.validate=true] - Whether to validate against schema
|
|
340
355
|
* @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
356
|
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
344
357
|
* @param {string} [options.signingKey] - Private key to sign with (hex format). If not provided, uses holosphere's default key.
|
|
345
358
|
* @param {boolean} [options.encrypt] - Whether to encrypt this data. Defaults to encryptByDefault config.
|
|
346
359
|
* @returns {Promise<boolean>} True if write succeeded (or queued for optimistic writes)
|
|
347
360
|
* @throws {ValidationError} If holonId, lensName, or data is invalid
|
|
348
|
-
* @throws {AuthorizationError} If capability token is invalid
|
|
349
361
|
*/
|
|
350
362
|
async write(holonId, lensName, data, options = {}) {
|
|
351
363
|
// Auto-convert to strings for convenience
|
|
@@ -362,6 +374,19 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
362
374
|
throw new ValidationError('ValidationError: data must be an object');
|
|
363
375
|
}
|
|
364
376
|
|
|
377
|
+
// HOLOGRAM ROUTING: If writing to a hologram, route to source holon
|
|
378
|
+
if (data._hologram?.isHologram) {
|
|
379
|
+
const sourceHolon = data._hologram.sourceHolon || this._extractHolonIdFromSoul(data._hologram.soul);
|
|
380
|
+
if (sourceHolon && sourceHolon !== holonId) {
|
|
381
|
+
this._log('DEBUG', '🔀 HOLOGRAM WRITE: Routing to source holon', {
|
|
382
|
+
requestedHolon: holonId?.slice(0, 12) + '...',
|
|
383
|
+
sourceHolon: sourceHolon?.slice(0, 12) + '...',
|
|
384
|
+
lensName
|
|
385
|
+
});
|
|
386
|
+
holonId = sourceHolon;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
365
390
|
// Check write authorization BEFORE optimistic caching
|
|
366
391
|
// Helper function to detect if a string is a pubkey (64-char hex)
|
|
367
392
|
const isPubkeyHolon = typeof holonId === 'string' && /^[0-9a-f]{64}$/i.test(holonId);
|
|
@@ -379,78 +404,17 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
379
404
|
// For pubkey-based holons, check if it's our own holon (matches effective writer's pubkey)
|
|
380
405
|
// For non-pubkey holons (H3 cells, UUIDs), skip authorization for backwards compatibility
|
|
381
406
|
const isOwnHolon = effectiveWriterPubKey && (holonId === effectiveWriterPubKey || !isPubkeyHolon);
|
|
382
|
-
const capToken = options.capabilityToken || options.capability;
|
|
383
|
-
const actingAsHolon = options.actingAs || options.actingAsHolon;
|
|
384
407
|
|
|
408
|
+
// SIMPLIFIED: Everyone can write everywhere (Nostr is open-write).
|
|
409
|
+
// Non-federated writes are simply invisible on read (filtered out).
|
|
410
|
+
// Only log for debugging; no capability checks needed.
|
|
385
411
|
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', {
|
|
412
|
+
this._log('DEBUG', '✅ External writer', {
|
|
391
413
|
writerPubKey: effectiveWriterPubKey?.slice(0, 12) + '...',
|
|
392
414
|
holonId: holonId?.slice(0, 12) + '...'
|
|
393
415
|
});
|
|
394
|
-
// Fall through to actual write below (skip all auth checks)
|
|
395
416
|
} 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
|
-
}
|
|
417
|
+
this._log('DEBUG', '📝 Writing to non-own holon (open write)', { holonId, lensName });
|
|
454
418
|
}
|
|
455
419
|
|
|
456
420
|
if (!data.id) {
|
|
@@ -578,14 +542,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
578
542
|
// Clear from write cache after successful sync (optional - keeps cache smaller)
|
|
579
543
|
// this._writeCache.delete(path);
|
|
580
544
|
|
|
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
545
|
return true;
|
|
590
546
|
} catch (error) {
|
|
591
547
|
const duration = Date.now() - startTime;
|
|
@@ -645,26 +601,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
645
601
|
await storage.write(this.client, path, localData);
|
|
646
602
|
|
|
647
603
|
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
|
-
|
|
604
|
+
// SIMPLIFIED: Everyone can write everywhere (Nostr is open-write).
|
|
605
|
+
// Just update the source directly through the hologram.
|
|
668
606
|
const sourcePath = storage.buildPath(
|
|
669
607
|
existingData.target.appname || this.config.appName,
|
|
670
608
|
existingData.target.holonId,
|
|
@@ -703,15 +641,14 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
703
641
|
}
|
|
704
642
|
|
|
705
643
|
/**
|
|
706
|
-
* Check if
|
|
644
|
+
* Check if a pubkey is in our federation.
|
|
707
645
|
*
|
|
708
646
|
* @private
|
|
709
|
-
* @param {string}
|
|
710
|
-
* @
|
|
711
|
-
* @returns {Promise<Object|null>} Capability entry or null
|
|
647
|
+
* @param {string} pubKey - Public key to check
|
|
648
|
+
* @returns {Promise<boolean>} True if federated
|
|
712
649
|
*/
|
|
713
|
-
async
|
|
714
|
-
return registry.
|
|
650
|
+
async _isFederatedWith(pubKey) {
|
|
651
|
+
return registry.isFederated(this.client, this.config.appName, pubKey);
|
|
715
652
|
}
|
|
716
653
|
|
|
717
654
|
/**
|
|
@@ -890,6 +827,9 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
890
827
|
* hologram resolution (including capability verification and recursive
|
|
891
828
|
* chain resolution) to federation.resolveHologram().
|
|
892
829
|
*
|
|
830
|
+
* Lens holograms (lens: true) are expanded separately — they fetch all
|
|
831
|
+
* items from the source author's lens and merge them into the result set.
|
|
832
|
+
*
|
|
893
833
|
* @private
|
|
894
834
|
* @param {Object|Array|null} data - Data that may contain holograms
|
|
895
835
|
* @returns {Promise<Object|Array|null>} Resolved data with holograms replaced by source data
|
|
@@ -899,16 +839,71 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
899
839
|
|
|
900
840
|
if (Array.isArray(data)) {
|
|
901
841
|
const resolved = [];
|
|
842
|
+
const lensHolograms = [];
|
|
843
|
+
|
|
844
|
+
// First pass: resolve item holograms, collect lens holograms
|
|
902
845
|
for (const item of data) {
|
|
846
|
+
if (federation.isLensHologram(item)) {
|
|
847
|
+
lensHolograms.push(item);
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
903
850
|
const resolvedItem = await this._resolveHolograms(item);
|
|
904
851
|
if (resolvedItem !== null) {
|
|
905
852
|
resolved.push(resolvedItem);
|
|
906
853
|
}
|
|
907
854
|
}
|
|
855
|
+
|
|
856
|
+
// Second pass: expand lens holograms
|
|
857
|
+
if (lensHolograms.length > 0) {
|
|
858
|
+
const existingIds = new Set(resolved.map(r => r.id));
|
|
859
|
+
|
|
860
|
+
for (const lh of lensHolograms) {
|
|
861
|
+
const target = lh.target || {};
|
|
862
|
+
const sourcePath = storage.buildPath(
|
|
863
|
+
target.app || this.config.appName,
|
|
864
|
+
target.holon,
|
|
865
|
+
target.lens
|
|
866
|
+
);
|
|
867
|
+
try {
|
|
868
|
+
const items = await storage.readAll(this.client, sourcePath, {
|
|
869
|
+
authors: [target.author],
|
|
870
|
+
});
|
|
871
|
+
if (Array.isArray(items)) {
|
|
872
|
+
for (const sourceItem of items) {
|
|
873
|
+
// Skip holograms in the source lens (no transitive expansion)
|
|
874
|
+
if (sourceItem.hologram) continue;
|
|
875
|
+
// Deduplicate by id — local items take precedence
|
|
876
|
+
if (sourceItem.id && !existingIds.has(sourceItem.id)) {
|
|
877
|
+
existingIds.add(sourceItem.id);
|
|
878
|
+
// Tag with hologram metadata so consumers know the provenance
|
|
879
|
+
resolved.push({
|
|
880
|
+
...sourceItem,
|
|
881
|
+
_hologram: {
|
|
882
|
+
isHologram: true,
|
|
883
|
+
soul: lh.soul,
|
|
884
|
+
sourceHolon: target.holon,
|
|
885
|
+
sourcePubKey: target.author,
|
|
886
|
+
lensHologram: true,
|
|
887
|
+
},
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
} catch (err) {
|
|
893
|
+
this._log('WARN', '⚠️ Failed to expand lens hologram', {
|
|
894
|
+
soul: lh.soul,
|
|
895
|
+
error: err.message,
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
908
901
|
return resolved;
|
|
909
902
|
}
|
|
910
903
|
|
|
911
904
|
if (data?.hologram === true && data.target) {
|
|
905
|
+
// Lens holograms return null from resolveHologram — they're handled in array context
|
|
906
|
+
if (data.lens === true) return null;
|
|
912
907
|
return federation.resolveHologram(this.client, data, new Set(), [], {
|
|
913
908
|
appname: this.config.appName,
|
|
914
909
|
deleteCircular: false,
|
|
@@ -925,11 +920,9 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
925
920
|
* @param {string} lensName - Name of the lens (data category)
|
|
926
921
|
* @param {string|null} [dataId=null] - Specific data ID, or null to read all
|
|
927
922
|
* @param {Object} [options={}] - Read options
|
|
928
|
-
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
929
923
|
* @param {boolean} [options.resolveHolograms=true] - Whether to resolve hologram references
|
|
930
924
|
* @returns {Promise<Object|Array|null>} Data object, array of objects, or null if not found
|
|
931
925
|
* @throws {ValidationError} If holonId or lensName is invalid
|
|
932
|
-
* @throws {AuthorizationError} If capability token is invalid
|
|
933
926
|
*/
|
|
934
927
|
async read(holonId, lensName, dataId = null, options = {}) {
|
|
935
928
|
// Auto-convert to strings for convenience
|
|
@@ -990,40 +983,16 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
990
983
|
if (options.forceRelay) readOptions.forceRelay = true;
|
|
991
984
|
if (options.timeout) readOptions.timeout = options.timeout;
|
|
992
985
|
|
|
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', {
|
|
986
|
+
if (isOtherAuthor) {
|
|
987
|
+
// Reading another author's data — check federation membership
|
|
988
|
+
const federated = await this._isFederatedWith(targetPubkey);
|
|
989
|
+
if (!federated) {
|
|
990
|
+
this._log('DEBUG', '🔍 Not federated with author — returning empty', {
|
|
1016
991
|
holonId,
|
|
1017
|
-
lensName,
|
|
1018
992
|
targetPubkey: targetPubkey?.slice(0, 12) + '...'
|
|
1019
993
|
});
|
|
1020
994
|
return dataId ? null : [];
|
|
1021
995
|
}
|
|
1022
|
-
this._log('DEBUG', '✅ Capability found for federated author', {
|
|
1023
|
-
holonId,
|
|
1024
|
-
lensName,
|
|
1025
|
-
targetPubkey: targetPubkey?.slice(0, 12) + '...'
|
|
1026
|
-
});
|
|
1027
996
|
readOptions.authors = [targetPubkey];
|
|
1028
997
|
}
|
|
1029
998
|
|
|
@@ -1154,13 +1123,11 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1154
1123
|
* @param {string} dataId - ID of the data to update
|
|
1155
1124
|
* @param {Object} updates - Object containing fields to update
|
|
1156
1125
|
* @param {Object} [options={}] - Update options
|
|
1157
|
-
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
1158
1126
|
* @param {boolean} [options.validate=true] - Whether to validate against schema
|
|
1159
1127
|
* @param {boolean} [options.strict] - Override schema strict mode
|
|
1160
1128
|
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
1161
1129
|
* @returns {Promise<boolean>} True if update succeeded, false if data not found
|
|
1162
1130
|
* @throws {ValidationError} If holonId, lensName, dataId, or updates is invalid
|
|
1163
|
-
* @throws {AuthorizationError} If capability token is invalid
|
|
1164
1131
|
*/
|
|
1165
1132
|
async update(holonId, lensName, dataId, updates, options = {}) {
|
|
1166
1133
|
if (!holonId || typeof holonId !== 'string') {
|
|
@@ -1176,38 +1143,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1176
1143
|
throw new ValidationError('ValidationError: updates must be an object');
|
|
1177
1144
|
}
|
|
1178
1145
|
|
|
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
|
-
}
|
|
1146
|
+
// SIMPLIFIED: Everyone can write everywhere (Nostr is open-write).
|
|
1147
|
+
// No capability checks needed — non-federated updates are invisible on read.
|
|
1211
1148
|
|
|
1212
1149
|
const path = storage.buildPath(this.config.appName, holonId, lensName, dataId);
|
|
1213
1150
|
|
|
@@ -1320,11 +1257,9 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1320
1257
|
* @param {string} lensName - Name of the lens (data category)
|
|
1321
1258
|
* @param {string} dataId - ID of the data to delete
|
|
1322
1259
|
* @param {Object} [options={}] - Delete options
|
|
1323
|
-
* @param {string} [options.capabilityToken] - Capability token for authorization
|
|
1324
1260
|
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
1325
1261
|
* @returns {Promise<boolean>} True if deletion succeeded, false if data not found
|
|
1326
1262
|
* @throws {ValidationError} If holonId, lensName, or dataId is invalid
|
|
1327
|
-
* @throws {AuthorizationError} If not owner and no valid capability token
|
|
1328
1263
|
*/
|
|
1329
1264
|
async delete(holonId, lensName, dataId, options = {}) {
|
|
1330
1265
|
// Auto-convert to strings for convenience
|
|
@@ -1357,16 +1292,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1357
1292
|
// For pubkey-based holons, check if it's our own holon (matches effective deleter's pubkey)
|
|
1358
1293
|
// For non-pubkey holons (H3 cells, UUIDs), skip authorization for backwards compatibility
|
|
1359
1294
|
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
|
-
}
|
|
1295
|
+
// SIMPLIFIED: Everyone can write/delete everywhere (Nostr is open-write).
|
|
1296
|
+
// Non-federated deletes are effectively invisible on read.
|
|
1370
1297
|
|
|
1371
1298
|
const path = storage.buildPath(this.config.appName, holonId, lensName, dataId);
|
|
1372
1299
|
|
|
@@ -1376,7 +1303,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1376
1303
|
let dataSource = cached ? 'write-cache' : null;
|
|
1377
1304
|
|
|
1378
1305
|
if (!existingData) {
|
|
1379
|
-
// When signingKey is provided, read using the signer's pubkey as author
|
|
1380
1306
|
const readOptions = {};
|
|
1381
1307
|
if (effectiveDeleterPubKey && effectiveDeleterPubKey !== this.client?.publicKey) {
|
|
1382
1308
|
readOptions.authors = [effectiveDeleterPubKey];
|
|
@@ -1385,7 +1311,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1385
1311
|
dataSource = existingData ? 'storage' : null;
|
|
1386
1312
|
}
|
|
1387
1313
|
|
|
1388
|
-
// Return false if data doesn't exist in cache or storage
|
|
1389
1314
|
if (!existingData) {
|
|
1390
1315
|
this._log('DEBUG', '🗑️ DELETE: Data not found', { path });
|
|
1391
1316
|
return false;
|
|
@@ -1393,27 +1318,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1393
1318
|
|
|
1394
1319
|
this._log('DEBUG', '🗑️ DELETE: Found data', { path, source: dataSource });
|
|
1395
1320
|
|
|
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
1321
|
// OPTIMISTIC DELETE: Remove from write cache and add to delete cache
|
|
1418
1322
|
this._writeCache.delete(path);
|
|
1419
1323
|
this._deleteCache.add(path);
|
|
@@ -1779,7 +1683,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1779
1683
|
*/
|
|
1780
1684
|
async createHologram(sourceHolon, lensName, data, targetHolon = null) {
|
|
1781
1685
|
const target = targetHolon || sourceHolon;
|
|
1782
|
-
// Use createHologramWithCapability which auto-generates capability for self-operations
|
|
1783
1686
|
return federation.createHologramWithCapability(
|
|
1784
1687
|
this.client,
|
|
1785
1688
|
sourceHolon,
|
|
@@ -1787,11 +1690,7 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1787
1690
|
lensName,
|
|
1788
1691
|
data.id,
|
|
1789
1692
|
this.config.appName,
|
|
1790
|
-
{
|
|
1791
|
-
sourceAuthorPubKey: this.client.publicKey,
|
|
1792
|
-
targetAuthorPubKey: this.client.publicKey,
|
|
1793
|
-
permissions: ['read'],
|
|
1794
|
-
}
|
|
1693
|
+
{ sourceAuthorPubKey: this.client.publicKey }
|
|
1795
1694
|
);
|
|
1796
1695
|
}
|
|
1797
1696
|
|
|
@@ -1867,7 +1766,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1867
1766
|
async propagateData(data, sourceHolon, targetHolon, lensName, options = {}) {
|
|
1868
1767
|
// Extract mode from options, default to 'reference' for hologram creation
|
|
1869
1768
|
const mode = options.mode || 'reference';
|
|
1870
|
-
// UNIFIED MODEL: Pass sourceAuthorPubKey for capability-based federation
|
|
1871
1769
|
return federation.propagateData(
|
|
1872
1770
|
this.client,
|
|
1873
1771
|
this.config.appName,
|
|
@@ -1878,7 +1776,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
1878
1776
|
mode,
|
|
1879
1777
|
{
|
|
1880
1778
|
sourceAuthorPubKey: options.sourceAuthorPubKey || this.client.publicKey,
|
|
1881
|
-
capability: options.capability,
|
|
1882
1779
|
}
|
|
1883
1780
|
);
|
|
1884
1781
|
}
|
|
@@ -2078,12 +1975,11 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2078
1975
|
* @param {string[]} [options.lensConfig.inbound] - Lenses for inbound federation
|
|
2079
1976
|
* @param {string[]} [options.lensConfig.outbound] - Lenses for outbound federation
|
|
2080
1977
|
* @param {string} [options.partnerName] - Human-readable name for the partner holon
|
|
2081
|
-
* @param {boolean} [options.skipPropagation=false] - Skip propagating existing data
|
|
2082
1978
|
* @returns {Promise<boolean>} True if federation was established
|
|
2083
1979
|
* @throws {Error} If trying to federate a holon with itself
|
|
2084
1980
|
*/
|
|
2085
1981
|
async federateHolon(sourceHolon, targetHolon, options = {}) {
|
|
2086
|
-
const { lensConfig = { inbound: [], outbound: [] }, partnerName = null
|
|
1982
|
+
const { lensConfig = { inbound: [], outbound: [] }, partnerName = null } = options;
|
|
2087
1983
|
|
|
2088
1984
|
if (sourceHolon === targetHolon) {
|
|
2089
1985
|
throw new Error('Cannot federate a holon with itself');
|
|
@@ -2142,16 +2038,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2142
2038
|
const success = await this.writeGlobal('federation', federationData);
|
|
2143
2039
|
this.clearCache('federation');
|
|
2144
2040
|
|
|
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
2041
|
return success;
|
|
2156
2042
|
}
|
|
2157
2043
|
|
|
@@ -2176,9 +2062,15 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2176
2062
|
delete federationData.lensConfig[targetHolon];
|
|
2177
2063
|
}
|
|
2178
2064
|
|
|
2065
|
+
if (federationData.partnerNames) {
|
|
2066
|
+
delete federationData.partnerNames[targetHolon];
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2179
2069
|
const success = await this.writeGlobal('federation', federationData);
|
|
2180
2070
|
this.clearCache('federation');
|
|
2181
2071
|
|
|
2072
|
+
await registry.removeFederatedPartner(this.client, this.config.appName, targetHolon);
|
|
2073
|
+
|
|
2182
2074
|
if (success && lensConfig) {
|
|
2183
2075
|
for (const lens of (lensConfig.inbound || [])) {
|
|
2184
2076
|
await this.unfederate(targetHolon, sourceHolon, lens);
|
|
@@ -2248,26 +2140,14 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2248
2140
|
const isPubkeyHolon = typeof holonId === 'string' && /^[0-9a-f]{64}$/i.test(holonId);
|
|
2249
2141
|
const isOtherAuthor = isPubkeyHolon && holonId !== this.client.publicKey;
|
|
2250
2142
|
|
|
2251
|
-
// For other-author subscriptions,
|
|
2143
|
+
// For other-author subscriptions, check federation membership
|
|
2252
2144
|
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', {
|
|
2145
|
+
const federated = await this._isFederatedWith(holonId);
|
|
2146
|
+
if (!federated) {
|
|
2147
|
+
this._log('DEBUG', '🔍 Subscribe: not federated with author - skipping', {
|
|
2269
2148
|
holonId, lensName, targetPubkey: holonId?.slice(0, 12) + '...'
|
|
2270
2149
|
});
|
|
2150
|
+
return;
|
|
2271
2151
|
}
|
|
2272
2152
|
}
|
|
2273
2153
|
|
|
@@ -2288,6 +2168,7 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2288
2168
|
|
|
2289
2169
|
// Post-setup enhancement: include federated authors
|
|
2290
2170
|
// Own-data subscription is already active; this adds partner visibility
|
|
2171
|
+
// Create a second subscription for federated author updates
|
|
2291
2172
|
if (!isOtherAuthor) {
|
|
2292
2173
|
try {
|
|
2293
2174
|
const fedAuthorsPromise = registry.getFederatedAuthors(
|
|
@@ -2299,7 +2180,17 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2299
2180
|
);
|
|
2300
2181
|
const federatedAuthors = await Promise.race([fedAuthorsPromise, fedAuthorsTimeout]);
|
|
2301
2182
|
if (federatedAuthors.length > 0) {
|
|
2302
|
-
this._log('DEBUG', 'Subscribe: federated authors
|
|
2183
|
+
this._log('DEBUG', 'Subscribe: creating subscription for federated authors', { count: federatedAuthors.length });
|
|
2184
|
+
// Create additional subscription for federated authors' updates
|
|
2185
|
+
const fedSubscription = await subscriptions.createSubscription(
|
|
2186
|
+
this.client, path, callback, { ...subscriptionOptions, authors: federatedAuthors }
|
|
2187
|
+
);
|
|
2188
|
+
// Store reference so it can be unsubscribed later
|
|
2189
|
+
if (!unsubscribeCalled) {
|
|
2190
|
+
this.subscriptionRegistry.register(`${subscriptionId}-fed`, fedSubscription);
|
|
2191
|
+
} else {
|
|
2192
|
+
fedSubscription.unsubscribe();
|
|
2193
|
+
}
|
|
2303
2194
|
}
|
|
2304
2195
|
} catch (err) {
|
|
2305
2196
|
this._log('WARN', 'Failed to get federated authors for subscription', { error: err.message });
|
|
@@ -2316,6 +2207,8 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2316
2207
|
unsubscribeCalled = true;
|
|
2317
2208
|
if (innerSubscription) {
|
|
2318
2209
|
this.subscriptionRegistry.unregister(subscriptionId);
|
|
2210
|
+
// Also unsubscribe federated authors subscription if it exists
|
|
2211
|
+
this.subscriptionRegistry.unregister(`${subscriptionId}-fed`);
|
|
2319
2212
|
}
|
|
2320
2213
|
}
|
|
2321
2214
|
};
|
|
@@ -2376,31 +2269,6 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2376
2269
|
return crypto.verify(content, signature, publicKey);
|
|
2377
2270
|
}
|
|
2378
2271
|
|
|
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
2272
|
// === Social Protocol Operations ===
|
|
2405
2273
|
|
|
2406
2274
|
/**
|
|
@@ -2435,34 +2303,13 @@ class HoloSphereBase extends HoloSphereCore {
|
|
|
2435
2303
|
return true;
|
|
2436
2304
|
}
|
|
2437
2305
|
|
|
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
2306
|
/**
|
|
2460
2307
|
* Queries social protocol data from a holon.
|
|
2461
2308
|
*
|
|
2462
2309
|
* @param {string} holonId - H3 cell ID for the holon
|
|
2463
2310
|
* @param {Object} [options={}] - Query options
|
|
2464
2311
|
* @param {string} [options.lens='social'] - Name of the lens
|
|
2465
|
-
* @param {string} [options.protocol] - Filter by protocol ('nostr'
|
|
2312
|
+
* @param {string} [options.protocol] - Filter by protocol ('nostr' or 'all')
|
|
2466
2313
|
* @param {string} [options.type] - Filter by content type
|
|
2467
2314
|
* @param {number} [options.since] - Filter events after this timestamp
|
|
2468
2315
|
* @param {number} [options.until] - Filter events before this timestamp
|
|
@@ -2785,11 +2632,10 @@ export {
|
|
|
2785
2632
|
export { spatial, storage, schema, federation, handshake, crypto, nostrUtils, social, subscriptions, hierarchical };
|
|
2786
2633
|
|
|
2787
2634
|
// Re-export federation submodules
|
|
2788
|
-
export {
|
|
2635
|
+
export { requestCard, cardStorage, holonRegistry, registry };
|
|
2789
2636
|
|
|
2790
2637
|
// Re-export specific utilities used in tests
|
|
2791
|
-
export {
|
|
2792
|
-
export { createHologram } from './federation/hologram.js';
|
|
2638
|
+
export { createHologram, createLensHologram, isLensHologram, sendShare, sendAck } from './federation/hologram.js';
|
|
2793
2639
|
|
|
2794
2640
|
// Re-export lens encryption utilities
|
|
2795
2641
|
export { LensKeyStore, buildLensPath, parseLensPath } from './crypto/key-store.js';
|