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
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Optional feature for automated federation handshake via Nostr events
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { addFederatedPartner, storeInboundCapability } from './registry.js';
|
|
6
|
+
import { addFederatedPartner } from './registry.js';
|
|
8
7
|
|
|
9
8
|
// Custom event kinds for federation protocol
|
|
10
9
|
const FEDERATION_REQUEST_KIND = 30078;
|
|
@@ -17,28 +16,14 @@ const FEDERATION_DECLINE_KIND = 30080;
|
|
|
17
16
|
* @param {string} appname - Application namespace
|
|
18
17
|
* @param {string} targetPubKey - Target holosphere's public key
|
|
19
18
|
* @param {Object} options - Request options
|
|
20
|
-
* @param {Object} options.scope - Requested scope { holonId, lensName }
|
|
21
|
-
* @param {string[]} options.permissions - Requested permissions
|
|
22
19
|
* @param {string} options.message - Optional message to include
|
|
23
|
-
* @param {number} options.expiresIn - Token expiration in ms (default: 30 days)
|
|
24
20
|
* @returns {Promise<Object>} Published event info
|
|
25
21
|
*/
|
|
26
22
|
export async function sendFederationRequest(client, appname, targetPubKey, options = {}) {
|
|
27
23
|
const {
|
|
28
|
-
scope = { holonId: '*', lensName: '*' },
|
|
29
|
-
permissions = ['read'],
|
|
30
24
|
message = 'Federation request',
|
|
31
|
-
expiresIn = 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
32
25
|
} = options;
|
|
33
26
|
|
|
34
|
-
// Issue capability we're offering them (access to our data)
|
|
35
|
-
const offeredCapability = await issueCapability(
|
|
36
|
-
permissions,
|
|
37
|
-
scope,
|
|
38
|
-
targetPubKey,
|
|
39
|
-
{ issuerKey: client.privateKey, expiresIn }
|
|
40
|
-
);
|
|
41
|
-
|
|
42
27
|
const event = {
|
|
43
28
|
kind: FEDERATION_REQUEST_KIND,
|
|
44
29
|
created_at: Math.floor(Date.now() / 1000),
|
|
@@ -46,14 +31,9 @@ export async function sendFederationRequest(client, appname, targetPubKey, optio
|
|
|
46
31
|
['d', `federation-request-${targetPubKey.slice(0, 16)}`],
|
|
47
32
|
['p', targetPubKey],
|
|
48
33
|
['holosphere', appname],
|
|
49
|
-
['scope', JSON.stringify(scope)],
|
|
50
|
-
['permissions', permissions.join(',')],
|
|
51
34
|
],
|
|
52
35
|
content: JSON.stringify({
|
|
53
36
|
message,
|
|
54
|
-
offeredCapability,
|
|
55
|
-
requestedScope: scope,
|
|
56
|
-
requestedPermissions: permissions,
|
|
57
37
|
}),
|
|
58
38
|
};
|
|
59
39
|
|
|
@@ -61,9 +41,6 @@ export async function sendFederationRequest(client, appname, targetPubKey, optio
|
|
|
61
41
|
return {
|
|
62
42
|
eventId: result.id || null,
|
|
63
43
|
targetPubKey,
|
|
64
|
-
scope,
|
|
65
|
-
permissions,
|
|
66
|
-
offeredCapability,
|
|
67
44
|
...result,
|
|
68
45
|
};
|
|
69
46
|
}
|
|
@@ -83,18 +60,13 @@ export async function subscribeFederationRequests(client, callback) {
|
|
|
83
60
|
return client.subscribe(filter, (event) => {
|
|
84
61
|
try {
|
|
85
62
|
const content = JSON.parse(event.content);
|
|
86
|
-
const scopeTag = event.tags.find(t => t[0] === 'scope');
|
|
87
|
-
const permissionsTag = event.tags.find(t => t[0] === 'permissions');
|
|
88
63
|
const holosphereTag = event.tags.find(t => t[0] === 'holosphere');
|
|
89
64
|
|
|
90
65
|
const request = {
|
|
91
66
|
eventId: event.id,
|
|
92
67
|
requesterPubKey: event.pubkey,
|
|
93
68
|
appname: holosphereTag?.[1],
|
|
94
|
-
scope: scopeTag ? JSON.parse(scopeTag[1]) : { holonId: '*', lensName: '*' },
|
|
95
|
-
permissions: permissionsTag ? permissionsTag[1].split(',') : ['read'],
|
|
96
69
|
message: content.message,
|
|
97
|
-
offeredCapability: content.offeredCapability,
|
|
98
70
|
timestamp: event.created_at,
|
|
99
71
|
};
|
|
100
72
|
|
|
@@ -111,38 +83,16 @@ export async function subscribeFederationRequests(client, callback) {
|
|
|
111
83
|
* @param {string} appname - Application namespace
|
|
112
84
|
* @param {Object} request - Request object from subscription
|
|
113
85
|
* @param {Object} options - Acceptance options
|
|
114
|
-
* @param {Object} options.scope - Scope we're granting (defaults to requested)
|
|
115
|
-
* @param {string[]} options.permissions - Permissions we're granting
|
|
116
86
|
* @param {string} options.alias - Alias for this partner
|
|
117
|
-
* @param {number} options.expiresIn - Token expiration in ms
|
|
118
87
|
* @returns {Promise<Object>} Published acceptance event info
|
|
119
88
|
*/
|
|
120
89
|
export async function acceptFederationRequest(client, appname, request, options = {}) {
|
|
121
|
-
const {
|
|
122
|
-
scope = request.scope,
|
|
123
|
-
permissions = request.permissions,
|
|
124
|
-
alias = null,
|
|
125
|
-
expiresIn = 30 * 24 * 60 * 60 * 1000,
|
|
126
|
-
} = options;
|
|
90
|
+
const { alias = null } = options;
|
|
127
91
|
|
|
128
|
-
//
|
|
129
|
-
const grantedCapability = await issueCapability(
|
|
130
|
-
permissions,
|
|
131
|
-
scope,
|
|
132
|
-
request.requesterPubKey,
|
|
133
|
-
{ issuerKey: client.privateKey, expiresIn }
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
// Store their offered capability in our registry
|
|
92
|
+
// Add the requester to our federation
|
|
137
93
|
await addFederatedPartner(client, appname, request.requesterPubKey, {
|
|
138
94
|
alias,
|
|
139
95
|
addedVia: 'nostr_discovery',
|
|
140
|
-
inboundCapabilities: [{
|
|
141
|
-
token: request.offeredCapability,
|
|
142
|
-
scope: request.scope,
|
|
143
|
-
permissions: request.permissions,
|
|
144
|
-
expires: Date.now() + expiresIn,
|
|
145
|
-
}],
|
|
146
96
|
});
|
|
147
97
|
|
|
148
98
|
// Publish acceptance event
|
|
@@ -154,12 +104,9 @@ export async function acceptFederationRequest(client, appname, request, options
|
|
|
154
104
|
['p', request.requesterPubKey],
|
|
155
105
|
['e', request.eventId], // Reference to original request
|
|
156
106
|
['holosphere', appname],
|
|
157
|
-
['scope', JSON.stringify(scope)],
|
|
158
|
-
['permissions', permissions.join(',')],
|
|
159
107
|
],
|
|
160
108
|
content: JSON.stringify({
|
|
161
|
-
|
|
162
|
-
grantedCapability,
|
|
109
|
+
accepted: true,
|
|
163
110
|
}),
|
|
164
111
|
};
|
|
165
112
|
|
|
@@ -167,8 +114,6 @@ export async function acceptFederationRequest(client, appname, request, options
|
|
|
167
114
|
return {
|
|
168
115
|
eventId: result.id || null,
|
|
169
116
|
requesterPubKey: request.requesterPubKey,
|
|
170
|
-
grantedCapability,
|
|
171
|
-
acceptedScope: scope,
|
|
172
117
|
...result,
|
|
173
118
|
};
|
|
174
119
|
}
|
|
@@ -218,23 +163,14 @@ export async function subscribeFederationAcceptances(client, appname, callback)
|
|
|
218
163
|
|
|
219
164
|
return client.subscribe(filter, async (event) => {
|
|
220
165
|
try {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
// Store their granted capability in our registry
|
|
226
|
-
await storeInboundCapability(client, appname, event.pubkey, {
|
|
227
|
-
token: content.grantedCapability,
|
|
228
|
-
scope: scopeTag ? JSON.parse(scopeTag[1]) : content.acceptedScope,
|
|
229
|
-
permissions: permissionsTag ? permissionsTag[1].split(',') : ['read'],
|
|
230
|
-
expires: Date.now() + 30 * 24 * 60 * 60 * 1000, // Assume 30 day expiry
|
|
166
|
+
// Add the accepter to our federation
|
|
167
|
+
await addFederatedPartner(client, appname, event.pubkey, {
|
|
168
|
+
addedVia: 'nostr_discovery',
|
|
231
169
|
});
|
|
232
170
|
|
|
233
171
|
callback({
|
|
234
172
|
accepterPubKey: event.pubkey,
|
|
235
173
|
originalRequestId: event.tags.find(t => t[0] === 'e')?.[1],
|
|
236
|
-
acceptedScope: content.acceptedScope,
|
|
237
|
-
grantedCapability: content.grantedCapability,
|
|
238
174
|
timestamp: event.created_at,
|
|
239
175
|
});
|
|
240
176
|
} catch (error) {
|
|
@@ -313,14 +249,10 @@ export async function getPendingFederationRequests(client, options = {}) {
|
|
|
313
249
|
try {
|
|
314
250
|
const content = JSON.parse(request.content);
|
|
315
251
|
const pTag = request.tags.find(t => t[0] === 'p');
|
|
316
|
-
const scopeTag = request.tags.find(t => t[0] === 'scope');
|
|
317
|
-
const permissionsTag = request.tags.find(t => t[0] === 'permissions');
|
|
318
252
|
|
|
319
253
|
pending.push({
|
|
320
254
|
eventId: request.id,
|
|
321
255
|
targetPubKey: pTag?.[1],
|
|
322
|
-
scope: scopeTag ? JSON.parse(scopeTag[1]) : { holonId: '*', lensName: '*' },
|
|
323
|
-
permissions: permissionsTag ? permissionsTag[1].split(',') : ['read'],
|
|
324
256
|
message: content.message,
|
|
325
257
|
timestamp: request.created_at,
|
|
326
258
|
});
|
|
@@ -19,13 +19,8 @@ import {
|
|
|
19
19
|
} from '../crypto/nostr-utils.js';
|
|
20
20
|
import {
|
|
21
21
|
addFederatedPartner,
|
|
22
|
-
storeInboundCapability,
|
|
23
|
-
grantAccess,
|
|
24
|
-
convertLegacyLensConfig,
|
|
25
|
-
convertToLegacyLensConfig,
|
|
26
22
|
} from './registry.js';
|
|
27
23
|
import { registerHolon } from './holon-registry.js';
|
|
28
|
-
import { issueCapability } from '../crypto/secp256k1.js';
|
|
29
24
|
import * as cardStorage from './card-storage.js';
|
|
30
25
|
import { buildLensPath } from '../crypto/key-store.js';
|
|
31
26
|
|
|
@@ -116,9 +111,11 @@ export function normalizeLensConfig(lensConfig) {
|
|
|
116
111
|
return { inbound: [], outbound: [], writeInbound: [], writeOutbound: [] };
|
|
117
112
|
}
|
|
118
113
|
|
|
119
|
-
// If v2.0 format,
|
|
114
|
+
// If v2.0 format, extract lens names into v1.0 shape
|
|
120
115
|
if (isV2LensConfig(lensConfig)) {
|
|
121
|
-
|
|
116
|
+
const perms = lensConfig.permissions || {};
|
|
117
|
+
const outbound = Object.keys(perms);
|
|
118
|
+
return { inbound: [], outbound, writeInbound: [], writeOutbound: [] };
|
|
122
119
|
}
|
|
123
120
|
|
|
124
121
|
// Already v1.0 format, just ensure all fields exist
|
|
@@ -148,7 +145,14 @@ export function toUnifiedPermissions(lensConfig) {
|
|
|
148
145
|
}
|
|
149
146
|
|
|
150
147
|
// Convert v1.0 to v2.0
|
|
151
|
-
|
|
148
|
+
const permissions = {};
|
|
149
|
+
for (const lens of (lensConfig.outbound || [])) {
|
|
150
|
+
permissions[lens] = ['read'];
|
|
151
|
+
}
|
|
152
|
+
for (const lens of (lensConfig.writeOutbound || [])) {
|
|
153
|
+
permissions[lens] = permissions[lens] ? [...permissions[lens], 'write'] : ['write'];
|
|
154
|
+
}
|
|
155
|
+
return { version: '2.0', permissions };
|
|
152
156
|
}
|
|
153
157
|
|
|
154
158
|
// ============================================================================
|
|
@@ -364,7 +368,7 @@ export async function sendLensKeyShare(client, privateKey, recipientPubKey, keyS
|
|
|
364
368
|
* @returns {Function} Unsubscribe function
|
|
365
369
|
*/
|
|
366
370
|
export function subscribeToFederationDMs(client, privateKey, publicKey, handlers, options = {}) {
|
|
367
|
-
const { onRequest, onResponse, onUpdate, onUpdateResponse, onKeyShare } = handlers;
|
|
371
|
+
const { onRequest, onResponse, onUpdate, onUpdateResponse, onKeyShare, onShare, onAck } = handlers;
|
|
368
372
|
const { appname } = options;
|
|
369
373
|
let isActive = true;
|
|
370
374
|
|
|
@@ -516,6 +520,38 @@ export function subscribeToFederationDMs(client, privateKey, publicKey, handlers
|
|
|
516
520
|
|
|
517
521
|
console.log('[Handshake] Received lens key share from:', event.pubkey.substring(0, 8) + '...', 'for lens:', payload.lensPath);
|
|
518
522
|
onKeyShare?.(payload, event.pubkey);
|
|
523
|
+
} else if (payload.type === 'share') {
|
|
524
|
+
// New share protocol — replaces federation_request + lens_key_share for cross-author
|
|
525
|
+
const shareKey = `share_${payload.id}_${event.pubkey}`;
|
|
526
|
+
if (processedResponseIds.has(shareKey)) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (appname && client?.client) {
|
|
531
|
+
await cardStorage.markDMProcessed(client.client, appname, event.id, 'share');
|
|
532
|
+
processedResponseIds.add(shareKey);
|
|
533
|
+
processedDMIds.add(event.id);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
console.log('[Handshake] Received share from:', event.pubkey.substring(0, 8) + '...',
|
|
537
|
+
'lens:', payload.lens, 'item:', payload.item || '(lens-level)');
|
|
538
|
+
onShare?.(payload, event.pubkey);
|
|
539
|
+
} else if (payload.type === 'share_ack') {
|
|
540
|
+
// New share ack protocol — replaces federation_response
|
|
541
|
+
const ackKey = `ack_${payload.id}_${event.pubkey}`;
|
|
542
|
+
if (processedResponseIds.has(ackKey)) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (appname && client?.client) {
|
|
547
|
+
await cardStorage.markDMProcessed(client.client, appname, event.id, 'share_ack');
|
|
548
|
+
processedResponseIds.add(ackKey);
|
|
549
|
+
processedDMIds.add(event.id);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
console.log('[Handshake] Received share ack from:', event.pubkey.substring(0, 8) + '...',
|
|
553
|
+
'status:', payload.status);
|
|
554
|
+
onAck?.(payload, event.pubkey);
|
|
519
555
|
}
|
|
520
556
|
} catch (error) {
|
|
521
557
|
// Silently ignore non-federation DMs
|
|
@@ -614,65 +650,21 @@ export async function initiateFederationHandshake(holosphere, privateKey, params
|
|
|
614
650
|
writeOutbound: lensConfig.writeOutbound || [],
|
|
615
651
|
};
|
|
616
652
|
|
|
617
|
-
//
|
|
618
|
-
const capabilities =
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
{ holonId, lensName, dataId: '*' },
|
|
624
|
-
partnerPubKey,
|
|
625
|
-
{
|
|
626
|
-
expiresIn: 365 * 24 * 60 * 60 * 1000, // 1 year
|
|
627
|
-
issuer: senderPubKey,
|
|
628
|
-
issuerKey: privateKey,
|
|
629
|
-
}
|
|
630
|
-
);
|
|
631
|
-
capabilities.push({
|
|
632
|
-
token,
|
|
633
|
-
scope: { holonId, lensName },
|
|
634
|
-
permissions: ['read'],
|
|
635
|
-
});
|
|
636
|
-
} catch (err) {
|
|
637
|
-
console.warn(`[Handshake] Failed to issue capability for ${lensName}:`, err.message);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
653
|
+
// Build stub capabilities for outbound lenses (simplified model)
|
|
654
|
+
const capabilities = normalizedLensConfig.outbound.map(lensName => ({
|
|
655
|
+
token: 'federation-member',
|
|
656
|
+
scope: { holonId, lensName },
|
|
657
|
+
permissions: ['read'],
|
|
658
|
+
}));
|
|
640
659
|
|
|
641
|
-
//
|
|
642
|
-
// This allows the partner to write to these lenses in our holon
|
|
660
|
+
// Add partner to our federation
|
|
643
661
|
if (holosphere.client) {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
partnerPubKey,
|
|
651
|
-
{ holonId: '*', lensName },
|
|
652
|
-
['write'],
|
|
653
|
-
{ direction: 'inbound' }
|
|
654
|
-
);
|
|
655
|
-
console.log(`[Handshake] Pre-granted write access to partner for lens "${lensName}"`);
|
|
656
|
-
} catch (err) {
|
|
657
|
-
console.warn(`[Handshake] Failed to grant write access for ${lensName}:`, err.message);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// Also grant read access for outbound lenses using unified format
|
|
662
|
-
for (const lensName of normalizedLensConfig.outbound) {
|
|
663
|
-
try {
|
|
664
|
-
await grantAccess(
|
|
665
|
-
holosphere.client,
|
|
666
|
-
holosphere.config.appName,
|
|
667
|
-
partnerPubKey,
|
|
668
|
-
{ holonId: '*', lensName },
|
|
669
|
-
['read'],
|
|
670
|
-
{ direction: 'outbound' }
|
|
671
|
-
);
|
|
672
|
-
} catch (err) {
|
|
673
|
-
console.warn(`[Handshake] Failed to grant read access for ${lensName}:`, err.message);
|
|
674
|
-
}
|
|
675
|
-
}
|
|
662
|
+
await addFederatedPartner(
|
|
663
|
+
holosphere.client,
|
|
664
|
+
holosphere.config.appName,
|
|
665
|
+
partnerPubKey,
|
|
666
|
+
{ addedVia: 'handshake' }
|
|
667
|
+
);
|
|
676
668
|
}
|
|
677
669
|
|
|
678
670
|
// Create federation request
|
|
@@ -749,55 +741,6 @@ export async function acceptFederationRequest(holosphere, privateKey, params) {
|
|
|
749
741
|
addedVia: 'handshake_accepted',
|
|
750
742
|
});
|
|
751
743
|
|
|
752
|
-
// Store any capabilities they sent
|
|
753
|
-
if (request.capabilities && request.capabilities.length > 0) {
|
|
754
|
-
for (const cap of request.capabilities) {
|
|
755
|
-
await storeInboundCapability(holosphere.client, holosphere.config.appName, senderPubKey, cap);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// Process write grants from sender (they offered writeOutbound = we get write access)
|
|
760
|
-
const senderWriteOutbound = request.lensConfig?.writeOutbound || [];
|
|
761
|
-
if (senderWriteOutbound.length > 0) {
|
|
762
|
-
console.log('[Handshake] Sender is granting write access for lenses:', senderWriteOutbound);
|
|
763
|
-
// Note: This is tracked on the sender's side - we just acknowledge it in the response
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
// Grant write access to sender for our writeOutbound lenses
|
|
767
|
-
// This allows the sender to write to these lenses in our holon
|
|
768
|
-
for (const lensName of normalizedLensConfig.writeOutbound) {
|
|
769
|
-
try {
|
|
770
|
-
// New unified grant
|
|
771
|
-
await grantAccess(
|
|
772
|
-
holosphere.client,
|
|
773
|
-
holosphere.config.appName,
|
|
774
|
-
senderPubKey,
|
|
775
|
-
{ holonId: '*', lensName },
|
|
776
|
-
['write'],
|
|
777
|
-
{ direction: 'inbound' }
|
|
778
|
-
);
|
|
779
|
-
console.log(`[Handshake] Granted write access to sender for lens "${lensName}"`);
|
|
780
|
-
} catch (err) {
|
|
781
|
-
console.warn(`[Handshake] Failed to grant write access for ${lensName}:`, err.message);
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// Also grant read access for outbound lenses using unified format
|
|
786
|
-
for (const lensName of normalizedLensConfig.outbound) {
|
|
787
|
-
try {
|
|
788
|
-
await grantAccess(
|
|
789
|
-
holosphere.client,
|
|
790
|
-
holosphere.config.appName,
|
|
791
|
-
senderPubKey,
|
|
792
|
-
{ holonId: '*', lensName },
|
|
793
|
-
['read'],
|
|
794
|
-
{ direction: 'outbound' }
|
|
795
|
-
);
|
|
796
|
-
} catch (err) {
|
|
797
|
-
console.warn(`[Handshake] Failed to grant read access for ${lensName}:`, err.message);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
|
|
801
744
|
// Register the sender's holon -> pubkey mapping in our holon registry
|
|
802
745
|
// This is critical for resolveHolonToPubkey to work when navigating to their holon
|
|
803
746
|
if (request.senderHolonId) {
|
|
@@ -811,7 +754,7 @@ export async function acceptFederationRequest(holosphere, privateKey, params) {
|
|
|
811
754
|
}
|
|
812
755
|
}
|
|
813
756
|
|
|
814
|
-
// Create the actual federation record in
|
|
757
|
+
// Create the actual federation record in Nostr storage
|
|
815
758
|
// This is what makes the partner appear in getFederation() / loadFederationData()
|
|
816
759
|
// Store the sender's name so we can display it without reading their settings
|
|
817
760
|
await holosphere.federateHolon(holonId, senderPubKey, {
|
|
@@ -819,29 +762,12 @@ export async function acceptFederationRequest(holosphere, privateKey, params) {
|
|
|
819
762
|
partnerName: request.senderHolonName
|
|
820
763
|
});
|
|
821
764
|
|
|
822
|
-
//
|
|
823
|
-
const capabilities =
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
{ holonId, lensName, dataId: '*' },
|
|
829
|
-
senderPubKey,
|
|
830
|
-
{
|
|
831
|
-
expiresIn: 365 * 24 * 60 * 60 * 1000, // 1 year
|
|
832
|
-
issuer: responderPubKey,
|
|
833
|
-
issuerKey: privateKey,
|
|
834
|
-
}
|
|
835
|
-
);
|
|
836
|
-
capabilities.push({
|
|
837
|
-
token,
|
|
838
|
-
scope: { holonId, lensName },
|
|
839
|
-
permissions: ['read'],
|
|
840
|
-
});
|
|
841
|
-
} catch (err) {
|
|
842
|
-
console.warn(`[Handshake] Failed to issue capability for ${lensName}:`, err.message);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
765
|
+
// Build stub capabilities for outbound lenses (simplified model)
|
|
766
|
+
const capabilities = normalizedLensConfig.outbound.map(lensName => ({
|
|
767
|
+
token: 'federation-member',
|
|
768
|
+
scope: { holonId, lensName },
|
|
769
|
+
permissions: ['read'],
|
|
770
|
+
}));
|
|
845
771
|
|
|
846
772
|
// Share lens keys for encrypted outbound lenses
|
|
847
773
|
// This allows the partner to decrypt data from our shared lenses
|
|
@@ -895,37 +821,7 @@ export async function acceptFederationRequest(holosphere, privateKey, params) {
|
|
|
895
821
|
console.log('[Handshake] Request dismissed after acceptance:', request.requestId);
|
|
896
822
|
}
|
|
897
823
|
|
|
898
|
-
|
|
899
|
-
// This enables the user to immediately see the partner's data in their holon
|
|
900
|
-
const receivedHolograms = {};
|
|
901
|
-
const inboundLenses = normalizedLensConfig.inbound;
|
|
902
|
-
|
|
903
|
-
if (inboundLenses.length > 0 && holosphere.receiveFederatedLens) {
|
|
904
|
-
console.log('[Handshake] Receiving federated lens data as holograms...');
|
|
905
|
-
|
|
906
|
-
for (const lensName of inboundLenses) {
|
|
907
|
-
try {
|
|
908
|
-
// The sender's holon ID comes from the request
|
|
909
|
-
const senderHolonId = request.senderHolonId;
|
|
910
|
-
|
|
911
|
-
const result = await holosphere.receiveFederatedLens(
|
|
912
|
-
senderPubKey,
|
|
913
|
-
senderHolonId,
|
|
914
|
-
lensName,
|
|
915
|
-
holonId,
|
|
916
|
-
{ overwrite: false }
|
|
917
|
-
);
|
|
918
|
-
|
|
919
|
-
receivedHolograms[lensName] = result;
|
|
920
|
-
console.log(`[Handshake] Received ${result.received} holograms for lens "${lensName}"`);
|
|
921
|
-
} catch (err) {
|
|
922
|
-
console.warn(`[Handshake] Failed to receive holograms for lens "${lensName}":`, err.message);
|
|
923
|
-
receivedHolograms[lensName] = { error: err.message };
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
return { success: true, requestId: request.requestId, receivedHolograms };
|
|
824
|
+
return { success: true, requestId: request.requestId };
|
|
929
825
|
} else {
|
|
930
826
|
return { success: false, error: 'Failed to send response DM' };
|
|
931
827
|
}
|
|
@@ -970,23 +866,20 @@ export async function rejectFederationRequest(holosphere, privateKey, params) {
|
|
|
970
866
|
* @param {Object} response - Federation response payload
|
|
971
867
|
* @param {string} responderPubKey - Responder's public key
|
|
972
868
|
* @param {Object} [options={}] - Processing options
|
|
973
|
-
* @
|
|
974
|
-
* @param {string[]} [options.inboundLenses] - Lenses to receive from responder
|
|
975
|
-
* @returns {Promise<Object>} Result with success, stored capability count, and received holograms
|
|
869
|
+
* @returns {Promise<Object>} Result with success and stored capability count
|
|
976
870
|
*/
|
|
977
871
|
export async function processFederationResponse(holosphere, response, responderPubKey, options = {}) {
|
|
978
872
|
if (!response || response.status !== 'accepted') {
|
|
979
873
|
return { success: false, error: 'Response not accepted', storedCapabilities: 0 };
|
|
980
874
|
}
|
|
981
875
|
|
|
982
|
-
const { holonId, inboundLenses = [] } = options;
|
|
983
876
|
let storedCapabilities = 0;
|
|
984
877
|
|
|
985
878
|
// Store capabilities from the responder
|
|
986
879
|
if (holosphere.client && response.capabilities && response.capabilities.length > 0) {
|
|
987
880
|
for (const cap of response.capabilities) {
|
|
988
881
|
try {
|
|
989
|
-
|
|
882
|
+
// Simplified: capabilities are not stored, partner is already federated
|
|
990
883
|
storedCapabilities++;
|
|
991
884
|
} catch (err) {
|
|
992
885
|
console.warn(`[Handshake] Failed to store capability:`, err.message);
|
|
@@ -1014,33 +907,7 @@ export async function processFederationResponse(holosphere, response, responderP
|
|
|
1014
907
|
}
|
|
1015
908
|
}
|
|
1016
909
|
|
|
1017
|
-
|
|
1018
|
-
const receivedHolograms = {};
|
|
1019
|
-
const responderHolonId = response.responderHolonId;
|
|
1020
|
-
|
|
1021
|
-
if (holonId && responderHolonId && inboundLenses.length > 0 && holosphere.receiveFederatedLens) {
|
|
1022
|
-
console.log('[Handshake] Receiving federated lens data as holograms from responder...');
|
|
1023
|
-
|
|
1024
|
-
for (const lensName of inboundLenses) {
|
|
1025
|
-
try {
|
|
1026
|
-
const result = await holosphere.receiveFederatedLens(
|
|
1027
|
-
responderPubKey,
|
|
1028
|
-
responderHolonId,
|
|
1029
|
-
lensName,
|
|
1030
|
-
holonId,
|
|
1031
|
-
{ overwrite: false }
|
|
1032
|
-
);
|
|
1033
|
-
|
|
1034
|
-
receivedHolograms[lensName] = result;
|
|
1035
|
-
console.log(`[Handshake] Received ${result.received} holograms for lens "${lensName}"`);
|
|
1036
|
-
} catch (err) {
|
|
1037
|
-
console.warn(`[Handshake] Failed to receive holograms for lens "${lensName}":`, err.message);
|
|
1038
|
-
receivedHolograms[lensName] = { error: err.message };
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
return { success: true, storedCapabilities, receivedHolograms };
|
|
910
|
+
return { success: true, storedCapabilities };
|
|
1044
911
|
}
|
|
1045
912
|
|
|
1046
913
|
// ============================================================================
|