holosphere 2.0.0-alpha11 → 2.0.0-alpha13
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/dist/{2019-D2OG2idw.js → 2019-CLMqIAfQ.js} +1722 -1668
- package/dist/{2019-D2OG2idw.js.map → 2019-CLMqIAfQ.js.map} +1 -1
- package/dist/2019-Cp3uYhyY.cjs +8 -0
- package/dist/{2019-EION3wKo.cjs.map → 2019-Cp3uYhyY.cjs.map} +1 -1
- package/dist/browser-D6cNVl0v.cjs +2 -0
- package/dist/{browser-Cq59Ij19.cjs.map → browser-D6cNVl0v.cjs.map} +1 -1
- package/dist/{browser-BSniCNqO.js → browser-nUQt1cnB.js} +2 -2
- package/dist/{browser-BSniCNqO.js.map → browser-nUQt1cnB.js.map} +1 -1
- package/dist/cjs/holosphere.cjs +1 -1
- package/dist/esm/holosphere.js +67 -50
- package/dist/{index-D-jZhliX.js → index-BN_uoxQK.js} +20324 -735
- package/dist/index-BN_uoxQK.js.map +1 -0
- package/dist/{index-Bl6rM1NW.js → index-CoAjtqsD.js} +2 -2
- package/dist/{index-Bl6rM1NW.js.map → index-CoAjtqsD.js.map} +1 -1
- package/dist/{index-Bwg3OzRM.cjs → index-Cp3tI53z.cjs} +3 -3
- package/dist/{index-Bwg3OzRM.cjs.map → index-Cp3tI53z.cjs.map} +1 -1
- package/dist/index-DJjGSwXG.cjs +13 -0
- package/dist/index-DJjGSwXG.cjs.map +1 -0
- package/dist/index-V8EHMYEY.cjs +29 -0
- package/dist/index-V8EHMYEY.cjs.map +1 -0
- package/dist/index-Z5TstN1e.js +11663 -0
- package/dist/index-Z5TstN1e.js.map +1 -0
- package/dist/indexeddb-storage-CZK5A7XH.cjs +2 -0
- package/dist/indexeddb-storage-CZK5A7XH.cjs.map +1 -0
- package/dist/{indexeddb-storage-5eiUNsHC.js → indexeddb-storage-bpA01pAU.js} +39 -2
- package/dist/indexeddb-storage-bpA01pAU.js.map +1 -0
- package/dist/{memory-storage-DMt36uZO.cjs → memory-storage-B1k8Jszd.cjs} +2 -2
- package/dist/{memory-storage-DMt36uZO.cjs.map → memory-storage-B1k8Jszd.cjs.map} +1 -1
- package/dist/{memory-storage-CI-gfmuG.js → memory-storage-BqhmytP_.js} +2 -2
- package/dist/{memory-storage-CI-gfmuG.js.map → memory-storage-BqhmytP_.js.map} +1 -1
- package/docs/FEDERATION.md +474 -0
- package/package.json +3 -1
- package/src/crypto/nostr-utils.js +7 -0
- package/src/crypto/secp256k1.js +104 -38
- package/src/federation/capabilities.js +162 -0
- package/src/federation/card-storage.js +376 -0
- package/src/federation/handshake.js +561 -9
- package/src/federation/hologram.js +194 -57
- package/src/federation/holon-registry.js +187 -0
- package/src/federation/index.js +68 -0
- package/src/federation/registry.js +164 -6
- package/src/federation/request-card.js +373 -0
- package/src/hierarchical/upcast.js +19 -3
- package/src/index.js +209 -75
- package/src/lib/federation-methods.js +527 -5
- package/src/storage/indexeddb-storage.js +41 -0
- package/src/storage/nostr-async.js +14 -5
- package/src/storage/nostr-client.js +471 -155
- package/src/storage/nostr-wrapper.js +6 -3
- package/dist/2019-EION3wKo.cjs +0 -8
- package/dist/_commonjsHelpers-C37NGDzP.cjs +0 -2
- package/dist/_commonjsHelpers-C37NGDzP.cjs.map +0 -1
- package/dist/_commonjsHelpers-CUmg6egw.js +0 -7
- package/dist/_commonjsHelpers-CUmg6egw.js.map +0 -1
- package/dist/browser-Cq59Ij19.cjs +0 -2
- package/dist/index-D-jZhliX.js.map +0 -1
- package/dist/index-Dc6Z8Aob.cjs +0 -18
- package/dist/index-Dc6Z8Aob.cjs.map +0 -1
- package/dist/indexeddb-storage-5eiUNsHC.js.map +0 -1
- package/dist/indexeddb-storage-FNFUVvTJ.cjs +0 -2
- package/dist/indexeddb-storage-FNFUVvTJ.cjs.map +0 -1
- package/dist/secp256k1-CEwJNcfV.js +0 -1890
- package/dist/secp256k1-CEwJNcfV.js.map +0 -1
- package/dist/secp256k1-CiEONUnj.cjs +0 -12
- package/dist/secp256k1-CiEONUnj.cjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "holosphere",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-alpha13",
|
|
4
4
|
"description": "Holonic geospatial communication infrastructure combining H3 hexagonal indexing with distributed P2P storage",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@noble/curves": "^1.3.0",
|
|
57
|
+
"@nostr-dev-kit/ndk": "^2.10.0",
|
|
57
58
|
"ajv": "^8.12.0",
|
|
58
59
|
"dotenv": "^17.2.3",
|
|
59
60
|
"ethers": "^6.13.4",
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
"ws": "^8.18.3"
|
|
65
66
|
},
|
|
66
67
|
"optionalDependencies": {
|
|
68
|
+
"@nostr-dev-kit/ndk-cache-dexie": "^2.5.0",
|
|
67
69
|
"gun": "^0.2020.1241"
|
|
68
70
|
},
|
|
69
71
|
"devDependencies": {
|
|
@@ -2,11 +2,18 @@
|
|
|
2
2
|
* @fileoverview Browser-compatible Nostr utility functions for key handling, encryption, and event management.
|
|
3
3
|
* Provides NIP-04 (legacy) and NIP-44 (modern, audited) encryption support, key conversion utilities,
|
|
4
4
|
* and event creation helpers. Applications can use these utilities without directly importing nostr-tools.
|
|
5
|
+
*
|
|
6
|
+
* This module uses nostr-tools for cryptographic operations while the main client uses NDK.
|
|
7
|
+
* Both libraries are compatible and can be used together.
|
|
8
|
+
*
|
|
5
9
|
* @module crypto/nostr-utils
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
12
|
import { nip04, nip44, nip19, getPublicKey as nostrGetPublicKey, finalizeEvent, verifyEvent as nostrVerifyEvent } from 'nostr-tools';
|
|
9
13
|
|
|
14
|
+
// Re-export NDK types for consumers who want to use NDK directly
|
|
15
|
+
export { default as NDK, NDKEvent, NDKPrivateKeySigner, NDKUser, NDKSubscriptionCacheUsage } from '@nostr-dev-kit/ndk';
|
|
16
|
+
|
|
10
17
|
// ============================================================================
|
|
11
18
|
// Key Conversion Utilities
|
|
12
19
|
// ============================================================================
|
package/src/crypto/secp256k1.js
CHANGED
|
@@ -10,29 +10,15 @@
|
|
|
10
10
|
|
|
11
11
|
import { sha256 } from '@noble/hashes/sha256';
|
|
12
12
|
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
|
|
13
|
-
|
|
14
|
-
let secp256k1 = null;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Lazy load secp256k1 module
|
|
18
|
-
* @private
|
|
19
|
-
*/
|
|
20
|
-
async function loadCrypto() {
|
|
21
|
-
if (!secp256k1) {
|
|
22
|
-
const module = await import('@noble/curves/secp256k1');
|
|
23
|
-
secp256k1 = module.secp256k1;
|
|
24
|
-
}
|
|
25
|
-
return secp256k1;
|
|
26
|
-
}
|
|
13
|
+
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
27
14
|
|
|
28
15
|
/**
|
|
29
16
|
* Get public key from private key
|
|
30
17
|
* @param {string} privateKey - Private key (hex string)
|
|
31
|
-
* @returns {
|
|
18
|
+
* @returns {string} Public key (hex string)
|
|
32
19
|
*/
|
|
33
|
-
export
|
|
34
|
-
const
|
|
35
|
-
const pubKey = crypto.getPublicKey(privateKey);
|
|
20
|
+
export function getPublicKey(privateKey) {
|
|
21
|
+
const pubKey = secp256k1.getPublicKey(privateKey);
|
|
36
22
|
return bytesToHex(pubKey);
|
|
37
23
|
}
|
|
38
24
|
|
|
@@ -44,13 +30,11 @@ export async function getPublicKey(privateKey) {
|
|
|
44
30
|
*/
|
|
45
31
|
export async function sign(content, privateKey) {
|
|
46
32
|
try {
|
|
47
|
-
const crypto = await loadCrypto();
|
|
48
|
-
|
|
49
33
|
// Hash content
|
|
50
34
|
const msgHash = hashContent(content);
|
|
51
35
|
|
|
52
36
|
// Sign - secp256k1.sign returns Signature object
|
|
53
|
-
const signature =
|
|
37
|
+
const signature = secp256k1.sign(msgHash, privateKey);
|
|
54
38
|
return signature.toCompactHex();
|
|
55
39
|
} catch (error) {
|
|
56
40
|
throw new Error(`Signature generation failed: ${error.message}`);
|
|
@@ -71,10 +55,8 @@ export async function verify(content, signature, publicKey) {
|
|
|
71
55
|
throw new Error('Invalid public key format');
|
|
72
56
|
}
|
|
73
57
|
|
|
74
|
-
const crypto = await loadCrypto();
|
|
75
|
-
|
|
76
58
|
const msgHash = hashContent(content);
|
|
77
|
-
const isValid =
|
|
59
|
+
const isValid = secp256k1.verify(signature, msgHash, publicKey);
|
|
78
60
|
return isValid;
|
|
79
61
|
} catch (error) {
|
|
80
62
|
// Invalid signatures or keys throw errors
|
|
@@ -127,18 +109,24 @@ export function matchScope(tokenScope, requestedScope) {
|
|
|
127
109
|
}
|
|
128
110
|
|
|
129
111
|
/**
|
|
130
|
-
* Issue capability token
|
|
112
|
+
* Issue capability token - UNIFIED MODEL
|
|
113
|
+
*
|
|
114
|
+
* Supports both self-capabilities (issuer === recipient) and cross-capabilities.
|
|
115
|
+
* Self-capabilities are used for same-author federation, providing consistent
|
|
116
|
+
* security model across all federation types.
|
|
117
|
+
*
|
|
131
118
|
* @param {string[]} permissions - Permissions array (e.g., ['read', 'write', 'delete'])
|
|
132
119
|
* @param {Object|string} scope - Scope (holon/lens path or object). Supports wildcards: { holonId: "*", lensName: "*" }
|
|
133
|
-
* @param {string} recipient - Recipient public key
|
|
120
|
+
* @param {string} recipient - Recipient public key (can be same as issuer for self-capability)
|
|
134
121
|
* @param {Object} options - Options
|
|
135
|
-
* @param {number} options.expiresIn - Expiration in milliseconds (default: 1 hour)
|
|
136
|
-
* @param {string} options.issuer - Issuer ID
|
|
122
|
+
* @param {number} options.expiresIn - Expiration in milliseconds (default: 1 hour, longer for self-caps)
|
|
123
|
+
* @param {string} options.issuer - Issuer ID/public key
|
|
137
124
|
* @param {string} options.issuerKey - Issuer private key for signing
|
|
125
|
+
* @param {boolean} options.isSelfCapability - If true, marks as self-capability (auto-detected if issuer === recipient)
|
|
138
126
|
* @returns {Promise<string>} Capability token (base64-encoded JWT-like)
|
|
139
127
|
*/
|
|
140
128
|
export async function issueCapability(permissions, scope, recipient, options = {}) {
|
|
141
|
-
const { expiresIn = 3600000, issuer = 'holosphere', issuerKey } = options;
|
|
129
|
+
const { expiresIn = 3600000, issuer = 'holosphere', issuerKey, isSelfCapability } = options;
|
|
142
130
|
|
|
143
131
|
// Validate permissions
|
|
144
132
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
@@ -165,12 +153,16 @@ export async function issueCapability(permissions, scope, recipient, options = {
|
|
|
165
153
|
throw new Error('Invalid issuer key');
|
|
166
154
|
}
|
|
167
155
|
|
|
156
|
+
// Detect self-capability (issuer === recipient)
|
|
157
|
+
const selfCap = isSelfCapability !== undefined ? isSelfCapability : (issuer === recipient);
|
|
158
|
+
|
|
168
159
|
const token = {
|
|
169
160
|
type: 'capability',
|
|
170
161
|
permissions,
|
|
171
162
|
scope,
|
|
172
163
|
recipient,
|
|
173
164
|
issuer,
|
|
165
|
+
isSelfCapability: selfCap, // Mark self-capabilities for clarity
|
|
174
166
|
nonce: generateNonce(),
|
|
175
167
|
issued: Date.now(),
|
|
176
168
|
expires: Date.now() + expiresIn,
|
|
@@ -191,6 +183,31 @@ export async function issueCapability(permissions, scope, recipient, options = {
|
|
|
191
183
|
return encoded;
|
|
192
184
|
}
|
|
193
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Issue a self-capability token - UNIFIED MODEL
|
|
188
|
+
*
|
|
189
|
+
* Convenience method for creating self-capabilities (same author federation).
|
|
190
|
+
* Self-capabilities have longer expiration by default (1 year).
|
|
191
|
+
*
|
|
192
|
+
* @param {string[]} permissions - Permissions array
|
|
193
|
+
* @param {Object} scope - Scope object { holonId, lensName, dataId? }
|
|
194
|
+
* @param {string} authorPubKey - Author's public key (both issuer and recipient)
|
|
195
|
+
* @param {Object} options - Options
|
|
196
|
+
* @param {string} options.privateKey - Author's private key for signing
|
|
197
|
+
* @param {number} [options.expiresIn=31536000000] - Expiration in ms (default: 1 year)
|
|
198
|
+
* @returns {Promise<string>} Self-capability token
|
|
199
|
+
*/
|
|
200
|
+
export async function issueSelfCapability(permissions, scope, authorPubKey, options = {}) {
|
|
201
|
+
const { privateKey, expiresIn = 365 * 24 * 60 * 60 * 1000 } = options; // 1 year default
|
|
202
|
+
|
|
203
|
+
return issueCapability(permissions, scope, authorPubKey, {
|
|
204
|
+
expiresIn,
|
|
205
|
+
issuer: authorPubKey,
|
|
206
|
+
issuerKey: privateKey,
|
|
207
|
+
isSelfCapability: true,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
194
211
|
/**
|
|
195
212
|
* Verify capability token
|
|
196
213
|
* @param {string|Object} token - Capability token (string or object)
|
|
@@ -202,41 +219,90 @@ export async function verifyCapability(token, requiredPermission, scope) {
|
|
|
202
219
|
try {
|
|
203
220
|
let tokenObj;
|
|
204
221
|
|
|
222
|
+
// Handle capability object wrapper { token, scope, permissions }
|
|
223
|
+
// This normalizes both formats: raw token string and capability info object
|
|
224
|
+
if (token && typeof token === 'object' && token.token) {
|
|
225
|
+
token = token.token; // Extract the actual token string
|
|
226
|
+
}
|
|
227
|
+
|
|
205
228
|
// Decode if string
|
|
206
229
|
if (typeof token === 'string') {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
230
|
+
// Check if it's a base64-encoded token (starts with "ey" for JSON base64)
|
|
231
|
+
if (token.startsWith('ey') || (!token.includes('.') && !token.startsWith('{'))) {
|
|
232
|
+
// Base64 encoded (with or without signature)
|
|
233
|
+
try {
|
|
234
|
+
const payload = token.includes('.') ? token.split('.')[0] : token;
|
|
235
|
+
const decoded = Buffer.from ?
|
|
236
|
+
Buffer.from(payload, 'base64').toString('utf8') :
|
|
237
|
+
atob(payload);
|
|
238
|
+
tokenObj = JSON.parse(decoded);
|
|
239
|
+
} catch (e) {
|
|
240
|
+
console.log('[verifyCapability] ❌ Token is not valid base64 JSON:', {
|
|
241
|
+
preview: token.substring(0, 50),
|
|
242
|
+
error: e.message
|
|
243
|
+
});
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
} else if (token.startsWith('{')) {
|
|
247
|
+
// Already JSON string
|
|
248
|
+
try {
|
|
249
|
+
tokenObj = JSON.parse(token);
|
|
250
|
+
} catch (e) {
|
|
251
|
+
console.log('[verifyCapability] ❌ Token is not valid JSON:', {
|
|
252
|
+
preview: token.substring(0, 50),
|
|
253
|
+
error: e.message
|
|
254
|
+
});
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
console.log('[verifyCapability] ❌ Unknown token format:', token.substring(0, 50));
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
} else if (token && typeof token === 'object') {
|
|
262
|
+
// Already decoded token object
|
|
216
263
|
tokenObj = token;
|
|
264
|
+
} else {
|
|
265
|
+
console.log('[verifyCapability] ❌ Invalid token:', typeof token);
|
|
266
|
+
return false;
|
|
217
267
|
}
|
|
218
268
|
|
|
219
269
|
if (!tokenObj || tokenObj.type !== 'capability') {
|
|
270
|
+
console.log('[verifyCapability] ❌ Invalid token type:', { type: tokenObj?.type, tokenObj });
|
|
220
271
|
return false;
|
|
221
272
|
}
|
|
222
273
|
|
|
223
274
|
// Check expiration
|
|
224
275
|
if (Date.now() > tokenObj.expires) {
|
|
276
|
+
console.log('[verifyCapability] ❌ Token expired:', {
|
|
277
|
+
expires: tokenObj.expires,
|
|
278
|
+
now: Date.now(),
|
|
279
|
+
expiredAgo: `${(Date.now() - tokenObj.expires) / 1000}s ago`
|
|
280
|
+
});
|
|
225
281
|
return false;
|
|
226
282
|
}
|
|
227
283
|
|
|
228
284
|
// Check scope using matchScope (supports wildcards)
|
|
229
285
|
if (!matchScope(tokenObj.scope, scope)) {
|
|
286
|
+
console.log('[verifyCapability] ❌ Scope mismatch:', {
|
|
287
|
+
tokenScope: tokenObj.scope,
|
|
288
|
+
requestedScope: scope
|
|
289
|
+
});
|
|
230
290
|
return false;
|
|
231
291
|
}
|
|
232
292
|
|
|
233
293
|
// Check permission
|
|
234
294
|
if (!tokenObj.permissions.includes(requiredPermission)) {
|
|
295
|
+
console.log('[verifyCapability] ❌ Permission denied:', {
|
|
296
|
+
required: requiredPermission,
|
|
297
|
+
has: tokenObj.permissions
|
|
298
|
+
});
|
|
235
299
|
return false;
|
|
236
300
|
}
|
|
237
301
|
|
|
302
|
+
console.log('[verifyCapability] ✅ Capability valid');
|
|
238
303
|
return true;
|
|
239
304
|
} catch (error) {
|
|
305
|
+
console.log('[verifyCapability] ❌ Error:', error.message);
|
|
240
306
|
return false;
|
|
241
307
|
}
|
|
242
308
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Federation Capabilities Module
|
|
3
|
+
*
|
|
4
|
+
* Handles capability token issuance, verification, and management
|
|
5
|
+
* for federation operations. Extracted from federation-methods.js
|
|
6
|
+
* for better modularity.
|
|
7
|
+
*
|
|
8
|
+
* @module federation/capabilities
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as crypto from '../crypto/secp256k1.js';
|
|
12
|
+
import * as registry from './registry.js';
|
|
13
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
14
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Issue a capability token for federation
|
|
18
|
+
* @param {Object} client - NostrClient instance
|
|
19
|
+
* @param {string} targetPubKey - Target public key to grant access to
|
|
20
|
+
* @param {Object} scope - Access scope { holonId, lensName, dataId }
|
|
21
|
+
* @param {string[]} permissions - Array of permissions ('read', 'write')
|
|
22
|
+
* @param {Object} options - Capability options
|
|
23
|
+
* @param {number} [options.expiresIn=3600000] - Expiration time in milliseconds
|
|
24
|
+
* @param {boolean} [options.isSelfCapability=false] - Whether this is a self-capability
|
|
25
|
+
* @returns {Promise<string>} Capability token
|
|
26
|
+
*/
|
|
27
|
+
export async function issueCapability(client, targetPubKey, scope, permissions, options = {}) {
|
|
28
|
+
const {
|
|
29
|
+
expiresIn = 3600000,
|
|
30
|
+
isSelfCapability = false,
|
|
31
|
+
} = options;
|
|
32
|
+
|
|
33
|
+
const token = await crypto.issueCapability(
|
|
34
|
+
permissions,
|
|
35
|
+
scope,
|
|
36
|
+
targetPubKey,
|
|
37
|
+
{
|
|
38
|
+
expiresIn,
|
|
39
|
+
issuer: client.publicKey,
|
|
40
|
+
issuerKey: client.privateKey,
|
|
41
|
+
isSelfCapability,
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return token;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Issue capability for a specific lens
|
|
50
|
+
* @param {Object} client - NostrClient instance
|
|
51
|
+
* @param {string} holonId - Holon ID
|
|
52
|
+
* @param {string} lensName - Lens name
|
|
53
|
+
* @param {string} targetPubKey - Target public key
|
|
54
|
+
* @param {Object} options - Options
|
|
55
|
+
* @returns {Promise<Object>} Capability info { token, scope, permissions }
|
|
56
|
+
*/
|
|
57
|
+
export async function issueCapabilityForLens(client, holonId, lensName, targetPubKey, options = {}) {
|
|
58
|
+
const {
|
|
59
|
+
permissions = ['read'],
|
|
60
|
+
expiresIn = 365 * 24 * 60 * 60 * 1000, // 1 year default
|
|
61
|
+
} = options;
|
|
62
|
+
|
|
63
|
+
const scope = { holonId, lensName, dataId: '*' };
|
|
64
|
+
|
|
65
|
+
const token = await issueCapability(client, targetPubKey, scope, permissions, {
|
|
66
|
+
expiresIn,
|
|
67
|
+
isSelfCapability: client.publicKey === targetPubKey,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
token,
|
|
72
|
+
scope,
|
|
73
|
+
permissions,
|
|
74
|
+
expiresAt: Date.now() + expiresIn,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Issue capabilities for multiple lenses
|
|
80
|
+
* @param {Object} client - NostrClient instance
|
|
81
|
+
* @param {string} holonId - Holon ID
|
|
82
|
+
* @param {string[]} lensNames - Array of lens names
|
|
83
|
+
* @param {string} targetPubKey - Target public key
|
|
84
|
+
* @param {Object} options - Options
|
|
85
|
+
* @returns {Promise<Object[]>} Array of capability info objects
|
|
86
|
+
*/
|
|
87
|
+
export async function issueCapabilitiesForLenses(client, holonId, lensNames, targetPubKey, options = {}) {
|
|
88
|
+
const capabilities = [];
|
|
89
|
+
|
|
90
|
+
for (const lensName of lensNames) {
|
|
91
|
+
try {
|
|
92
|
+
const capInfo = await issueCapabilityForLens(client, holonId, lensName, targetPubKey, options);
|
|
93
|
+
capabilities.push(capInfo);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.warn(`[Capabilities] Failed to issue capability for ${lensName}:`, err.message);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return capabilities;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Store a capability in the registry
|
|
104
|
+
* @param {Object} client - NostrClient instance
|
|
105
|
+
* @param {string} appname - Application namespace
|
|
106
|
+
* @param {string} partnerPubKey - Partner's public key
|
|
107
|
+
* @param {Object} capabilityInfo - Capability info to store
|
|
108
|
+
* @param {Object} options - Options
|
|
109
|
+
* @returns {Promise<boolean>} Success indicator
|
|
110
|
+
*/
|
|
111
|
+
export async function storeCapability(client, appname, partnerPubKey, capabilityInfo, options = {}) {
|
|
112
|
+
const { isSelf = false } = options;
|
|
113
|
+
|
|
114
|
+
if (isSelf) {
|
|
115
|
+
return registry.storeSelfCapability(client, appname, capabilityInfo);
|
|
116
|
+
} else {
|
|
117
|
+
return registry.storeInboundCapability(client, appname, partnerPubKey, capabilityInfo);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Hash a capability token for storage
|
|
123
|
+
* @param {string} token - Capability token to hash
|
|
124
|
+
* @returns {string} SHA256 hash of token
|
|
125
|
+
*/
|
|
126
|
+
export function hashToken(token) {
|
|
127
|
+
const encoder = new TextEncoder();
|
|
128
|
+
return bytesToHex(sha256(encoder.encode(token)));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Verify a capability token
|
|
133
|
+
* @param {string} token - Capability token
|
|
134
|
+
* @param {string} permission - Required permission
|
|
135
|
+
* @param {Object} scope - Requested scope
|
|
136
|
+
* @returns {Promise<boolean>} True if valid
|
|
137
|
+
*/
|
|
138
|
+
export async function verifyCapability(token, permission, scope) {
|
|
139
|
+
return crypto.verifyCapability(token, permission, scope);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get capability for accessing a partner's data
|
|
144
|
+
* @param {Object} client - NostrClient instance
|
|
145
|
+
* @param {string} appname - Application namespace
|
|
146
|
+
* @param {string} partnerPubKey - Partner's public key
|
|
147
|
+
* @param {Object} scope - Requested scope
|
|
148
|
+
* @returns {Promise<Object|null>} Capability entry or null
|
|
149
|
+
*/
|
|
150
|
+
export async function getCapabilityForPartner(client, appname, partnerPubKey, scope) {
|
|
151
|
+
return registry.getCapabilityForAuthor(client, appname, partnerPubKey, scope);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export default {
|
|
155
|
+
issueCapability,
|
|
156
|
+
issueCapabilityForLens,
|
|
157
|
+
issueCapabilitiesForLenses,
|
|
158
|
+
storeCapability,
|
|
159
|
+
hashToken,
|
|
160
|
+
verifyCapability,
|
|
161
|
+
getCapabilityForPartner,
|
|
162
|
+
};
|