holosphere 2.0.0-alpha11 → 2.0.0-alpha12
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-DQdDE6DG.js} +2 -2
- package/dist/{2019-D2OG2idw.js.map → 2019-DQdDE6DG.js.map} +1 -1
- package/dist/{2019-EION3wKo.cjs → 2019-Ew-DTDlI.cjs} +2 -2
- package/dist/{2019-EION3wKo.cjs.map → 2019-Ew-DTDlI.cjs.map} +1 -1
- package/dist/{browser-BSniCNqO.js → browser-CckFyRI9.js} +2 -2
- package/dist/{browser-BSniCNqO.js.map → browser-CckFyRI9.js.map} +1 -1
- package/dist/browser-D2qtVhH5.cjs +2 -0
- package/dist/{browser-Cq59Ij19.cjs.map → browser-D2qtVhH5.cjs.map} +1 -1
- package/dist/cjs/holosphere.cjs +1 -1
- package/dist/esm/holosphere.js +67 -50
- package/dist/index-9sqetkAn.cjs +29 -0
- package/dist/index-9sqetkAn.cjs.map +1 -0
- package/dist/index-C0ITDyFo.cjs +13 -0
- package/dist/index-C0ITDyFo.cjs.map +1 -0
- package/dist/{index-Bl6rM1NW.js → index-DrYM1LOY.js} +2 -2
- package/dist/{index-Bl6rM1NW.js.map → index-DrYM1LOY.js.map} +1 -1
- package/dist/{index-D-jZhliX.js → index-TDDyakLc.js} +19827 -720
- package/dist/index-TDDyakLc.js.map +1 -0
- package/dist/{index-Bwg3OzRM.cjs → index-kyf1sjaC.cjs} +3 -3
- package/dist/{index-Bwg3OzRM.cjs.map → index-kyf1sjaC.cjs.map} +1 -1
- package/dist/index-lbSQUoRz.js +11663 -0
- package/dist/index-lbSQUoRz.js.map +1 -0
- package/dist/{indexeddb-storage-5eiUNsHC.js → indexeddb-storage-CXhjqwhA.js} +2 -2
- package/dist/{indexeddb-storage-5eiUNsHC.js.map → indexeddb-storage-CXhjqwhA.js.map} +1 -1
- package/dist/{indexeddb-storage-FNFUVvTJ.cjs → indexeddb-storage-DFESDYIj.cjs} +2 -2
- package/dist/{indexeddb-storage-FNFUVvTJ.cjs.map → indexeddb-storage-DFESDYIj.cjs.map} +1 -1
- package/dist/{memory-storage-DMt36uZO.cjs → memory-storage-D1tc1bjk.cjs} +2 -2
- package/dist/{memory-storage-DMt36uZO.cjs.map → memory-storage-D1tc1bjk.cjs.map} +1 -1
- package/dist/{memory-storage-CI-gfmuG.js → memory-storage-DkewsdcM.js} +2 -2
- package/dist/{memory-storage-CI-gfmuG.js.map → memory-storage-DkewsdcM.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 +46 -29
- package/src/federation/capabilities.js +162 -0
- package/src/federation/card-storage.js +304 -0
- package/src/federation/handshake.js +204 -7
- package/src/federation/hologram.js +167 -58
- package/src/federation/holon-registry.js +166 -0
- package/src/federation/index.js +68 -0
- package/src/federation/registry.js +110 -2
- package/src/federation/request-card.js +373 -0
- package/src/hierarchical/upcast.js +19 -3
- package/src/index.js +162 -76
- package/src/lib/federation-methods.js +527 -5
- package/src/storage/nostr-async.js +2 -2
- package/src/storage/nostr-client.js +370 -155
- package/src/storage/nostr-wrapper.js +1 -1
- 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/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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Holon Registry - Maps holonId to public key
|
|
3
|
+
*
|
|
4
|
+
* Every holon (H3 cell, telegram chat, concept) can be registered with a keypair.
|
|
5
|
+
* The holonId IS the public key (or resolves to one via this registry).
|
|
6
|
+
*
|
|
7
|
+
* When reading data, the system resolves holonId -> pubkey to determine
|
|
8
|
+
* whose data to read and what capabilities are needed.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { writeGlobal, readGlobal, getAllGlobal } from '../storage/global-tables.js';
|
|
12
|
+
|
|
13
|
+
const HOLON_REGISTRY_TABLE = '_holon-registry';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if a string is a valid 64-character hex public key
|
|
17
|
+
* @param {string} str - String to check
|
|
18
|
+
* @returns {boolean} True if valid pubkey format
|
|
19
|
+
*/
|
|
20
|
+
export function isPubkey(str) {
|
|
21
|
+
return typeof str === 'string' && /^[0-9a-f]{64}$/i.test(str);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Register a holon -> public key mapping
|
|
26
|
+
* @param {Object} client - NostrClient instance
|
|
27
|
+
* @param {string} appname - Application namespace
|
|
28
|
+
* @param {string} holonId - Holon identifier (H3, chatId, concept, etc.)
|
|
29
|
+
* @param {string} publicKey - 64-char hex public key
|
|
30
|
+
* @param {Object} options - Registration options
|
|
31
|
+
* @param {string} [options.alias] - Human-readable name
|
|
32
|
+
* @returns {Promise<boolean>} Success indicator
|
|
33
|
+
*/
|
|
34
|
+
export async function registerHolon(client, appname, holonId, publicKey, options = {}) {
|
|
35
|
+
if (!isPubkey(publicKey)) {
|
|
36
|
+
throw new Error(`Invalid public key format: ${publicKey}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Don't register if holonId is already a pubkey
|
|
40
|
+
if (isPubkey(holonId)) {
|
|
41
|
+
return true; // No registration needed
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const entry = {
|
|
45
|
+
id: holonId,
|
|
46
|
+
holonId: holonId,
|
|
47
|
+
publicKey: publicKey,
|
|
48
|
+
alias: options.alias || null,
|
|
49
|
+
createdAt: Date.now(),
|
|
50
|
+
updatedAt: Date.now(),
|
|
51
|
+
createdBy: client.publicKey,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return writeGlobal(client, appname, HOLON_REGISTRY_TABLE, entry);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Look up a holon's registration entry
|
|
59
|
+
* @param {Object} client - NostrClient instance
|
|
60
|
+
* @param {string} appname - Application namespace
|
|
61
|
+
* @param {string} holonId - Holon identifier
|
|
62
|
+
* @returns {Promise<Object|null>} Registry entry or null
|
|
63
|
+
*/
|
|
64
|
+
export async function lookupHolon(client, appname, holonId) {
|
|
65
|
+
// If already a pubkey, return synthetic entry
|
|
66
|
+
if (isPubkey(holonId)) {
|
|
67
|
+
return {
|
|
68
|
+
holonId: holonId,
|
|
69
|
+
publicKey: holonId,
|
|
70
|
+
isDirect: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return readGlobal(client, appname, HOLON_REGISTRY_TABLE, holonId);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resolve holonId to public key
|
|
79
|
+
* @param {Object} client - NostrClient instance
|
|
80
|
+
* @param {string} appname - Application namespace
|
|
81
|
+
* @param {string} holonId - Holon identifier
|
|
82
|
+
* @returns {Promise<string|null>} Public key or null if not found
|
|
83
|
+
*/
|
|
84
|
+
export async function resolveHolonToPubkey(client, appname, holonId) {
|
|
85
|
+
// Direct pubkey
|
|
86
|
+
if (isPubkey(holonId)) {
|
|
87
|
+
return holonId;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Registry lookup
|
|
91
|
+
const entry = await lookupHolon(client, appname, holonId);
|
|
92
|
+
return entry?.publicKey || null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Unregister a holon
|
|
97
|
+
* @param {Object} client - NostrClient instance
|
|
98
|
+
* @param {string} appname - Application namespace
|
|
99
|
+
* @param {string} holonId - Holon identifier
|
|
100
|
+
* @returns {Promise<boolean>} Success indicator
|
|
101
|
+
*/
|
|
102
|
+
export async function unregisterHolon(client, appname, holonId) {
|
|
103
|
+
// Can't unregister a direct pubkey
|
|
104
|
+
if (isPubkey(holonId)) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Mark as deleted by writing a tombstone
|
|
109
|
+
const entry = {
|
|
110
|
+
id: holonId,
|
|
111
|
+
holonId: holonId,
|
|
112
|
+
publicKey: null,
|
|
113
|
+
_deleted: true,
|
|
114
|
+
deletedAt: Date.now(),
|
|
115
|
+
deletedBy: client.publicKey,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return writeGlobal(client, appname, HOLON_REGISTRY_TABLE, entry);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Update a holon's alias or metadata
|
|
123
|
+
* @param {Object} client - NostrClient instance
|
|
124
|
+
* @param {string} appname - Application namespace
|
|
125
|
+
* @param {string} holonId - Holon identifier
|
|
126
|
+
* @param {Object} updates - Fields to update
|
|
127
|
+
* @param {string} [updates.alias] - New alias
|
|
128
|
+
* @returns {Promise<boolean>} Success indicator
|
|
129
|
+
*/
|
|
130
|
+
export async function updateHolon(client, appname, holonId, updates = {}) {
|
|
131
|
+
const existing = await lookupHolon(client, appname, holonId);
|
|
132
|
+
if (!existing || existing.isDirect) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const entry = {
|
|
137
|
+
...existing,
|
|
138
|
+
...updates,
|
|
139
|
+
updatedAt: Date.now(),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return writeGlobal(client, appname, HOLON_REGISTRY_TABLE, entry);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* List all registered holons
|
|
147
|
+
* @param {Object} client - NostrClient instance
|
|
148
|
+
* @param {string} appname - Application namespace
|
|
149
|
+
* @returns {Promise<Object[]>} Array of registry entries
|
|
150
|
+
*/
|
|
151
|
+
export async function listRegisteredHolons(client, appname) {
|
|
152
|
+
const entries = await getAllGlobal(client, appname, HOLON_REGISTRY_TABLE);
|
|
153
|
+
return (entries || []).filter(e => !e._deleted);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Find holons by public key
|
|
158
|
+
* @param {Object} client - NostrClient instance
|
|
159
|
+
* @param {string} appname - Application namespace
|
|
160
|
+
* @param {string} publicKey - Public key to search for
|
|
161
|
+
* @returns {Promise<Object[]>} Array of holons with this pubkey
|
|
162
|
+
*/
|
|
163
|
+
export async function findHolonsByPubkey(client, appname, publicKey) {
|
|
164
|
+
const all = await listRegisteredHolons(client, appname);
|
|
165
|
+
return all.filter(e => e.publicKey === publicKey);
|
|
166
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Federation Module Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all federation-related functionality in a modular structure.
|
|
5
|
+
* The federation system is now split into smaller, focused components:
|
|
6
|
+
*
|
|
7
|
+
* - capabilities: Token issuance and verification
|
|
8
|
+
* - registry: Partner and capability storage
|
|
9
|
+
* - handshake: NIP-44 encrypted DM protocol
|
|
10
|
+
* - discovery: Nostr event-based discovery
|
|
11
|
+
* - hologram: Reference/hologram management
|
|
12
|
+
* - request-card: Card-based configuration UI
|
|
13
|
+
* - card-storage: Persistent card state management
|
|
14
|
+
* - holon-registry: HolonId to pubKey mapping
|
|
15
|
+
*
|
|
16
|
+
* @module federation
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// Core federation components
|
|
20
|
+
export * from './registry.js';
|
|
21
|
+
export * from './hologram.js';
|
|
22
|
+
export * from './discovery.js';
|
|
23
|
+
|
|
24
|
+
// Handshake protocol
|
|
25
|
+
export * from './handshake.js';
|
|
26
|
+
|
|
27
|
+
// Capability management
|
|
28
|
+
export * from './capabilities.js';
|
|
29
|
+
|
|
30
|
+
// Card-based configuration
|
|
31
|
+
export * from './request-card.js';
|
|
32
|
+
export * from './card-storage.js';
|
|
33
|
+
|
|
34
|
+
// Holon registry
|
|
35
|
+
export * from './holon-registry.js';
|
|
36
|
+
|
|
37
|
+
// Named exports for convenience
|
|
38
|
+
import * as registry from './registry.js';
|
|
39
|
+
import * as hologram from './hologram.js';
|
|
40
|
+
import * as discovery from './discovery.js';
|
|
41
|
+
import * as handshake from './handshake.js';
|
|
42
|
+
import * as capabilities from './capabilities.js';
|
|
43
|
+
import * as requestCard from './request-card.js';
|
|
44
|
+
import * as cardStorage from './card-storage.js';
|
|
45
|
+
import * as holonRegistry from './holon-registry.js';
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
registry,
|
|
49
|
+
hologram,
|
|
50
|
+
discovery,
|
|
51
|
+
handshake,
|
|
52
|
+
capabilities,
|
|
53
|
+
requestCard,
|
|
54
|
+
cardStorage,
|
|
55
|
+
holonRegistry,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Default export with all modules
|
|
59
|
+
export default {
|
|
60
|
+
registry,
|
|
61
|
+
hologram,
|
|
62
|
+
discovery,
|
|
63
|
+
handshake,
|
|
64
|
+
capabilities,
|
|
65
|
+
requestCard,
|
|
66
|
+
cardStorage,
|
|
67
|
+
holonRegistry,
|
|
68
|
+
};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Federation Registry -
|
|
3
|
-
*
|
|
2
|
+
* Federation Registry - UNIFIED MODEL
|
|
3
|
+
*
|
|
4
|
+
* Manages federation partnerships and capability tokens for both:
|
|
5
|
+
* - Self-federation (same author, different holons)
|
|
6
|
+
* - Cross-federation (different authors)
|
|
7
|
+
*
|
|
8
|
+
* In the unified model, all federation uses capabilities, including self-federation.
|
|
9
|
+
* This provides consistent security semantics across all federation types.
|
|
4
10
|
*/
|
|
5
11
|
|
|
6
12
|
import { writeGlobal, readGlobal } from '../storage/global-tables.js';
|
|
@@ -384,3 +390,105 @@ export async function cleanupExpiredCapabilities(client, appname) {
|
|
|
384
390
|
|
|
385
391
|
return { inboundRemoved, outboundRemoved };
|
|
386
392
|
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Store a self-capability (for same-author federation) - UNIFIED MODEL
|
|
396
|
+
*
|
|
397
|
+
* In the unified model, self-capabilities are stored similarly to cross-capabilities
|
|
398
|
+
* but marked as self-issued. This provides consistent capability management.
|
|
399
|
+
*
|
|
400
|
+
* @param {Object} client - NostrClient instance
|
|
401
|
+
* @param {string} appname - Application namespace
|
|
402
|
+
* @param {Object} capabilityInfo - Capability information
|
|
403
|
+
* @param {string} capabilityInfo.token - The capability token
|
|
404
|
+
* @param {Object} capabilityInfo.scope - Token scope
|
|
405
|
+
* @param {string[]} capabilityInfo.permissions - Granted permissions
|
|
406
|
+
* @returns {Promise<boolean>} Success indicator
|
|
407
|
+
*/
|
|
408
|
+
export async function storeSelfCapability(client, appname, capabilityInfo) {
|
|
409
|
+
const registry = await getFederationRegistry(client, appname);
|
|
410
|
+
|
|
411
|
+
// Self-capabilities are stored under the user's own pubKey
|
|
412
|
+
const selfPubKey = client.publicKey;
|
|
413
|
+
|
|
414
|
+
// Find or create self-entry
|
|
415
|
+
let selfEntry = registry.federatedWith.find(p => p.pubKey === selfPubKey);
|
|
416
|
+
|
|
417
|
+
if (!selfEntry) {
|
|
418
|
+
selfEntry = {
|
|
419
|
+
pubKey: selfPubKey,
|
|
420
|
+
alias: 'self',
|
|
421
|
+
addedAt: Date.now(),
|
|
422
|
+
addedVia: 'self_capability',
|
|
423
|
+
isSelf: true,
|
|
424
|
+
inboundCapabilities: [],
|
|
425
|
+
outboundCapabilities: [],
|
|
426
|
+
selfCapabilities: [], // Special array for self-capabilities
|
|
427
|
+
};
|
|
428
|
+
registry.federatedWith.push(selfEntry);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (!selfEntry.selfCapabilities) {
|
|
432
|
+
selfEntry.selfCapabilities = [];
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Check if we already have this capability (by scope match)
|
|
436
|
+
const existingIndex = selfEntry.selfCapabilities.findIndex(cap =>
|
|
437
|
+
JSON.stringify(cap.scope) === JSON.stringify(capabilityInfo.scope)
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
if (existingIndex >= 0) {
|
|
441
|
+
// Update existing capability
|
|
442
|
+
selfEntry.selfCapabilities[existingIndex] = {
|
|
443
|
+
...capabilityInfo,
|
|
444
|
+
updatedAt: Date.now(),
|
|
445
|
+
isSelfCapability: true,
|
|
446
|
+
};
|
|
447
|
+
} else {
|
|
448
|
+
// Add new capability
|
|
449
|
+
selfEntry.selfCapabilities.push({
|
|
450
|
+
...capabilityInfo,
|
|
451
|
+
issuedAt: Date.now(),
|
|
452
|
+
isSelfCapability: true,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return saveFederationRegistry(client, appname, registry);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Get self-capability for a scope - UNIFIED MODEL
|
|
461
|
+
*
|
|
462
|
+
* @param {Object} client - NostrClient instance
|
|
463
|
+
* @param {string} appname - Application namespace
|
|
464
|
+
* @param {Object} scope - Requested scope { holonId, lensName, dataId? }
|
|
465
|
+
* @returns {Promise<Object|null>} Self-capability or null
|
|
466
|
+
*/
|
|
467
|
+
export async function getSelfCapability(client, appname, scope) {
|
|
468
|
+
const registry = await getFederationRegistry(client, appname);
|
|
469
|
+
const selfEntry = registry.federatedWith.find(p => p.pubKey === client.publicKey && p.isSelf);
|
|
470
|
+
|
|
471
|
+
if (!selfEntry || !selfEntry.selfCapabilities) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Find matching, non-expired self-capability
|
|
476
|
+
return selfEntry.selfCapabilities.find(cap => {
|
|
477
|
+
// Check expiration
|
|
478
|
+
if (cap.expires && cap.expires < Date.now()) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Check scope match (supports wildcards)
|
|
483
|
+
return matchScope(cap.scope, scope);
|
|
484
|
+
}) || null;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Check if a capability is a self-capability
|
|
489
|
+
* @param {Object} capabilityInfo - Capability info object
|
|
490
|
+
* @returns {boolean} True if self-capability
|
|
491
|
+
*/
|
|
492
|
+
export function isSelfCapability(capabilityInfo) {
|
|
493
|
+
return capabilityInfo && capabilityInfo.isSelfCapability === true;
|
|
494
|
+
}
|