holosphere 2.0.0-alpha1 → 2.0.0-alpha2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/cjs/holosphere.cjs +2 -0
  2. package/dist/cjs/holosphere.cjs.map +1 -0
  3. package/dist/esm/holosphere.js +56 -0
  4. package/dist/esm/holosphere.js.map +1 -0
  5. package/dist/index-CDfIuXew.js +15974 -0
  6. package/dist/index-CDfIuXew.js.map +1 -0
  7. package/dist/index-ifOgtDvd.cjs +3 -0
  8. package/dist/index-ifOgtDvd.cjs.map +1 -0
  9. package/dist/indexeddb-storage-CMW4qRQS.js +96 -0
  10. package/dist/indexeddb-storage-CMW4qRQS.js.map +1 -0
  11. package/dist/indexeddb-storage-DLZOgetM.cjs +2 -0
  12. package/dist/indexeddb-storage-DLZOgetM.cjs.map +1 -0
  13. package/dist/memory-storage-DQzcAZlf.js +47 -0
  14. package/dist/memory-storage-DQzcAZlf.js.map +1 -0
  15. package/dist/memory-storage-DmePEP2q.cjs +2 -0
  16. package/dist/memory-storage-DmePEP2q.cjs.map +1 -0
  17. package/dist/secp256k1-CP0ZkpAx.cjs +13 -0
  18. package/dist/secp256k1-CP0ZkpAx.cjs.map +1 -0
  19. package/dist/secp256k1-vOXp40Fx.js +2281 -0
  20. package/dist/secp256k1-vOXp40Fx.js.map +1 -0
  21. package/docs/FOSDEM_PROPOSAL.md +388 -0
  22. package/docs/LOCALFIRST.md +266 -0
  23. package/docs/contracts/api-interface.md +793 -0
  24. package/docs/data-model.md +476 -0
  25. package/docs/gun-async-usage.md +338 -0
  26. package/docs/plan.md +349 -0
  27. package/docs/quickstart.md +674 -0
  28. package/docs/research.md +362 -0
  29. package/docs/spec.md +244 -0
  30. package/docs/storage-backends.md +326 -0
  31. package/docs/tasks.md +947 -0
  32. package/package.json +1 -1
  33. package/tests/unit/ai/aggregation.test.js +0 -295
  34. package/tests/unit/ai/breakdown.test.js +0 -446
  35. package/tests/unit/ai/classifier.test.js +0 -294
  36. package/tests/unit/ai/council.test.js +0 -262
  37. package/tests/unit/ai/embeddings.test.js +0 -384
  38. package/tests/unit/ai/federation-ai.test.js +0 -344
  39. package/tests/unit/ai/h3-ai.test.js +0 -458
  40. package/tests/unit/ai/index.test.js +0 -304
  41. package/tests/unit/ai/json-ops.test.js +0 -307
  42. package/tests/unit/ai/llm-service.test.js +0 -390
  43. package/tests/unit/ai/nl-query.test.js +0 -383
  44. package/tests/unit/ai/relationships.test.js +0 -311
  45. package/tests/unit/ai/schema-extractor.test.js +0 -384
  46. package/tests/unit/ai/spatial.test.js +0 -279
  47. package/tests/unit/ai/tts.test.js +0 -279
  48. package/tests/unit/content.test.js +0 -332
  49. package/tests/unit/contract/core.test.js +0 -88
  50. package/tests/unit/contract/crypto.test.js +0 -198
  51. package/tests/unit/contract/data.test.js +0 -223
  52. package/tests/unit/contract/federation.test.js +0 -181
  53. package/tests/unit/contract/hierarchical.test.js +0 -113
  54. package/tests/unit/contract/schema.test.js +0 -114
  55. package/tests/unit/contract/social.test.js +0 -217
  56. package/tests/unit/contract/spatial.test.js +0 -110
  57. package/tests/unit/contract/subscriptions.test.js +0 -128
  58. package/tests/unit/contract/utils.test.js +0 -159
  59. package/tests/unit/core.test.js +0 -152
  60. package/tests/unit/crypto.test.js +0 -328
  61. package/tests/unit/federation.test.js +0 -234
  62. package/tests/unit/gun-async.test.js +0 -252
  63. package/tests/unit/hierarchical.test.js +0 -399
  64. package/tests/unit/integration/scenario-01-geographic-storage.test.js +0 -74
  65. package/tests/unit/integration/scenario-02-federation.test.js +0 -76
  66. package/tests/unit/integration/scenario-03-subscriptions.test.js +0 -102
  67. package/tests/unit/integration/scenario-04-validation.test.js +0 -129
  68. package/tests/unit/integration/scenario-05-hierarchy.test.js +0 -125
  69. package/tests/unit/integration/scenario-06-social.test.js +0 -135
  70. package/tests/unit/integration/scenario-07-persistence.test.js +0 -130
  71. package/tests/unit/integration/scenario-08-authorization.test.js +0 -161
  72. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +0 -139
  73. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +0 -357
  74. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +0 -410
  75. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +0 -719
  76. package/tests/unit/performance/benchmark.test.js +0 -85
  77. package/tests/unit/schema.test.js +0 -213
  78. package/tests/unit/spatial.test.js +0 -158
  79. package/tests/unit/storage.test.js +0 -195
  80. package/tests/unit/subscriptions.test.js +0 -328
  81. package/tests/unit/test-data-permanence-debug.js +0 -197
  82. package/tests/unit/test-data-permanence.js +0 -340
  83. package/tests/unit/test-key-persistence-fixed.js +0 -148
  84. package/tests/unit/test-key-persistence.js +0 -172
  85. package/tests/unit/test-relay-permanence.js +0 -376
  86. package/tests/unit/test-second-node.js +0 -95
  87. package/tests/unit/test-simple-write.js +0 -89
@@ -1,719 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import HoloSphere from '../../../src/index.js';
3
-
4
- /**
5
- * Scenario 12: Capability-Based Federated Data Access
6
- *
7
- * This test verifies that:
8
- * 1. Capability tokens work correctly for read, write, and delete operations
9
- * 2. The federation registry correctly stores and retrieves capabilities
10
- * 3. Capability verification works for different permission levels
11
- * 4. Scope matching works correctly (holon, lens, dataId levels)
12
- *
13
- * Note: In test environment with MemoryStorage, we use different appNames
14
- * to simulate isolation between holospheres. In production with Nostr relays,
15
- * isolation is enforced by author (public key) filtering.
16
- */
17
- describe('Integration: Scenario 12 - Capability-Based Federated Read', () => {
18
- // Three distinct private keys for three separate holospheres
19
- const privateKeyA = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
20
- const privateKeyB = 'fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210';
21
- const privateKeyC = 'abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789';
22
-
23
- let holosphereA;
24
- let holosphereB;
25
- let holosphereC;
26
- let publicKeyA;
27
- let publicKeyB;
28
- let publicKeyC;
29
- let testHolon;
30
- let testHolon2;
31
-
32
- beforeEach(async () => {
33
- // Create three independent holospheres with different appNames for isolation
34
- // In production with Nostr, they would use same appName but different keys
35
- holosphereA = new HoloSphere({
36
- relays: [],
37
- appName: 'scenario-12-a',
38
- privateKey: privateKeyA,
39
- logLevel: 'ERROR'
40
- });
41
-
42
- holosphereB = new HoloSphere({
43
- relays: [],
44
- appName: 'scenario-12-b',
45
- privateKey: privateKeyB,
46
- logLevel: 'ERROR'
47
- });
48
-
49
- holosphereC = new HoloSphere({
50
- relays: [],
51
- appName: 'scenario-12-c',
52
- privateKey: privateKeyC,
53
- logLevel: 'ERROR'
54
- });
55
-
56
- // Get public keys
57
- publicKeyA = holosphereA.client.publicKey;
58
- publicKeyB = holosphereB.client.publicKey;
59
- publicKeyC = holosphereC.client.publicKey;
60
-
61
- // Create test holons (H3 cells)
62
- testHolon = await holosphereA.toHolon(37.7749, -122.4194, 9);
63
- testHolon2 = await holosphereA.toHolon(40.7128, -74.0060, 9);
64
- });
65
-
66
- afterEach(async () => {
67
- // Clean up
68
- if (holosphereA?.client?.close) await holosphereA.client.close();
69
- if (holosphereB?.client?.close) await holosphereB.client.close();
70
- if (holosphereC?.client?.close) await holosphereC.client.close();
71
- });
72
-
73
- describe('Capability Token Permissions', () => {
74
- it('should issue and verify capability token with READ permission', async () => {
75
- const token = await holosphereA.issueCapability(
76
- ['read'],
77
- { holonId: testHolon, lensName: 'documents' },
78
- publicKeyB,
79
- { expiresIn: 3600000, issuerKey: privateKeyA }
80
- );
81
-
82
- expect(typeof token).toBe('string');
83
-
84
- // Verify read permission is granted
85
- const hasRead = await holosphereB.verifyCapability(token, 'read', {
86
- holonId: testHolon,
87
- lensName: 'documents'
88
- });
89
- expect(hasRead).toBe(true);
90
-
91
- // Verify write permission is NOT granted
92
- const hasWrite = await holosphereB.verifyCapability(token, 'write', {
93
- holonId: testHolon,
94
- lensName: 'documents'
95
- });
96
- expect(hasWrite).toBe(false);
97
-
98
- // Verify delete permission is NOT granted
99
- const hasDelete = await holosphereB.verifyCapability(token, 'delete', {
100
- holonId: testHolon,
101
- lensName: 'documents'
102
- });
103
- expect(hasDelete).toBe(false);
104
- });
105
-
106
- it('should issue and verify capability token with WRITE permission', async () => {
107
- const token = await holosphereA.issueCapability(
108
- ['write'],
109
- { holonId: testHolon, lensName: 'documents' },
110
- publicKeyB,
111
- { expiresIn: 3600000, issuerKey: privateKeyA }
112
- );
113
-
114
- const hasWrite = await holosphereB.verifyCapability(token, 'write', {
115
- holonId: testHolon,
116
- lensName: 'documents'
117
- });
118
- expect(hasWrite).toBe(true);
119
-
120
- const hasRead = await holosphereB.verifyCapability(token, 'read', {
121
- holonId: testHolon,
122
- lensName: 'documents'
123
- });
124
- expect(hasRead).toBe(false);
125
- });
126
-
127
- it('should issue and verify capability token with DELETE permission', async () => {
128
- const token = await holosphereA.issueCapability(
129
- ['delete'],
130
- { holonId: testHolon, lensName: 'documents' },
131
- publicKeyB,
132
- { expiresIn: 3600000, issuerKey: privateKeyA }
133
- );
134
-
135
- const hasDelete = await holosphereB.verifyCapability(token, 'delete', {
136
- holonId: testHolon,
137
- lensName: 'documents'
138
- });
139
- expect(hasDelete).toBe(true);
140
-
141
- const hasWrite = await holosphereB.verifyCapability(token, 'write', {
142
- holonId: testHolon,
143
- lensName: 'documents'
144
- });
145
- expect(hasWrite).toBe(false);
146
- });
147
-
148
- it('should issue capability token with ALL permissions (read, write, delete)', async () => {
149
- const token = await holosphereA.issueCapability(
150
- ['read', 'write', 'delete'],
151
- { holonId: testHolon, lensName: 'documents' },
152
- publicKeyB,
153
- { expiresIn: 3600000, issuerKey: privateKeyA }
154
- );
155
-
156
- const hasRead = await holosphereB.verifyCapability(token, 'read', {
157
- holonId: testHolon,
158
- lensName: 'documents'
159
- });
160
- expect(hasRead).toBe(true);
161
-
162
- const hasWrite = await holosphereB.verifyCapability(token, 'write', {
163
- holonId: testHolon,
164
- lensName: 'documents'
165
- });
166
- expect(hasWrite).toBe(true);
167
-
168
- const hasDelete = await holosphereB.verifyCapability(token, 'delete', {
169
- holonId: testHolon,
170
- lensName: 'documents'
171
- });
172
- expect(hasDelete).toBe(true);
173
- });
174
-
175
- it('should reject expired capability tokens', async () => {
176
- const expiredToken = await holosphereA.issueCapability(
177
- ['read', 'write', 'delete'],
178
- { holonId: testHolon, lensName: 'documents' },
179
- publicKeyB,
180
- { expiresIn: -1000, issuerKey: privateKeyA } // Already expired
181
- );
182
-
183
- const hasRead = await holosphereB.verifyCapability(expiredToken, 'read', {
184
- holonId: testHolon,
185
- lensName: 'documents'
186
- });
187
- expect(hasRead).toBe(false);
188
-
189
- const hasWrite = await holosphereB.verifyCapability(expiredToken, 'write', {
190
- holonId: testHolon,
191
- lensName: 'documents'
192
- });
193
- expect(hasWrite).toBe(false);
194
- });
195
- });
196
-
197
- describe('Write Operations with Capability Tokens', () => {
198
- it('should allow write operation with valid write capability token', async () => {
199
- // A issues write capability to B
200
- const writeToken = await holosphereA.issueCapability(
201
- ['write'],
202
- { holonId: testHolon, lensName: 'shared' },
203
- publicKeyB,
204
- { expiresIn: 3600000, issuerKey: privateKeyA }
205
- );
206
-
207
- // B writes data using the capability token
208
- await holosphereB.write(testHolon, 'shared', {
209
- id: 'item-from-b',
210
- content: 'Written by B with capability',
211
- author: publicKeyB
212
- }, { capability: writeToken });
213
-
214
- // B can read their own data
215
- const data = await holosphereB.read(testHolon, 'shared', 'item-from-b');
216
- expect(data).toBeDefined();
217
- expect(data.content).toBe('Written by B with capability');
218
- });
219
-
220
- it('should reject write with invalid capability token', async () => {
221
- // Create an invalid token (for wrong scope)
222
- const wrongScopeToken = await holosphereA.issueCapability(
223
- ['write'],
224
- { holonId: testHolon, lensName: 'other-lens' },
225
- publicKeyB,
226
- { expiresIn: 3600000, issuerKey: privateKeyA }
227
- );
228
-
229
- // Verify token is invalid for 'shared' lens
230
- const isValid = await holosphereB.verifyCapability(wrongScopeToken, 'write', {
231
- holonId: testHolon,
232
- lensName: 'shared'
233
- });
234
- expect(isValid).toBe(false);
235
- });
236
-
237
- it('should reject write with read-only capability token', async () => {
238
- const readOnlyToken = await holosphereA.issueCapability(
239
- ['read'],
240
- { holonId: testHolon, lensName: 'protected' },
241
- publicKeyB,
242
- { expiresIn: 3600000, issuerKey: privateKeyA }
243
- );
244
-
245
- // Verify token doesn't have write permission
246
- const hasWrite = await holosphereB.verifyCapability(readOnlyToken, 'write', {
247
- holonId: testHolon,
248
- lensName: 'protected'
249
- });
250
- expect(hasWrite).toBe(false);
251
- });
252
- });
253
-
254
- describe('Delete Operations with Capability Tokens', () => {
255
- it('should allow delete operation with valid delete capability token', async () => {
256
- // A writes data
257
- await holosphereA.write(testHolon, 'deletable', {
258
- id: 'to-delete',
259
- content: 'This will be deleted'
260
- });
261
-
262
- // Verify data exists
263
- const exists = await holosphereA.read(testHolon, 'deletable', 'to-delete');
264
- expect(exists).toBeDefined();
265
-
266
- // A issues delete capability to B
267
- const deleteToken = await holosphereA.issueCapability(
268
- ['delete'],
269
- { holonId: testHolon, lensName: 'deletable' },
270
- publicKeyB,
271
- { expiresIn: 3600000, issuerKey: privateKeyA }
272
- );
273
-
274
- // Verify B has delete permission
275
- const hasDelete = await holosphereB.verifyCapability(deleteToken, 'delete', {
276
- holonId: testHolon,
277
- lensName: 'deletable'
278
- });
279
- expect(hasDelete).toBe(true);
280
- });
281
-
282
- it('should reject delete with write-only capability token', async () => {
283
- const writeOnlyToken = await holosphereA.issueCapability(
284
- ['write'],
285
- { holonId: testHolon, lensName: 'no-delete' },
286
- publicKeyB,
287
- { expiresIn: 3600000, issuerKey: privateKeyA }
288
- );
289
-
290
- // Verify token doesn't have delete permission
291
- const hasDelete = await holosphereB.verifyCapability(writeOnlyToken, 'delete', {
292
- holonId: testHolon,
293
- lensName: 'no-delete'
294
- });
295
- expect(hasDelete).toBe(false);
296
- });
297
- });
298
-
299
- describe('Federation Registry Operations', () => {
300
- it('should store and retrieve inbound capability', async () => {
301
- // A issues capability to B
302
- const readToken = await holosphereA.issueCapability(
303
- ['read'],
304
- { holonId: testHolon, lensName: 'federated-docs' },
305
- publicKeyB,
306
- { expiresIn: 3600000, issuerKey: privateKeyA }
307
- );
308
-
309
- // B stores the capability
310
- await holosphereB.storeInboundCapability(publicKeyA, {
311
- token: readToken,
312
- scope: { holonId: testHolon, lensName: 'federated-docs' },
313
- permissions: ['read'],
314
- expires: Date.now() + 3600000
315
- });
316
-
317
- // B can verify the stored capability
318
- const isValid = await holosphereB.verifyCapability(readToken, 'read', {
319
- holonId: testHolon,
320
- lensName: 'federated-docs'
321
- });
322
- expect(isValid).toBe(true);
323
- });
324
-
325
- it('should store capabilities from multiple partners', async () => {
326
- // A issues capability to C
327
- const tokenFromA = await holosphereA.issueCapability(
328
- ['read'],
329
- { holonId: testHolon, lensName: 'multi-partner' },
330
- publicKeyC,
331
- { expiresIn: 3600000, issuerKey: privateKeyA }
332
- );
333
-
334
- // B issues capability to C
335
- const tokenFromB = await holosphereB.issueCapability(
336
- ['read', 'write'],
337
- { holonId: testHolon, lensName: 'multi-partner' },
338
- publicKeyC,
339
- { expiresIn: 3600000, issuerKey: privateKeyB }
340
- );
341
-
342
- // C stores both capabilities
343
- await holosphereC.storeInboundCapability(publicKeyA, {
344
- token: tokenFromA,
345
- scope: { holonId: testHolon, lensName: 'multi-partner' },
346
- permissions: ['read'],
347
- expires: Date.now() + 3600000
348
- });
349
-
350
- await holosphereC.storeInboundCapability(publicKeyB, {
351
- token: tokenFromB,
352
- scope: { holonId: testHolon, lensName: 'multi-partner' },
353
- permissions: ['read', 'write'],
354
- expires: Date.now() + 3600000
355
- });
356
-
357
- // C can verify both capabilities
358
- const validFromA = await holosphereC.verifyCapability(tokenFromA, 'read', {
359
- holonId: testHolon,
360
- lensName: 'multi-partner'
361
- });
362
- expect(validFromA).toBe(true);
363
-
364
- const validFromB = await holosphereC.verifyCapability(tokenFromB, 'write', {
365
- holonId: testHolon,
366
- lensName: 'multi-partner'
367
- });
368
- expect(validFromB).toBe(true);
369
- });
370
- });
371
-
372
- describe('Scope Matching for Capabilities', () => {
373
- it('should validate capability only for correct holon scope', async () => {
374
- // Token for testHolon only
375
- const token = await holosphereA.issueCapability(
376
- ['read'],
377
- { holonId: testHolon, lensName: 'scoped' },
378
- publicKeyB,
379
- { expiresIn: 3600000, issuerKey: privateKeyA }
380
- );
381
-
382
- // Valid for testHolon
383
- const validForHolon1 = await holosphereB.verifyCapability(token, 'read', {
384
- holonId: testHolon,
385
- lensName: 'scoped'
386
- });
387
- expect(validForHolon1).toBe(true);
388
-
389
- // Invalid for testHolon2
390
- const validForHolon2 = await holosphereB.verifyCapability(token, 'read', {
391
- holonId: testHolon2,
392
- lensName: 'scoped'
393
- });
394
- expect(validForHolon2).toBe(false);
395
- });
396
-
397
- it('should validate capability only for correct lens scope', async () => {
398
- // Token for lens-a only
399
- const token = await holosphereA.issueCapability(
400
- ['read'],
401
- { holonId: testHolon, lensName: 'lens-a' },
402
- publicKeyB,
403
- { expiresIn: 3600000, issuerKey: privateKeyA }
404
- );
405
-
406
- // Valid for lens-a
407
- const validForLensA = await holosphereB.verifyCapability(token, 'read', {
408
- holonId: testHolon,
409
- lensName: 'lens-a'
410
- });
411
- expect(validForLensA).toBe(true);
412
-
413
- // Invalid for lens-b
414
- const validForLensB = await holosphereB.verifyCapability(token, 'read', {
415
- holonId: testHolon,
416
- lensName: 'lens-b'
417
- });
418
- expect(validForLensB).toBe(false);
419
- });
420
-
421
- it('should support wildcard holon scope (*)', async () => {
422
- // Token with wildcard holon
423
- const wildcardToken = await holosphereA.issueCapability(
424
- ['read'],
425
- { holonId: '*', lensName: 'global' },
426
- publicKeyB,
427
- { expiresIn: 3600000, issuerKey: privateKeyA }
428
- );
429
-
430
- // Valid for any holon
431
- const validForHolon1 = await holosphereB.verifyCapability(wildcardToken, 'read', {
432
- holonId: testHolon,
433
- lensName: 'global'
434
- });
435
- expect(validForHolon1).toBe(true);
436
-
437
- const validForHolon2 = await holosphereB.verifyCapability(wildcardToken, 'read', {
438
- holonId: testHolon2,
439
- lensName: 'global'
440
- });
441
- expect(validForHolon2).toBe(true);
442
-
443
- // But still restricted to 'global' lens
444
- const invalidForOtherLens = await holosphereB.verifyCapability(wildcardToken, 'read', {
445
- holonId: testHolon,
446
- lensName: 'other-lens'
447
- });
448
- expect(invalidForOtherLens).toBe(false);
449
- });
450
-
451
- it('should support wildcard lens scope (*)', async () => {
452
- // Token with wildcard lens
453
- const wildcardToken = await holosphereA.issueCapability(
454
- ['read'],
455
- { holonId: testHolon, lensName: '*' },
456
- publicKeyB,
457
- { expiresIn: 3600000, issuerKey: privateKeyA }
458
- );
459
-
460
- // Valid for any lens in testHolon
461
- const validForLensA = await holosphereB.verifyCapability(wildcardToken, 'read', {
462
- holonId: testHolon,
463
- lensName: 'lens-a'
464
- });
465
- expect(validForLensA).toBe(true);
466
-
467
- const validForLensB = await holosphereB.verifyCapability(wildcardToken, 'read', {
468
- holonId: testHolon,
469
- lensName: 'lens-b'
470
- });
471
- expect(validForLensB).toBe(true);
472
-
473
- // But still restricted to testHolon
474
- const invalidForOtherHolon = await holosphereB.verifyCapability(wildcardToken, 'read', {
475
- holonId: testHolon2,
476
- lensName: 'lens-a'
477
- });
478
- expect(invalidForOtherHolon).toBe(false);
479
- });
480
-
481
- it('should support full wildcard scope (*, *)', async () => {
482
- // Token with full wildcard - access to all holons and lenses
483
- const fullWildcardToken = await holosphereA.issueCapability(
484
- ['read'],
485
- { holonId: '*', lensName: '*' },
486
- publicKeyB,
487
- { expiresIn: 3600000, issuerKey: privateKeyA }
488
- );
489
-
490
- // Valid for any holon and lens combination
491
- const valid1 = await holosphereB.verifyCapability(fullWildcardToken, 'read', {
492
- holonId: testHolon,
493
- lensName: 'lens-a'
494
- });
495
- expect(valid1).toBe(true);
496
-
497
- const valid2 = await holosphereB.verifyCapability(fullWildcardToken, 'read', {
498
- holonId: testHolon2,
499
- lensName: 'lens-b'
500
- });
501
- expect(valid2).toBe(true);
502
- });
503
-
504
- it('should support dataId-specific scope', async () => {
505
- // Token for specific item only
506
- const itemToken = await holosphereA.issueCapability(
507
- ['read'],
508
- { holonId: testHolon, lensName: 'items', dataId: 'item-2' },
509
- publicKeyB,
510
- { expiresIn: 3600000, issuerKey: privateKeyA }
511
- );
512
-
513
- // Valid for item-2
514
- const validForItem2 = await holosphereB.verifyCapability(itemToken, 'read', {
515
- holonId: testHolon,
516
- lensName: 'items',
517
- dataId: 'item-2'
518
- });
519
- expect(validForItem2).toBe(true);
520
-
521
- // Invalid for item-1
522
- const validForItem1 = await holosphereB.verifyCapability(itemToken, 'read', {
523
- holonId: testHolon,
524
- lensName: 'items',
525
- dataId: 'item-1'
526
- });
527
- expect(validForItem1).toBe(false);
528
- });
529
- });
530
-
531
- describe('Complete Capability Workflow', () => {
532
- it('should complete full capability workflow: issue -> verify -> use', async () => {
533
- // === STEP 1: A issues full capability (read, write, delete) to B ===
534
- const fullCapability = await holosphereA.issueCapability(
535
- ['read', 'write', 'delete'],
536
- { holonId: testHolon, lensName: 'workflow-test' },
537
- publicKeyB,
538
- { expiresIn: 3600000, issuerKey: privateKeyA }
539
- );
540
-
541
- expect(typeof fullCapability).toBe('string');
542
-
543
- // === STEP 2: B stores the capability ===
544
- await holosphereB.storeInboundCapability(publicKeyA, {
545
- token: fullCapability,
546
- scope: { holonId: testHolon, lensName: 'workflow-test' },
547
- permissions: ['read', 'write', 'delete'],
548
- expires: Date.now() + 3600000
549
- });
550
-
551
- // === STEP 3: B verifies all permissions ===
552
- const hasRead = await holosphereB.verifyCapability(fullCapability, 'read', {
553
- holonId: testHolon,
554
- lensName: 'workflow-test'
555
- });
556
- expect(hasRead).toBe(true);
557
-
558
- const hasWrite = await holosphereB.verifyCapability(fullCapability, 'write', {
559
- holonId: testHolon,
560
- lensName: 'workflow-test'
561
- });
562
- expect(hasWrite).toBe(true);
563
-
564
- const hasDelete = await holosphereB.verifyCapability(fullCapability, 'delete', {
565
- holonId: testHolon,
566
- lensName: 'workflow-test'
567
- });
568
- expect(hasDelete).toBe(true);
569
-
570
- // === STEP 4: B can write data with capability ===
571
- await holosphereB.write(testHolon, 'workflow-test', {
572
- id: 'b-contribution',
573
- title: 'B Contribution',
574
- content: 'Added by B with capability',
575
- contributor: publicKeyB
576
- }, { capability: fullCapability });
577
-
578
- // Verify write worked
579
- const bContrib = await holosphereB.read(testHolon, 'workflow-test', 'b-contribution');
580
- expect(bContrib).toBeDefined();
581
- expect(bContrib.title).toBe('B Contribution');
582
-
583
- // === STEP 5: B deletes with capability ===
584
- const deleteResult = await holosphereB.delete(testHolon, 'workflow-test', 'b-contribution', {
585
- capability: fullCapability
586
- });
587
- expect(deleteResult).toBe(true);
588
-
589
- // Verify deletion
590
- const deleted = await holosphereB.read(testHolon, 'workflow-test', 'b-contribution');
591
- expect(deleted).toBeNull();
592
- });
593
-
594
- it('should properly isolate data between holospheres (different appNames)', async () => {
595
- // A writes data
596
- await holosphereA.write(testHolon, 'isolated', {
597
- id: 'a-data',
598
- content: 'From A',
599
- owner: publicKeyA
600
- });
601
-
602
- // B writes data
603
- await holosphereB.write(testHolon, 'isolated', {
604
- id: 'b-data',
605
- content: 'From B',
606
- owner: publicKeyB
607
- });
608
-
609
- // A can only see A's data
610
- const aData = await holosphereA.read(testHolon, 'isolated', 'a-data');
611
- expect(aData).toBeDefined();
612
- expect(aData.content).toBe('From A');
613
-
614
- const aSeesB = await holosphereA.read(testHolon, 'isolated', 'b-data');
615
- expect(aSeesB).toBeNull(); // Different appName
616
-
617
- // B can only see B's data
618
- const bData = await holosphereB.read(testHolon, 'isolated', 'b-data');
619
- expect(bData).toBeDefined();
620
- expect(bData.content).toBe('From B');
621
-
622
- const bSeesA = await holosphereB.read(testHolon, 'isolated', 'a-data');
623
- expect(bSeesA).toBeNull(); // Different appName
624
- });
625
- });
626
-
627
- describe('Capability Token Security', () => {
628
- it('should not allow capability reuse for different scope', async () => {
629
- const token = await holosphereA.issueCapability(
630
- ['read', 'write', 'delete'],
631
- { holonId: testHolon, lensName: 'specific-lens' },
632
- publicKeyB,
633
- { expiresIn: 3600000, issuerKey: privateKeyA }
634
- );
635
-
636
- // Token is valid for specific-lens
637
- const validForSpecific = await holosphereB.verifyCapability(token, 'read', {
638
- holonId: testHolon,
639
- lensName: 'specific-lens'
640
- });
641
- expect(validForSpecific).toBe(true);
642
-
643
- // Token is NOT valid for different-lens
644
- const validForDifferent = await holosphereB.verifyCapability(token, 'read', {
645
- holonId: testHolon,
646
- lensName: 'different-lens'
647
- });
648
- expect(validForDifferent).toBe(false);
649
-
650
- // Token is NOT valid for different holon
651
- const validForDifferentHolon = await holosphereB.verifyCapability(token, 'read', {
652
- holonId: testHolon2,
653
- lensName: 'specific-lens'
654
- });
655
- expect(validForDifferentHolon).toBe(false);
656
- });
657
-
658
- it('should enforce permission restrictions', async () => {
659
- // Issue read-only token
660
- const readOnlyToken = await holosphereA.issueCapability(
661
- ['read'],
662
- { holonId: testHolon, lensName: 'read-only' },
663
- publicKeyB,
664
- { expiresIn: 3600000, issuerKey: privateKeyA }
665
- );
666
-
667
- // Has read
668
- expect(await holosphereB.verifyCapability(readOnlyToken, 'read', {
669
- holonId: testHolon,
670
- lensName: 'read-only'
671
- })).toBe(true);
672
-
673
- // Does NOT have write
674
- expect(await holosphereB.verifyCapability(readOnlyToken, 'write', {
675
- holonId: testHolon,
676
- lensName: 'read-only'
677
- })).toBe(false);
678
-
679
- // Does NOT have delete
680
- expect(await holosphereB.verifyCapability(readOnlyToken, 'delete', {
681
- holonId: testHolon,
682
- lensName: 'read-only'
683
- })).toBe(false);
684
- });
685
-
686
- it('should have unique nonce per token (replay protection)', async () => {
687
- // Issue two tokens with same parameters
688
- const token1 = await holosphereA.issueCapability(
689
- ['read'],
690
- { holonId: testHolon, lensName: 'replay-test' },
691
- publicKeyB,
692
- { expiresIn: 3600000, issuerKey: privateKeyA }
693
- );
694
-
695
- const token2 = await holosphereA.issueCapability(
696
- ['read'],
697
- { holonId: testHolon, lensName: 'replay-test' },
698
- publicKeyB,
699
- { expiresIn: 3600000, issuerKey: privateKeyA }
700
- );
701
-
702
- // Tokens should be different (different nonces)
703
- expect(token1).not.toBe(token2);
704
-
705
- // But both should be valid
706
- const valid1 = await holosphereB.verifyCapability(token1, 'read', {
707
- holonId: testHolon,
708
- lensName: 'replay-test'
709
- });
710
- const valid2 = await holosphereB.verifyCapability(token2, 'read', {
711
- holonId: testHolon,
712
- lensName: 'replay-test'
713
- });
714
-
715
- expect(valid1).toBe(true);
716
- expect(valid2).toBe(true);
717
- });
718
- });
719
- });