holosphere 2.0.0-alpha13 → 2.0.0-alpha15
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-ATLjawsU.cjs +8 -0
- package/dist/{2019-Cp3uYhyY.cjs.map → 2019-ATLjawsU.cjs.map} +1 -1
- package/dist/{2019-CLMqIAfQ.js → 2019-BfjzDRje.js} +1667 -1721
- package/dist/{2019-CLMqIAfQ.js.map → 2019-BfjzDRje.js.map} +1 -1
- package/dist/{browser-nUQt1cnB.js → browser-CKTczilW.js} +2 -2
- package/dist/{browser-nUQt1cnB.js.map → browser-CKTczilW.js.map} +1 -1
- package/dist/{browser-D6cNVl0v.cjs → browser-GOg6KKOV.cjs} +2 -2
- package/dist/{browser-D6cNVl0v.cjs.map → browser-GOg6KKOV.cjs.map} +1 -1
- package/dist/cjs/holosphere.cjs +1 -1
- package/dist/esm/holosphere.js +25 -21
- package/dist/{index-CoAjtqsD.js → index-BdnrGafX.js} +2 -2
- package/dist/{index-CoAjtqsD.js.map → index-BdnrGafX.js.map} +1 -1
- package/dist/{index-BN_uoxQK.js → index-C3Cag0SV.js} +2558 -495
- package/dist/index-C3Cag0SV.js.map +1 -0
- package/dist/index-ChpSfdYS.cjs +29 -0
- package/dist/index-ChpSfdYS.cjs.map +1 -0
- package/dist/{index-DJjGSwXG.cjs → index-D_QecZNu.cjs} +2 -2
- package/dist/{index-DJjGSwXG.cjs.map → index-D_QecZNu.cjs.map} +1 -1
- package/dist/{index-Z5TstN1e.js → index-nMC3dWZ5.js} +2 -2
- package/dist/{index-Z5TstN1e.js.map → index-nMC3dWZ5.js.map} +1 -1
- package/dist/{index-Cp3tI53z.cjs → index-z5HWfWMu.cjs} +2 -2
- package/dist/{index-Cp3tI53z.cjs.map → index-z5HWfWMu.cjs.map} +1 -1
- package/dist/{indexeddb-storage-CZK5A7XH.cjs → indexeddb-storage-DWSeL-YF.cjs} +2 -2
- package/dist/{indexeddb-storage-CZK5A7XH.cjs.map → indexeddb-storage-DWSeL-YF.cjs.map} +1 -1
- package/dist/{indexeddb-storage-bpA01pAU.js → indexeddb-storage-dx01N0ET.js} +2 -2
- package/dist/{indexeddb-storage-bpA01pAU.js.map → indexeddb-storage-dx01N0ET.js.map} +1 -1
- package/dist/{memory-storage-BqhmytP_.js → memory-storage-BPIfkpcf.js} +2 -2
- package/dist/{memory-storage-BqhmytP_.js.map → memory-storage-BPIfkpcf.js.map} +1 -1
- package/dist/{memory-storage-B1k8Jszd.cjs → memory-storage-CKUGDq2d.cjs} +2 -2
- package/dist/{memory-storage-B1k8Jszd.cjs.map → memory-storage-CKUGDq2d.cjs.map} +1 -1
- package/package.json +3 -1
- package/scripts/test-ndk-direct.js +104 -0
- package/src/crypto/key-store.js +356 -0
- package/src/crypto/lens-keys.js +205 -0
- package/src/crypto/secp256k1.js +181 -18
- package/src/federation/handshake.js +317 -23
- package/src/federation/hologram.js +25 -17
- package/src/federation/registry.js +779 -59
- package/src/index.js +416 -27
- package/src/lib/federation-methods.js +308 -4
- package/src/storage/nostr-async.js +144 -86
- package/src/storage/nostr-client.js +77 -18
- package/src/storage/nostr-wrapper.js +4 -1
- package/src/storage/unified-storage.js +5 -4
- package/src/subscriptions/manager.js +1 -1
- package/vitest.config.js +6 -1
- package/dist/2019-Cp3uYhyY.cjs +0 -8
- package/dist/index-BN_uoxQK.js.map +0 -1
- package/dist/index-V8EHMYEY.cjs +0 -29
- package/dist/index-V8EHMYEY.cjs.map +0 -1
|
@@ -1347,7 +1347,7 @@ function createHasher$1(hashCons) {
|
|
|
1347
1347
|
hashC.create = () => hashCons();
|
|
1348
1348
|
return hashC;
|
|
1349
1349
|
}
|
|
1350
|
-
function randomBytes$
|
|
1350
|
+
function randomBytes$3(bytesLength = 32) {
|
|
1351
1351
|
if (crypto$2 && typeof crypto$2.getRandomValues === "function") {
|
|
1352
1352
|
return crypto$2.getRandomValues(new Uint8Array(bytesLength));
|
|
1353
1353
|
}
|
|
@@ -2930,15 +2930,15 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
2930
2930
|
if (!Fn.isValidNot0(scalar))
|
|
2931
2931
|
throw new Error("invalid scalar: out of range");
|
|
2932
2932
|
let point, fake;
|
|
2933
|
-
const
|
|
2933
|
+
const mul3 = (n2) => wnaf.cached(this, n2, (p) => normalizeZ(Point, p));
|
|
2934
2934
|
if (endo2) {
|
|
2935
2935
|
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
|
|
2936
|
-
const { p: k1p, f: k1f } =
|
|
2937
|
-
const { p: k2p, f: k2f } =
|
|
2936
|
+
const { p: k1p, f: k1f } = mul3(k1);
|
|
2937
|
+
const { p: k2p, f: k2f } = mul3(k2);
|
|
2938
2938
|
fake = k1f.add(k2f);
|
|
2939
2939
|
point = finishEndo(endo2.beta, k1p, k2p, k1neg, k2neg);
|
|
2940
2940
|
} else {
|
|
2941
|
-
const { p, f } =
|
|
2941
|
+
const { p, f } = mul3(scalar);
|
|
2942
2942
|
point = p;
|
|
2943
2943
|
fake = f;
|
|
2944
2944
|
}
|
|
@@ -3062,7 +3062,7 @@ function getWLengths(Fp, Fn) {
|
|
|
3062
3062
|
}
|
|
3063
3063
|
function ecdh(Point, ecdhOpts = {}) {
|
|
3064
3064
|
const { Fn } = Point;
|
|
3065
|
-
const randomBytes_ = ecdhOpts.randomBytes || randomBytes$
|
|
3065
|
+
const randomBytes_ = ecdhOpts.randomBytes || randomBytes$3;
|
|
3066
3066
|
const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) });
|
|
3067
3067
|
function isValidSecretKey(secretKey) {
|
|
3068
3068
|
try {
|
|
@@ -3137,7 +3137,7 @@ function ecdsa(Point, hash2, ecdsaOpts = {}) {
|
|
|
3137
3137
|
bits2int: "function",
|
|
3138
3138
|
bits2int_modN: "function"
|
|
3139
3139
|
});
|
|
3140
|
-
const randomBytes2 = ecdsaOpts.randomBytes || randomBytes$
|
|
3140
|
+
const randomBytes2 = ecdsaOpts.randomBytes || randomBytes$3;
|
|
3141
3141
|
const hmac2 = ecdsaOpts.hmac || ((key, ...msgs) => hmac$1(hash2, key, concatBytes$3(...msgs)));
|
|
3142
3142
|
const { Fp, Fn } = Point;
|
|
3143
3143
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
@@ -3541,7 +3541,7 @@ function challenge(...args) {
|
|
|
3541
3541
|
function schnorrGetPublicKey(secretKey) {
|
|
3542
3542
|
return schnorrGetExtPubKey(secretKey).bytes;
|
|
3543
3543
|
}
|
|
3544
|
-
function schnorrSign(message, secretKey, auxRand = randomBytes$
|
|
3544
|
+
function schnorrSign(message, secretKey, auxRand = randomBytes$3(32)) {
|
|
3545
3545
|
const { Fn } = Pointk1;
|
|
3546
3546
|
const m = ensureBytes("message", message);
|
|
3547
3547
|
const { bytes: px, scalar: d2 } = schnorrGetExtPubKey(secretKey);
|
|
@@ -3583,7 +3583,7 @@ function schnorrVerify(signature, message, publicKey) {
|
|
|
3583
3583
|
const schnorr = /* @__PURE__ */ (() => {
|
|
3584
3584
|
const size = 32;
|
|
3585
3585
|
const seedLength = 48;
|
|
3586
|
-
const randomSecretKey = (seed = randomBytes$
|
|
3586
|
+
const randomSecretKey = (seed = randomBytes$3(seedLength)) => {
|
|
3587
3587
|
return mapHashToField(seed, secp256k1_CURVE.n);
|
|
3588
3588
|
};
|
|
3589
3589
|
secp256k1$1.utils.randomSecretKey;
|
|
@@ -4808,7 +4808,7 @@ function wrapConstructor$2(hashCons) {
|
|
|
4808
4808
|
hashC.create = () => hashCons();
|
|
4809
4809
|
return hashC;
|
|
4810
4810
|
}
|
|
4811
|
-
function randomBytes$
|
|
4811
|
+
function randomBytes$2(bytesLength = 32) {
|
|
4812
4812
|
if (crypto$1 && typeof crypto$1.getRandomValues === "function") {
|
|
4813
4813
|
return crypto$1.getRandomValues(new Uint8Array(bytesLength));
|
|
4814
4814
|
}
|
|
@@ -5365,6 +5365,7 @@ function output$2(out, instance) {
|
|
|
5365
5365
|
}
|
|
5366
5366
|
}
|
|
5367
5367
|
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
|
5368
|
+
const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
5368
5369
|
const u32$1 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|
5369
5370
|
const createView$2 = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
5370
5371
|
const isLE$2 = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68;
|
|
@@ -5409,8 +5410,8 @@ function setBigUint64$1(view, byteOffset, value, isLE2) {
|
|
|
5409
5410
|
const _u32_max = BigInt(4294967295);
|
|
5410
5411
|
const wh = Number(value >> _32n2 & _u32_max);
|
|
5411
5412
|
const wl = Number(value & _u32_max);
|
|
5412
|
-
const h = 4;
|
|
5413
|
-
const l = 0;
|
|
5413
|
+
const h = isLE2 ? 4 : 0;
|
|
5414
|
+
const l = isLE2 ? 0 : 4;
|
|
5414
5415
|
view.setUint32(byteOffset + h, wh, isLE2);
|
|
5415
5416
|
view.setUint32(byteOffset + l, wl, isLE2);
|
|
5416
5417
|
}
|
|
@@ -5659,7 +5660,7 @@ class Poly1305 {
|
|
|
5659
5660
|
return res;
|
|
5660
5661
|
}
|
|
5661
5662
|
}
|
|
5662
|
-
function wrapConstructorWithKey(hashCons) {
|
|
5663
|
+
function wrapConstructorWithKey$1(hashCons) {
|
|
5663
5664
|
const hashC = (msg, key) => hashCons(key).update(toBytes$2(msg)).digest();
|
|
5664
5665
|
const tmp = hashCons(new Uint8Array(32));
|
|
5665
5666
|
hashC.outputLen = tmp.outputLen;
|
|
@@ -5667,7 +5668,7 @@ function wrapConstructorWithKey(hashCons) {
|
|
|
5667
5668
|
hashC.create = (key) => hashCons(key);
|
|
5668
5669
|
return hashC;
|
|
5669
5670
|
}
|
|
5670
|
-
const poly1305 = wrapConstructorWithKey((key) => new Poly1305(key));
|
|
5671
|
+
const poly1305 = wrapConstructorWithKey$1((key) => new Poly1305(key));
|
|
5671
5672
|
const _utf8ToBytes = (str2) => Uint8Array.from(str2.split("").map((c) => c.charCodeAt(0)));
|
|
5672
5673
|
const sigma16 = _utf8ToBytes("expand 16-byte k");
|
|
5673
5674
|
const sigma32 = _utf8ToBytes("expand 32-byte k");
|
|
@@ -5948,16 +5949,16 @@ const xchacha20 = /* @__PURE__ */ createCipher(chachaCore, {
|
|
|
5948
5949
|
extendNonceFn: hchacha,
|
|
5949
5950
|
allowShortKeys: false
|
|
5950
5951
|
});
|
|
5951
|
-
const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
|
|
5952
|
+
const ZEROS16$1 = /* @__PURE__ */ new Uint8Array(16);
|
|
5952
5953
|
const updatePadded = (h, msg) => {
|
|
5953
5954
|
h.update(msg);
|
|
5954
5955
|
const left = msg.length % 16;
|
|
5955
5956
|
if (left)
|
|
5956
|
-
h.update(ZEROS16.subarray(left));
|
|
5957
|
+
h.update(ZEROS16$1.subarray(left));
|
|
5957
5958
|
};
|
|
5958
|
-
const ZEROS32 = /* @__PURE__ */ new Uint8Array(32);
|
|
5959
|
-
function computeTag(fn, key, nonce, data, AAD) {
|
|
5960
|
-
const authKey = fn(key, nonce, ZEROS32);
|
|
5959
|
+
const ZEROS32$1 = /* @__PURE__ */ new Uint8Array(32);
|
|
5960
|
+
function computeTag$1(fn, key, nonce, data, AAD) {
|
|
5961
|
+
const authKey = fn(key, nonce, ZEROS32$1);
|
|
5961
5962
|
const h = poly1305.create(authKey);
|
|
5962
5963
|
if (AAD)
|
|
5963
5964
|
updatePadded(h, AAD);
|
|
@@ -5985,7 +5986,7 @@ const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|
|
5985
5986
|
output2 = new Uint8Array(clength);
|
|
5986
5987
|
}
|
|
5987
5988
|
xorStream(key, nonce, plaintext, output2, 1);
|
|
5988
|
-
const tag = computeTag(xorStream, key, nonce, output2.subarray(0, -tagLength), AAD);
|
|
5989
|
+
const tag = computeTag$1(xorStream, key, nonce, output2.subarray(0, -tagLength), AAD);
|
|
5989
5990
|
output2.set(tag, plength);
|
|
5990
5991
|
return output2;
|
|
5991
5992
|
},
|
|
@@ -6001,7 +6002,7 @@ const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|
|
6001
6002
|
}
|
|
6002
6003
|
const data = ciphertext.subarray(0, -tagLength);
|
|
6003
6004
|
const passedTag = ciphertext.subarray(-tagLength);
|
|
6004
|
-
const tag = computeTag(xorStream, key, nonce, data, AAD);
|
|
6005
|
+
const tag = computeTag$1(xorStream, key, nonce, data, AAD);
|
|
6005
6006
|
if (!equalBytes(passedTag, tag))
|
|
6006
6007
|
throw new Error("invalid tag");
|
|
6007
6008
|
xorStream(key, nonce, data, output2, 1);
|
|
@@ -6424,11 +6425,11 @@ function encodeBech32$1(prefix, data) {
|
|
|
6424
6425
|
function encodeBytes$1(prefix, bytes2) {
|
|
6425
6426
|
return encodeBech32$1(prefix, bytes2);
|
|
6426
6427
|
}
|
|
6427
|
-
function encrypt$
|
|
6428
|
-
let salt = randomBytes$
|
|
6428
|
+
function encrypt$2(sec, password, logn = 16, ksb = 2) {
|
|
6429
|
+
let salt = randomBytes$2(16);
|
|
6429
6430
|
let n2 = 2 ** logn;
|
|
6430
6431
|
let key = scrypt(password.normalize("NFKC"), salt, { N: n2, r: 8, p: 1, dkLen: 32 });
|
|
6431
|
-
let nonce = randomBytes$
|
|
6432
|
+
let nonce = randomBytes$2(24);
|
|
6432
6433
|
let aad = Uint8Array.from([ksb]);
|
|
6433
6434
|
let xc2p1 = xchacha20poly1305(key, nonce, aad);
|
|
6434
6435
|
let ciphertext = xc2p1.encrypt(sec);
|
|
@@ -6460,7 +6461,7 @@ function decrypt$1(ncryptsec, password) {
|
|
|
6460
6461
|
const nip49_star = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
6461
6462
|
__proto__: null,
|
|
6462
6463
|
decrypt: decrypt$1,
|
|
6463
|
-
encrypt: encrypt$
|
|
6464
|
+
encrypt: encrypt$2
|
|
6464
6465
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
6465
6466
|
var utf8Decoder = new TextDecoder("utf-8");
|
|
6466
6467
|
var utf8Encoder = new TextEncoder();
|
|
@@ -7408,9 +7409,9 @@ var NDKRelayConnectivity = class {
|
|
|
7408
7409
|
}
|
|
7409
7410
|
case "COUNT": {
|
|
7410
7411
|
const payload = data[2];
|
|
7411
|
-
const
|
|
7412
|
-
if (
|
|
7413
|
-
|
|
7412
|
+
const cr2 = this.openCountRequests.get(id2);
|
|
7413
|
+
if (cr2) {
|
|
7414
|
+
cr2.resolve(payload.count);
|
|
7414
7415
|
this.openCountRequests.delete(id2);
|
|
7415
7416
|
}
|
|
7416
7417
|
return;
|
|
@@ -9050,7 +9051,7 @@ async function generateContentTags(content, tags = [], opts, ctx) {
|
|
|
9050
9051
|
async function maybeGetEventRelayUrl(_nip19Id) {
|
|
9051
9052
|
return "";
|
|
9052
9053
|
}
|
|
9053
|
-
async function encrypt(recipient, signer, scheme = "nip44") {
|
|
9054
|
+
async function encrypt$1(recipient, signer, scheme = "nip44") {
|
|
9054
9055
|
let encrypted;
|
|
9055
9056
|
if (!this.ndk) throw new Error("No NDK instance found!");
|
|
9056
9057
|
let currentSigner = signer;
|
|
@@ -9718,7 +9719,7 @@ var NDKEvent = class _NDKEvent extends lib$1.EventEmitter {
|
|
|
9718
9719
|
* @returns {string} - Encoded naddr, note or nevent.
|
|
9719
9720
|
*/
|
|
9720
9721
|
encode = encode$1.bind(this);
|
|
9721
|
-
encrypt = encrypt.bind(this);
|
|
9722
|
+
encrypt = encrypt$1.bind(this);
|
|
9722
9723
|
decrypt = decrypt.bind(this);
|
|
9723
9724
|
/**
|
|
9724
9725
|
* Get all tags with the given name
|
|
@@ -15528,7 +15529,7 @@ var NDKPrivateKeySigner = class _NDKPrivateKeySigner {
|
|
|
15528
15529
|
*/
|
|
15529
15530
|
encryptToNcryptsec(password, logn = 16, ksb = 2) {
|
|
15530
15531
|
if (!this._privateKey) throw new Error("Private key not available");
|
|
15531
|
-
return encrypt$
|
|
15532
|
+
return encrypt$2(this._privateKey, password, logn, ksb);
|
|
15532
15533
|
}
|
|
15533
15534
|
/**
|
|
15534
15535
|
* Generate a new private key.
|
|
@@ -18010,12 +18011,12 @@ async function createPersistentStorage(namespace, options = {}) {
|
|
|
18010
18011
|
const isBrowser = typeof window !== "undefined" && typeof window.indexedDB !== "undefined";
|
|
18011
18012
|
typeof process !== "undefined" && process.versions && void 0;
|
|
18012
18013
|
if (isBrowser) {
|
|
18013
|
-
const { IndexedDBStorage } = await import("./indexeddb-storage-
|
|
18014
|
+
const { IndexedDBStorage } = await import("./indexeddb-storage-dx01N0ET.js");
|
|
18014
18015
|
const storage = new IndexedDBStorage();
|
|
18015
18016
|
await storage.init(namespace);
|
|
18016
18017
|
return storage;
|
|
18017
18018
|
} else {
|
|
18018
|
-
const { MemoryStorage } = await import("./memory-storage-
|
|
18019
|
+
const { MemoryStorage } = await import("./memory-storage-BPIfkpcf.js");
|
|
18019
18020
|
const storage = new MemoryStorage();
|
|
18020
18021
|
await storage.init(namespace);
|
|
18021
18022
|
return storage;
|
|
@@ -18028,7 +18029,7 @@ async function loadNDKCacheAdapter() {
|
|
|
18028
18029
|
ndkCacheAdapterLoaded = true;
|
|
18029
18030
|
if (typeof window !== "undefined" && typeof indexedDB !== "undefined") {
|
|
18030
18031
|
try {
|
|
18031
|
-
const module2 = await import("./index-
|
|
18032
|
+
const module2 = await import("./index-nMC3dWZ5.js");
|
|
18032
18033
|
NDKCacheAdapterDexie = module2.default || module2.NDKCacheAdapterDexie;
|
|
18033
18034
|
} catch (e) {
|
|
18034
18035
|
console.debug("[nostr] NDK cache adapter not available, using LRU cache");
|
|
@@ -18220,7 +18221,13 @@ class NostrClient {
|
|
|
18220
18221
|
cacheAdapter
|
|
18221
18222
|
});
|
|
18222
18223
|
this.relays.forEach((r) => globalNDKRelays.add(r));
|
|
18223
|
-
|
|
18224
|
+
console.log("[NostrClient] Connecting to relays:", this.relays);
|
|
18225
|
+
try {
|
|
18226
|
+
await this.ndk.connect();
|
|
18227
|
+
console.log("[NostrClient] NDK connect() completed. Pool stats:", this.ndk.pool?.stats?.());
|
|
18228
|
+
} catch (err) {
|
|
18229
|
+
console.error("[NostrClient] NDK connect() FAILED:", err.message);
|
|
18230
|
+
}
|
|
18224
18231
|
} else {
|
|
18225
18232
|
this.ndk = null;
|
|
18226
18233
|
}
|
|
@@ -18403,7 +18410,15 @@ class NostrClient {
|
|
|
18403
18410
|
* @returns {Promise<Object|null>} The event or null if not found
|
|
18404
18411
|
*/
|
|
18405
18412
|
async persistentGet(path) {
|
|
18406
|
-
|
|
18413
|
+
try {
|
|
18414
|
+
await Promise.race([
|
|
18415
|
+
this._initReady,
|
|
18416
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Init timeout")), 1e4))
|
|
18417
|
+
]);
|
|
18418
|
+
} catch (err) {
|
|
18419
|
+
console.warn("[NostrClient] persistentGet: Init timeout, returning null");
|
|
18420
|
+
return null;
|
|
18421
|
+
}
|
|
18407
18422
|
if (!this.persistentStorage) return null;
|
|
18408
18423
|
try {
|
|
18409
18424
|
const event = await this.persistentStorage.get(path);
|
|
@@ -18490,7 +18505,14 @@ class NostrClient {
|
|
|
18490
18505
|
* });
|
|
18491
18506
|
*/
|
|
18492
18507
|
async publish(event, options = {}) {
|
|
18493
|
-
|
|
18508
|
+
try {
|
|
18509
|
+
await Promise.race([
|
|
18510
|
+
this._initReady,
|
|
18511
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Init timeout")), 15e3))
|
|
18512
|
+
]);
|
|
18513
|
+
} catch (err) {
|
|
18514
|
+
console.warn("[NostrClient] publish: Init timeout, continuing with available state");
|
|
18515
|
+
}
|
|
18494
18516
|
const waitForRelays = options.waitForRelays || false;
|
|
18495
18517
|
const isReplaceable2 = event.kind >= 3e4 && event.kind < 4e4;
|
|
18496
18518
|
const shouldDebounce = isReplaceable2 && options.debounce !== false && !waitForRelays;
|
|
@@ -18642,9 +18664,14 @@ class NostrClient {
|
|
|
18642
18664
|
const failed = [];
|
|
18643
18665
|
const formattedResults = [];
|
|
18644
18666
|
try {
|
|
18645
|
-
const
|
|
18667
|
+
const publishPromise = ndkEvent.publish();
|
|
18668
|
+
const timeoutPromise = new Promise(
|
|
18669
|
+
(_, reject) => setTimeout(() => reject(new Error("Publish timeout")), 3e4)
|
|
18670
|
+
);
|
|
18671
|
+
const publishedRelays = await Promise.race([publishPromise, timeoutPromise]);
|
|
18646
18672
|
for (const relay of relays) {
|
|
18647
|
-
const
|
|
18673
|
+
const ndkRelay = this.ndk.pool.getRelay(relay);
|
|
18674
|
+
const wasPublished = publishedRelays.has(ndkRelay);
|
|
18648
18675
|
if (wasPublished) {
|
|
18649
18676
|
successful.push(relay);
|
|
18650
18677
|
formattedResults.push({
|
|
@@ -18701,8 +18728,16 @@ class NostrClient {
|
|
|
18701
18728
|
* });
|
|
18702
18729
|
*/
|
|
18703
18730
|
async query(filter, options = {}) {
|
|
18704
|
-
|
|
18705
|
-
|
|
18731
|
+
const queryTimeout = options.timeout !== void 0 ? options.timeout : 3e4;
|
|
18732
|
+
try {
|
|
18733
|
+
await Promise.race([
|
|
18734
|
+
this._initReady,
|
|
18735
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Init timeout")), Math.min(queryTimeout, 1e4)))
|
|
18736
|
+
]);
|
|
18737
|
+
} catch (err) {
|
|
18738
|
+
console.warn("[NostrClient] query: Init timeout, continuing with available state");
|
|
18739
|
+
}
|
|
18740
|
+
const timeout = queryTimeout;
|
|
18706
18741
|
const localFirst = options.localFirst !== false;
|
|
18707
18742
|
const forceRelay = options.forceRelay === true;
|
|
18708
18743
|
if (this.relays.length === 0 || !this.ndk) {
|
|
@@ -18715,7 +18750,7 @@ class NostrClient {
|
|
|
18715
18750
|
const matchingEvents = this._getMatchingCachedEvents(filter);
|
|
18716
18751
|
return matchingEvents;
|
|
18717
18752
|
}
|
|
18718
|
-
if (isOwnDataQuery && subInfo && !subInfo.initialized) {
|
|
18753
|
+
if (!forceRelay && isOwnDataQuery && subInfo && !subInfo.initialized) {
|
|
18719
18754
|
await Promise.race([
|
|
18720
18755
|
subInfo.initPromise,
|
|
18721
18756
|
new Promise((resolve) => setTimeout(resolve, Math.min(timeout, AUTHOR_SUB_INIT_TIMEOUT)))
|
|
@@ -18757,10 +18792,23 @@ class NostrClient {
|
|
|
18757
18792
|
try {
|
|
18758
18793
|
let events = [];
|
|
18759
18794
|
if (this.ndk) {
|
|
18760
|
-
const
|
|
18795
|
+
const fetchPromise = this.ndk.fetchEvents(filter, {
|
|
18761
18796
|
closeOnEose: true
|
|
18762
18797
|
});
|
|
18763
|
-
|
|
18798
|
+
const timeoutPromise = new Promise(
|
|
18799
|
+
(_, reject) => setTimeout(() => reject(new Error("Relay query timeout")), timeout || 3e4)
|
|
18800
|
+
);
|
|
18801
|
+
try {
|
|
18802
|
+
const fetchedEvents = await Promise.race([fetchPromise, timeoutPromise]);
|
|
18803
|
+
events = Array.from(fetchedEvents).map((e) => e.rawEvent());
|
|
18804
|
+
} catch (err) {
|
|
18805
|
+
if (err.message === "Relay query timeout") {
|
|
18806
|
+
console.warn("[NostrClient] Relay query timed out, returning empty results");
|
|
18807
|
+
events = [];
|
|
18808
|
+
} else {
|
|
18809
|
+
throw err;
|
|
18810
|
+
}
|
|
18811
|
+
}
|
|
18764
18812
|
}
|
|
18765
18813
|
if (filter.authors && filter.authors.length > 0) {
|
|
18766
18814
|
events = events.filter((event) => filter.authors.includes(event.pubkey));
|
|
@@ -18935,16 +18983,20 @@ class NostrClient {
|
|
|
18935
18983
|
*/
|
|
18936
18984
|
async queryHybrid(filter, options = {}) {
|
|
18937
18985
|
await this._initReady;
|
|
18938
|
-
options.timeout !== void 0 ? options.timeout : 3e4;
|
|
18986
|
+
const timeout = options.timeout !== void 0 ? options.timeout : 3e4;
|
|
18939
18987
|
const localEvents = this._getMatchingCachedEvents(filter);
|
|
18940
18988
|
if (this.relays.length === 0 || !this.ndk) {
|
|
18941
18989
|
return localEvents;
|
|
18942
18990
|
}
|
|
18943
18991
|
let relayEvents = [];
|
|
18944
18992
|
try {
|
|
18945
|
-
const
|
|
18993
|
+
const fetchPromise = this.ndk.fetchEvents(filter, {
|
|
18946
18994
|
closeOnEose: true
|
|
18947
18995
|
});
|
|
18996
|
+
const timeoutPromise = new Promise(
|
|
18997
|
+
(_, reject) => setTimeout(() => reject(new Error("Relay query timeout")), timeout)
|
|
18998
|
+
);
|
|
18999
|
+
const fetchedEvents = await Promise.race([fetchPromise, timeoutPromise]);
|
|
18948
19000
|
relayEvents = Array.from(fetchedEvents).map((e) => e.rawEvent());
|
|
18949
19001
|
if (filter.authors && filter.authors.length > 0) {
|
|
18950
19002
|
relayEvents = relayEvents.filter((event) => filter.authors.includes(event.pubkey));
|
|
@@ -20963,7 +21015,7 @@ class GunSchemaValidator {
|
|
|
20963
21015
|
return true;
|
|
20964
21016
|
}
|
|
20965
21017
|
try {
|
|
20966
|
-
const AjvModule = await import("./2019-
|
|
21018
|
+
const AjvModule = await import("./2019-BfjzDRje.js").then((n2) => n2._);
|
|
20967
21019
|
this.Ajv = AjvModule.default || AjvModule;
|
|
20968
21020
|
this.validator = new this.Ajv({
|
|
20969
21021
|
allErrors: true,
|
|
@@ -21184,7 +21236,7 @@ class GunDBBackend extends StorageBackend {
|
|
|
21184
21236
|
let Gun;
|
|
21185
21237
|
try {
|
|
21186
21238
|
console.log("[gundb-backend] Importing Gun...");
|
|
21187
|
-
const gunModule = await import("./browser-
|
|
21239
|
+
const gunModule = await import("./browser-CKTczilW.js").then((n2) => n2.b);
|
|
21188
21240
|
Gun = gunModule.default || gunModule;
|
|
21189
21241
|
console.log("[gundb-backend] Gun imported:", typeof Gun);
|
|
21190
21242
|
} catch (error) {
|
|
@@ -21815,7 +21867,7 @@ class GunDBBackend extends StorageBackend {
|
|
|
21815
21867
|
}
|
|
21816
21868
|
}
|
|
21817
21869
|
const name = "holosphere";
|
|
21818
|
-
const version$1 = "2.0.0-
|
|
21870
|
+
const version$1 = "2.0.0-alpha14";
|
|
21819
21871
|
const description = "Holonic geospatial communication infrastructure combining H3 hexagonal indexing with distributed P2P storage";
|
|
21820
21872
|
const type = "module";
|
|
21821
21873
|
const bin = {
|
|
@@ -21840,6 +21892,8 @@ const scripts = {
|
|
|
21840
21892
|
test: "vitest run",
|
|
21841
21893
|
"test:watch": "vitest",
|
|
21842
21894
|
"test:coverage": "vitest run --coverage",
|
|
21895
|
+
"test:real-relays": "USE_REAL_RELAYS=true vitest run",
|
|
21896
|
+
"test:integration:real": "USE_REAL_RELAYS=true vitest run tests/unit/integration",
|
|
21843
21897
|
lint: "eslint src tests",
|
|
21844
21898
|
format: 'prettier --write "src/**/*.js" "tests/**/*.js"'
|
|
21845
21899
|
};
|
|
@@ -22580,44 +22634,784 @@ const h3Operations = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.define
|
|
|
22580
22634
|
isValidH3,
|
|
22581
22635
|
toHolon
|
|
22582
22636
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
22637
|
+
const BLOCK_SIZE$1 = 16;
|
|
22638
|
+
const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
|
|
22639
|
+
const ZEROS32 = u32$1(ZEROS16);
|
|
22640
|
+
const POLY$1 = 225;
|
|
22641
|
+
const mul2$1 = (s0, s1, s2, s3) => {
|
|
22642
|
+
const hiBit = s3 & 1;
|
|
22643
|
+
return {
|
|
22644
|
+
s3: s2 << 31 | s3 >>> 1,
|
|
22645
|
+
s2: s1 << 31 | s2 >>> 1,
|
|
22646
|
+
s1: s0 << 31 | s1 >>> 1,
|
|
22647
|
+
s0: s0 >>> 1 ^ POLY$1 << 24 & -(hiBit & 1)
|
|
22648
|
+
// reduce % poly
|
|
22649
|
+
};
|
|
22650
|
+
};
|
|
22651
|
+
const swapLE = (n2) => (n2 >>> 0 & 255) << 24 | (n2 >>> 8 & 255) << 16 | (n2 >>> 16 & 255) << 8 | n2 >>> 24 & 255 | 0;
|
|
22652
|
+
function _toGHASHKey(k) {
|
|
22653
|
+
k.reverse();
|
|
22654
|
+
const hiBit = k[15] & 1;
|
|
22655
|
+
let carry = 0;
|
|
22656
|
+
for (let i = 0; i < k.length; i++) {
|
|
22657
|
+
const t = k[i];
|
|
22658
|
+
k[i] = t >>> 1 | carry;
|
|
22659
|
+
carry = (t & 1) << 7;
|
|
22660
|
+
}
|
|
22661
|
+
k[0] ^= -hiBit & 225;
|
|
22662
|
+
return k;
|
|
22663
|
+
}
|
|
22664
|
+
const estimateWindow = (bytes2) => {
|
|
22665
|
+
if (bytes2 > 64 * 1024)
|
|
22666
|
+
return 8;
|
|
22667
|
+
if (bytes2 > 1024)
|
|
22668
|
+
return 4;
|
|
22669
|
+
return 2;
|
|
22670
|
+
};
|
|
22671
|
+
class GHASH {
|
|
22672
|
+
// We select bits per window adaptively based on expectedLength
|
|
22673
|
+
constructor(key, expectedLength) {
|
|
22674
|
+
this.blockLen = BLOCK_SIZE$1;
|
|
22675
|
+
this.outputLen = BLOCK_SIZE$1;
|
|
22676
|
+
this.s0 = 0;
|
|
22677
|
+
this.s1 = 0;
|
|
22678
|
+
this.s2 = 0;
|
|
22679
|
+
this.s3 = 0;
|
|
22680
|
+
this.finished = false;
|
|
22681
|
+
key = toBytes$2(key);
|
|
22682
|
+
bytes$2(key, 16);
|
|
22683
|
+
const kView = createView$2(key);
|
|
22684
|
+
let k0 = kView.getUint32(0, false);
|
|
22685
|
+
let k1 = kView.getUint32(4, false);
|
|
22686
|
+
let k2 = kView.getUint32(8, false);
|
|
22687
|
+
let k3 = kView.getUint32(12, false);
|
|
22688
|
+
const doubles = [];
|
|
22689
|
+
for (let i = 0; i < 128; i++) {
|
|
22690
|
+
doubles.push({ s0: swapLE(k0), s1: swapLE(k1), s2: swapLE(k2), s3: swapLE(k3) });
|
|
22691
|
+
({ s0: k0, s1: k1, s2: k2, s3: k3 } = mul2$1(k0, k1, k2, k3));
|
|
22692
|
+
}
|
|
22693
|
+
const W = estimateWindow(expectedLength || 1024);
|
|
22694
|
+
if (![1, 2, 4, 8].includes(W))
|
|
22695
|
+
throw new Error(`ghash: wrong window size=${W}, should be 2, 4 or 8`);
|
|
22696
|
+
this.W = W;
|
|
22697
|
+
const bits = 128;
|
|
22698
|
+
const windows = bits / W;
|
|
22699
|
+
const windowSize = this.windowSize = 2 ** W;
|
|
22700
|
+
const items = [];
|
|
22701
|
+
for (let w = 0; w < windows; w++) {
|
|
22702
|
+
for (let byte = 0; byte < windowSize; byte++) {
|
|
22703
|
+
let s0 = 0, s1 = 0, s2 = 0, s3 = 0;
|
|
22704
|
+
for (let j = 0; j < W; j++) {
|
|
22705
|
+
const bit = byte >>> W - j - 1 & 1;
|
|
22706
|
+
if (!bit)
|
|
22707
|
+
continue;
|
|
22708
|
+
const { s0: d0, s1: d1, s2: d2, s3: d3 } = doubles[W * w + j];
|
|
22709
|
+
s0 ^= d0, s1 ^= d1, s2 ^= d2, s3 ^= d3;
|
|
22710
|
+
}
|
|
22711
|
+
items.push({ s0, s1, s2, s3 });
|
|
22712
|
+
}
|
|
22713
|
+
}
|
|
22714
|
+
this.t = items;
|
|
22715
|
+
}
|
|
22716
|
+
_updateBlock(s0, s1, s2, s3) {
|
|
22717
|
+
s0 ^= this.s0, s1 ^= this.s1, s2 ^= this.s2, s3 ^= this.s3;
|
|
22718
|
+
const { W, t, windowSize } = this;
|
|
22719
|
+
let o0 = 0, o1 = 0, o2 = 0, o3 = 0;
|
|
22720
|
+
const mask2 = (1 << W) - 1;
|
|
22721
|
+
let w = 0;
|
|
22722
|
+
for (const num2 of [s0, s1, s2, s3]) {
|
|
22723
|
+
for (let bytePos = 0; bytePos < 4; bytePos++) {
|
|
22724
|
+
const byte = num2 >>> 8 * bytePos & 255;
|
|
22725
|
+
for (let bitPos = 8 / W - 1; bitPos >= 0; bitPos--) {
|
|
22726
|
+
const bit = byte >>> W * bitPos & mask2;
|
|
22727
|
+
const { s0: e0, s1: e1, s2: e2, s3: e3 } = t[w * windowSize + bit];
|
|
22728
|
+
o0 ^= e0, o1 ^= e1, o2 ^= e2, o3 ^= e3;
|
|
22729
|
+
w += 1;
|
|
22730
|
+
}
|
|
22731
|
+
}
|
|
22732
|
+
}
|
|
22733
|
+
this.s0 = o0;
|
|
22734
|
+
this.s1 = o1;
|
|
22735
|
+
this.s2 = o2;
|
|
22736
|
+
this.s3 = o3;
|
|
22737
|
+
}
|
|
22738
|
+
update(data) {
|
|
22739
|
+
data = toBytes$2(data);
|
|
22740
|
+
exists$2(this);
|
|
22741
|
+
const b32 = u32$1(data);
|
|
22742
|
+
const blocks = Math.floor(data.length / BLOCK_SIZE$1);
|
|
22743
|
+
const left = data.length % BLOCK_SIZE$1;
|
|
22744
|
+
for (let i = 0; i < blocks; i++) {
|
|
22745
|
+
this._updateBlock(b32[i * 4 + 0], b32[i * 4 + 1], b32[i * 4 + 2], b32[i * 4 + 3]);
|
|
22746
|
+
}
|
|
22747
|
+
if (left) {
|
|
22748
|
+
ZEROS16.set(data.subarray(blocks * BLOCK_SIZE$1));
|
|
22749
|
+
this._updateBlock(ZEROS32[0], ZEROS32[1], ZEROS32[2], ZEROS32[3]);
|
|
22750
|
+
ZEROS32.fill(0);
|
|
22751
|
+
}
|
|
22752
|
+
return this;
|
|
22753
|
+
}
|
|
22754
|
+
destroy() {
|
|
22755
|
+
const { t } = this;
|
|
22756
|
+
for (const elm of t) {
|
|
22757
|
+
elm.s0 = 0, elm.s1 = 0, elm.s2 = 0, elm.s3 = 0;
|
|
22758
|
+
}
|
|
22759
|
+
}
|
|
22760
|
+
digestInto(out) {
|
|
22761
|
+
exists$2(this);
|
|
22762
|
+
output$2(out, this);
|
|
22763
|
+
this.finished = true;
|
|
22764
|
+
const { s0, s1, s2, s3 } = this;
|
|
22765
|
+
const o32 = u32$1(out);
|
|
22766
|
+
o32[0] = s0;
|
|
22767
|
+
o32[1] = s1;
|
|
22768
|
+
o32[2] = s2;
|
|
22769
|
+
o32[3] = s3;
|
|
22770
|
+
return out;
|
|
22771
|
+
}
|
|
22772
|
+
digest() {
|
|
22773
|
+
const res = new Uint8Array(BLOCK_SIZE$1);
|
|
22774
|
+
this.digestInto(res);
|
|
22775
|
+
this.destroy();
|
|
22776
|
+
return res;
|
|
22777
|
+
}
|
|
22778
|
+
}
|
|
22779
|
+
class Polyval extends GHASH {
|
|
22780
|
+
constructor(key, expectedLength) {
|
|
22781
|
+
key = toBytes$2(key);
|
|
22782
|
+
const ghKey = _toGHASHKey(key.slice());
|
|
22783
|
+
super(ghKey, expectedLength);
|
|
22784
|
+
ghKey.fill(0);
|
|
22785
|
+
}
|
|
22786
|
+
update(data) {
|
|
22787
|
+
data = toBytes$2(data);
|
|
22788
|
+
exists$2(this);
|
|
22789
|
+
const b32 = u32$1(data);
|
|
22790
|
+
const left = data.length % BLOCK_SIZE$1;
|
|
22791
|
+
const blocks = Math.floor(data.length / BLOCK_SIZE$1);
|
|
22792
|
+
for (let i = 0; i < blocks; i++) {
|
|
22793
|
+
this._updateBlock(swapLE(b32[i * 4 + 3]), swapLE(b32[i * 4 + 2]), swapLE(b32[i * 4 + 1]), swapLE(b32[i * 4 + 0]));
|
|
22794
|
+
}
|
|
22795
|
+
if (left) {
|
|
22796
|
+
ZEROS16.set(data.subarray(blocks * BLOCK_SIZE$1));
|
|
22797
|
+
this._updateBlock(swapLE(ZEROS32[3]), swapLE(ZEROS32[2]), swapLE(ZEROS32[1]), swapLE(ZEROS32[0]));
|
|
22798
|
+
ZEROS32.fill(0);
|
|
22799
|
+
}
|
|
22800
|
+
return this;
|
|
22801
|
+
}
|
|
22802
|
+
digestInto(out) {
|
|
22803
|
+
exists$2(this);
|
|
22804
|
+
output$2(out, this);
|
|
22805
|
+
this.finished = true;
|
|
22806
|
+
const { s0, s1, s2, s3 } = this;
|
|
22807
|
+
const o32 = u32$1(out);
|
|
22808
|
+
o32[0] = s0;
|
|
22809
|
+
o32[1] = s1;
|
|
22810
|
+
o32[2] = s2;
|
|
22811
|
+
o32[3] = s3;
|
|
22812
|
+
return out.reverse();
|
|
22813
|
+
}
|
|
22814
|
+
}
|
|
22815
|
+
function wrapConstructorWithKey(hashCons) {
|
|
22816
|
+
const hashC = (msg, key) => hashCons(key, msg.length).update(toBytes$2(msg)).digest();
|
|
22817
|
+
const tmp = hashCons(new Uint8Array(16), 0);
|
|
22818
|
+
hashC.outputLen = tmp.outputLen;
|
|
22819
|
+
hashC.blockLen = tmp.blockLen;
|
|
22820
|
+
hashC.create = (key, expectedLength) => hashCons(key, expectedLength);
|
|
22821
|
+
return hashC;
|
|
22822
|
+
}
|
|
22823
|
+
const ghash = wrapConstructorWithKey((key, expectedLength) => new GHASH(key, expectedLength));
|
|
22824
|
+
wrapConstructorWithKey((key, expectedLength) => new Polyval(key, expectedLength));
|
|
22825
|
+
const BLOCK_SIZE = 16;
|
|
22826
|
+
const BLOCK_SIZE32 = 4;
|
|
22827
|
+
const EMPTY_BLOCK = new Uint8Array(BLOCK_SIZE);
|
|
22828
|
+
const POLY = 283;
|
|
22829
|
+
function mul2(n2) {
|
|
22830
|
+
return n2 << 1 ^ POLY & -(n2 >> 7);
|
|
22831
|
+
}
|
|
22832
|
+
function mul(a, b2) {
|
|
22833
|
+
let res = 0;
|
|
22834
|
+
for (; b2 > 0; b2 >>= 1) {
|
|
22835
|
+
res ^= a & -(b2 & 1);
|
|
22836
|
+
a = mul2(a);
|
|
22837
|
+
}
|
|
22838
|
+
return res;
|
|
22839
|
+
}
|
|
22840
|
+
const sbox = /* @__PURE__ */ (() => {
|
|
22841
|
+
let t = new Uint8Array(256);
|
|
22842
|
+
for (let i = 0, x = 1; i < 256; i++, x ^= mul2(x))
|
|
22843
|
+
t[i] = x;
|
|
22844
|
+
const box = new Uint8Array(256);
|
|
22845
|
+
box[0] = 99;
|
|
22846
|
+
for (let i = 0; i < 255; i++) {
|
|
22847
|
+
let x = t[255 - i];
|
|
22848
|
+
x |= x << 8;
|
|
22849
|
+
box[t[i]] = (x ^ x >> 4 ^ x >> 5 ^ x >> 6 ^ x >> 7 ^ 99) & 255;
|
|
22850
|
+
}
|
|
22851
|
+
return box;
|
|
22852
|
+
})();
|
|
22853
|
+
const rotr32_8 = (n2) => n2 << 24 | n2 >>> 8;
|
|
22854
|
+
const rotl32_8 = (n2) => n2 << 8 | n2 >>> 24;
|
|
22855
|
+
function genTtable(sbox2, fn) {
|
|
22856
|
+
if (sbox2.length !== 256)
|
|
22857
|
+
throw new Error("Wrong sbox length");
|
|
22858
|
+
const T0 = new Uint32Array(256).map((_, j) => fn(sbox2[j]));
|
|
22859
|
+
const T1 = T0.map(rotl32_8);
|
|
22860
|
+
const T2 = T1.map(rotl32_8);
|
|
22861
|
+
const T3 = T2.map(rotl32_8);
|
|
22862
|
+
const T01 = new Uint32Array(256 * 256);
|
|
22863
|
+
const T23 = new Uint32Array(256 * 256);
|
|
22864
|
+
const sbox22 = new Uint16Array(256 * 256);
|
|
22865
|
+
for (let i = 0; i < 256; i++) {
|
|
22866
|
+
for (let j = 0; j < 256; j++) {
|
|
22867
|
+
const idx = i * 256 + j;
|
|
22868
|
+
T01[idx] = T0[i] ^ T1[j];
|
|
22869
|
+
T23[idx] = T2[i] ^ T3[j];
|
|
22870
|
+
sbox22[idx] = sbox2[i] << 8 | sbox2[j];
|
|
22871
|
+
}
|
|
22872
|
+
}
|
|
22873
|
+
return { sbox: sbox2, sbox2: sbox22, T0, T1, T2, T3, T01, T23 };
|
|
22874
|
+
}
|
|
22875
|
+
const tableEncoding = /* @__PURE__ */ genTtable(sbox, (s) => mul(s, 3) << 24 | s << 16 | s << 8 | mul(s, 2));
|
|
22876
|
+
const xPowers = /* @__PURE__ */ (() => {
|
|
22877
|
+
const p = new Uint8Array(16);
|
|
22878
|
+
for (let i = 0, x = 1; i < 16; i++, x = mul2(x))
|
|
22879
|
+
p[i] = x;
|
|
22880
|
+
return p;
|
|
22881
|
+
})();
|
|
22882
|
+
function expandKeyLE(key) {
|
|
22883
|
+
bytes$2(key);
|
|
22884
|
+
const len = key.length;
|
|
22885
|
+
if (![16, 24, 32].includes(len))
|
|
22886
|
+
throw new Error(`aes: wrong key size: should be 16, 24 or 32, got: ${len}`);
|
|
22887
|
+
const { sbox2 } = tableEncoding;
|
|
22888
|
+
const k32 = u32$1(key);
|
|
22889
|
+
const Nk = k32.length;
|
|
22890
|
+
const subByte = (n2) => applySbox(sbox2, n2, n2, n2, n2);
|
|
22891
|
+
const xk = new Uint32Array(len + 28);
|
|
22892
|
+
xk.set(k32);
|
|
22893
|
+
for (let i = Nk; i < xk.length; i++) {
|
|
22894
|
+
let t = xk[i - 1];
|
|
22895
|
+
if (i % Nk === 0)
|
|
22896
|
+
t = subByte(rotr32_8(t)) ^ xPowers[i / Nk - 1];
|
|
22897
|
+
else if (Nk > 6 && i % Nk === 4)
|
|
22898
|
+
t = subByte(t);
|
|
22899
|
+
xk[i] = xk[i - Nk] ^ t;
|
|
22900
|
+
}
|
|
22901
|
+
return xk;
|
|
22902
|
+
}
|
|
22903
|
+
function apply0123(T01, T23, s0, s1, s2, s3) {
|
|
22904
|
+
return T01[s0 << 8 & 65280 | s1 >>> 8 & 255] ^ T23[s2 >>> 8 & 65280 | s3 >>> 24 & 255];
|
|
22905
|
+
}
|
|
22906
|
+
function applySbox(sbox2, s0, s1, s2, s3) {
|
|
22907
|
+
return sbox2[s0 & 255 | s1 & 65280] | sbox2[s2 >>> 16 & 255 | s3 >>> 16 & 65280] << 16;
|
|
22908
|
+
}
|
|
22909
|
+
function encrypt(xk, s0, s1, s2, s3) {
|
|
22910
|
+
const { sbox2, T01, T23 } = tableEncoding;
|
|
22911
|
+
let k = 0;
|
|
22912
|
+
s0 ^= xk[k++], s1 ^= xk[k++], s2 ^= xk[k++], s3 ^= xk[k++];
|
|
22913
|
+
const rounds = xk.length / 4 - 2;
|
|
22914
|
+
for (let i = 0; i < rounds; i++) {
|
|
22915
|
+
const t02 = xk[k++] ^ apply0123(T01, T23, s0, s1, s2, s3);
|
|
22916
|
+
const t12 = xk[k++] ^ apply0123(T01, T23, s1, s2, s3, s0);
|
|
22917
|
+
const t22 = xk[k++] ^ apply0123(T01, T23, s2, s3, s0, s1);
|
|
22918
|
+
const t32 = xk[k++] ^ apply0123(T01, T23, s3, s0, s1, s2);
|
|
22919
|
+
s0 = t02, s1 = t12, s2 = t22, s3 = t32;
|
|
22920
|
+
}
|
|
22921
|
+
const t0 = xk[k++] ^ applySbox(sbox2, s0, s1, s2, s3);
|
|
22922
|
+
const t1 = xk[k++] ^ applySbox(sbox2, s1, s2, s3, s0);
|
|
22923
|
+
const t2 = xk[k++] ^ applySbox(sbox2, s2, s3, s0, s1);
|
|
22924
|
+
const t3 = xk[k++] ^ applySbox(sbox2, s3, s0, s1, s2);
|
|
22925
|
+
return { s0: t0, s1: t1, s2: t2, s3: t3 };
|
|
22926
|
+
}
|
|
22927
|
+
function getDst(len, dst) {
|
|
22928
|
+
if (!dst)
|
|
22929
|
+
return new Uint8Array(len);
|
|
22930
|
+
bytes$2(dst);
|
|
22931
|
+
if (dst.length < len)
|
|
22932
|
+
throw new Error(`aes: wrong destination length, expected at least ${len}, got: ${dst.length}`);
|
|
22933
|
+
return dst;
|
|
22934
|
+
}
|
|
22935
|
+
function ctr32(xk, isLE2, nonce, src, dst) {
|
|
22936
|
+
bytes$2(nonce, BLOCK_SIZE);
|
|
22937
|
+
bytes$2(src);
|
|
22938
|
+
dst = getDst(src.length, dst);
|
|
22939
|
+
const ctr = nonce;
|
|
22940
|
+
const c32 = u32$1(ctr);
|
|
22941
|
+
const view = createView$2(ctr);
|
|
22942
|
+
const src32 = u32$1(src);
|
|
22943
|
+
const dst32 = u32$1(dst);
|
|
22944
|
+
const ctrPos = isLE2 ? 0 : 12;
|
|
22945
|
+
const srcLen = src.length;
|
|
22946
|
+
let ctrNum = view.getUint32(ctrPos, isLE2);
|
|
22947
|
+
let { s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]);
|
|
22948
|
+
for (let i = 0; i + 4 <= src32.length; i += 4) {
|
|
22949
|
+
dst32[i + 0] = src32[i + 0] ^ s0;
|
|
22950
|
+
dst32[i + 1] = src32[i + 1] ^ s1;
|
|
22951
|
+
dst32[i + 2] = src32[i + 2] ^ s2;
|
|
22952
|
+
dst32[i + 3] = src32[i + 3] ^ s3;
|
|
22953
|
+
ctrNum = ctrNum + 1 >>> 0;
|
|
22954
|
+
view.setUint32(ctrPos, ctrNum, isLE2);
|
|
22955
|
+
({ s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]));
|
|
22956
|
+
}
|
|
22957
|
+
const start = BLOCK_SIZE * Math.floor(src32.length / BLOCK_SIZE32);
|
|
22958
|
+
if (start < srcLen) {
|
|
22959
|
+
const b32 = new Uint32Array([s0, s1, s2, s3]);
|
|
22960
|
+
const buf = u8(b32);
|
|
22961
|
+
for (let i = start, pos = 0; i < srcLen; i++, pos++)
|
|
22962
|
+
dst[i] = src[i] ^ buf[pos];
|
|
22963
|
+
}
|
|
22964
|
+
return dst;
|
|
22965
|
+
}
|
|
22966
|
+
function computeTag(fn, isLE2, key, data, AAD) {
|
|
22967
|
+
const h = fn.create(key, data.length + (AAD?.length || 0));
|
|
22968
|
+
if (AAD)
|
|
22969
|
+
h.update(AAD);
|
|
22970
|
+
h.update(data);
|
|
22971
|
+
const num2 = new Uint8Array(16);
|
|
22972
|
+
const view = createView$2(num2);
|
|
22973
|
+
if (AAD)
|
|
22974
|
+
setBigUint64$1(view, 0, BigInt(AAD.length * 8), isLE2);
|
|
22975
|
+
setBigUint64$1(view, 8, BigInt(data.length * 8), isLE2);
|
|
22976
|
+
h.update(num2);
|
|
22977
|
+
return h.digest();
|
|
22978
|
+
}
|
|
22979
|
+
const gcm = /* @__PURE__ */ wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function gcm2(key, nonce, AAD) {
|
|
22980
|
+
bytes$2(nonce);
|
|
22981
|
+
if (nonce.length === 0)
|
|
22982
|
+
throw new Error("aes/gcm: empty nonce");
|
|
22983
|
+
const tagLength = 16;
|
|
22984
|
+
function _computeTag(authKey, tagMask, data) {
|
|
22985
|
+
const tag = computeTag(ghash, false, authKey, data, AAD);
|
|
22986
|
+
for (let i = 0; i < tagMask.length; i++)
|
|
22987
|
+
tag[i] ^= tagMask[i];
|
|
22988
|
+
return tag;
|
|
22989
|
+
}
|
|
22990
|
+
function deriveKeys() {
|
|
22991
|
+
const xk = expandKeyLE(key);
|
|
22992
|
+
const authKey = EMPTY_BLOCK.slice();
|
|
22993
|
+
const counter = EMPTY_BLOCK.slice();
|
|
22994
|
+
ctr32(xk, false, counter, counter, authKey);
|
|
22995
|
+
if (nonce.length === 12) {
|
|
22996
|
+
counter.set(nonce);
|
|
22997
|
+
} else {
|
|
22998
|
+
const nonceLen = EMPTY_BLOCK.slice();
|
|
22999
|
+
const view = createView$2(nonceLen);
|
|
23000
|
+
setBigUint64$1(view, 8, BigInt(nonce.length * 8), false);
|
|
23001
|
+
ghash.create(authKey).update(nonce).update(nonceLen).digestInto(counter);
|
|
23002
|
+
}
|
|
23003
|
+
const tagMask = ctr32(xk, false, counter, EMPTY_BLOCK);
|
|
23004
|
+
return { xk, authKey, counter, tagMask };
|
|
23005
|
+
}
|
|
23006
|
+
return {
|
|
23007
|
+
encrypt: (plaintext) => {
|
|
23008
|
+
bytes$2(plaintext);
|
|
23009
|
+
const { xk, authKey, counter, tagMask } = deriveKeys();
|
|
23010
|
+
const out = new Uint8Array(plaintext.length + tagLength);
|
|
23011
|
+
ctr32(xk, false, counter, plaintext, out);
|
|
23012
|
+
const tag = _computeTag(authKey, tagMask, out.subarray(0, out.length - tagLength));
|
|
23013
|
+
out.set(tag, plaintext.length);
|
|
23014
|
+
xk.fill(0);
|
|
23015
|
+
return out;
|
|
23016
|
+
},
|
|
23017
|
+
decrypt: (ciphertext) => {
|
|
23018
|
+
bytes$2(ciphertext);
|
|
23019
|
+
if (ciphertext.length < tagLength)
|
|
23020
|
+
throw new Error(`aes/gcm: ciphertext less than tagLen (${tagLength})`);
|
|
23021
|
+
const { xk, authKey, counter, tagMask } = deriveKeys();
|
|
23022
|
+
const data = ciphertext.subarray(0, -tagLength);
|
|
23023
|
+
const passedTag = ciphertext.subarray(-tagLength);
|
|
23024
|
+
const tag = _computeTag(authKey, tagMask, data);
|
|
23025
|
+
if (!equalBytes(tag, passedTag))
|
|
23026
|
+
throw new Error("aes/gcm: invalid ghash tag");
|
|
23027
|
+
const out = ctr32(xk, false, counter, data);
|
|
23028
|
+
authKey.fill(0);
|
|
23029
|
+
tagMask.fill(0);
|
|
23030
|
+
xk.fill(0);
|
|
23031
|
+
return out;
|
|
23032
|
+
}
|
|
23033
|
+
};
|
|
23034
|
+
});
|
|
23035
|
+
const cr = typeof globalThis === "object" && "crypto" in globalThis ? globalThis.crypto : void 0;
|
|
23036
|
+
function randomBytes$1(bytesLength = 32) {
|
|
23037
|
+
if (cr && typeof cr.getRandomValues === "function")
|
|
23038
|
+
return cr.getRandomValues(new Uint8Array(bytesLength));
|
|
23039
|
+
throw new Error("crypto.getRandomValues must be defined");
|
|
23040
|
+
}
|
|
23041
|
+
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
23042
|
+
const u8a$1 = (a) => a instanceof Uint8Array;
|
|
23043
|
+
const createView$1 = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
23044
|
+
const rotr$1 = (word, shift) => word << 32 - shift | word >>> shift;
|
|
23045
|
+
const isLE$1 = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68;
|
|
23046
|
+
if (!isLE$1)
|
|
23047
|
+
throw new Error("Non little-endian hardware is not supported");
|
|
23048
|
+
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, "0"));
|
|
23049
|
+
function bytesToHex$1(bytes2) {
|
|
23050
|
+
if (!u8a$1(bytes2))
|
|
23051
|
+
throw new Error("Uint8Array expected");
|
|
23052
|
+
let hex2 = "";
|
|
23053
|
+
for (let i = 0; i < bytes2.length; i++) {
|
|
23054
|
+
hex2 += hexes[bytes2[i]];
|
|
23055
|
+
}
|
|
23056
|
+
return hex2;
|
|
23057
|
+
}
|
|
23058
|
+
function hexToBytes$1(hex2) {
|
|
23059
|
+
if (typeof hex2 !== "string")
|
|
23060
|
+
throw new Error("hex string expected, got " + typeof hex2);
|
|
23061
|
+
const len = hex2.length;
|
|
23062
|
+
if (len % 2)
|
|
23063
|
+
throw new Error("padded hex string expected, got unpadded hex of length " + len);
|
|
23064
|
+
const array = new Uint8Array(len / 2);
|
|
23065
|
+
for (let i = 0; i < array.length; i++) {
|
|
23066
|
+
const j = i * 2;
|
|
23067
|
+
const hexByte = hex2.slice(j, j + 2);
|
|
23068
|
+
const byte = Number.parseInt(hexByte, 16);
|
|
23069
|
+
if (Number.isNaN(byte) || byte < 0)
|
|
23070
|
+
throw new Error("Invalid byte sequence");
|
|
23071
|
+
array[i] = byte;
|
|
23072
|
+
}
|
|
23073
|
+
return array;
|
|
23074
|
+
}
|
|
23075
|
+
function utf8ToBytes$1(str2) {
|
|
23076
|
+
if (typeof str2 !== "string")
|
|
23077
|
+
throw new Error(`utf8ToBytes expected string, got ${typeof str2}`);
|
|
23078
|
+
return new Uint8Array(new TextEncoder().encode(str2));
|
|
23079
|
+
}
|
|
23080
|
+
function toBytes$1(data) {
|
|
23081
|
+
if (typeof data === "string")
|
|
23082
|
+
data = utf8ToBytes$1(data);
|
|
23083
|
+
if (!u8a$1(data))
|
|
23084
|
+
throw new Error(`expected Uint8Array, got ${typeof data}`);
|
|
23085
|
+
return data;
|
|
23086
|
+
}
|
|
23087
|
+
function concatBytes$1(...arrays) {
|
|
23088
|
+
const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
|
|
23089
|
+
let pad = 0;
|
|
23090
|
+
arrays.forEach((a) => {
|
|
23091
|
+
if (!u8a$1(a))
|
|
23092
|
+
throw new Error("Uint8Array expected");
|
|
23093
|
+
r.set(a, pad);
|
|
23094
|
+
pad += a.length;
|
|
23095
|
+
});
|
|
23096
|
+
return r;
|
|
23097
|
+
}
|
|
23098
|
+
let Hash$1 = class Hash5 {
|
|
23099
|
+
// Safe version that clones internal state
|
|
23100
|
+
clone() {
|
|
23101
|
+
return this._cloneInto();
|
|
23102
|
+
}
|
|
23103
|
+
};
|
|
23104
|
+
function wrapConstructor$1(hashCons) {
|
|
23105
|
+
const hashC = (msg) => hashCons().update(toBytes$1(msg)).digest();
|
|
23106
|
+
const tmp = hashCons();
|
|
23107
|
+
hashC.outputLen = tmp.outputLen;
|
|
23108
|
+
hashC.blockLen = tmp.blockLen;
|
|
23109
|
+
hashC.create = () => hashCons();
|
|
23110
|
+
return hashC;
|
|
23111
|
+
}
|
|
23112
|
+
function hexToBytes(hex2) {
|
|
23113
|
+
const bytes2 = new Uint8Array(hex2.length / 2);
|
|
23114
|
+
for (let i = 0; i < hex2.length; i += 2) {
|
|
23115
|
+
bytes2[i / 2] = parseInt(hex2.substr(i, 2), 16);
|
|
23116
|
+
}
|
|
23117
|
+
return bytes2;
|
|
23118
|
+
}
|
|
23119
|
+
function bytesToHex(bytes2) {
|
|
23120
|
+
return Array.from(bytes2).map((b2) => b2.toString(16).padStart(2, "0")).join("");
|
|
23121
|
+
}
|
|
23122
|
+
function parseNpubOrHex(input) {
|
|
23123
|
+
const trimmed = input?.trim();
|
|
23124
|
+
if (!trimmed) {
|
|
23125
|
+
return { valid: false, error: "Public key is required" };
|
|
23126
|
+
}
|
|
23127
|
+
if (/^[0-9a-fA-F]{64}$/.test(trimmed)) {
|
|
23128
|
+
return { valid: true, hexPubKey: trimmed.toLowerCase() };
|
|
23129
|
+
}
|
|
23130
|
+
let npubString = trimmed;
|
|
23131
|
+
if (npubString.startsWith("nostr:")) {
|
|
23132
|
+
npubString = npubString.slice(6);
|
|
23133
|
+
}
|
|
23134
|
+
if (npubString.startsWith("npub1")) {
|
|
23135
|
+
try {
|
|
23136
|
+
const decoded = nip19.decode(npubString);
|
|
23137
|
+
if (decoded.type === "npub") {
|
|
23138
|
+
return { valid: true, hexPubKey: decoded.data };
|
|
23139
|
+
}
|
|
23140
|
+
return { valid: false, error: "Invalid npub format" };
|
|
23141
|
+
} catch (e) {
|
|
23142
|
+
return { valid: false, error: "Invalid npub: unable to decode" };
|
|
23143
|
+
}
|
|
23144
|
+
}
|
|
23145
|
+
return { valid: false, error: "Enter a valid npub (npub1...) or 64-character hex public key" };
|
|
23146
|
+
}
|
|
23147
|
+
function hexToNpub(hexPubKey) {
|
|
23148
|
+
try {
|
|
23149
|
+
return nip19.npubEncode(hexPubKey);
|
|
23150
|
+
} catch (e) {
|
|
23151
|
+
console.error("Failed to encode hex to npub:", e);
|
|
23152
|
+
return hexPubKey;
|
|
23153
|
+
}
|
|
23154
|
+
}
|
|
23155
|
+
function npubToHex(npub2) {
|
|
23156
|
+
try {
|
|
23157
|
+
const decoded = nip19.decode(npub2);
|
|
23158
|
+
if (decoded.type === "npub") {
|
|
23159
|
+
return decoded.data;
|
|
23160
|
+
}
|
|
23161
|
+
return null;
|
|
23162
|
+
} catch (e) {
|
|
23163
|
+
return null;
|
|
23164
|
+
}
|
|
23165
|
+
}
|
|
23166
|
+
function shortenPubKey(pubKey) {
|
|
23167
|
+
if (!pubKey || pubKey.length <= 20) return pubKey || "";
|
|
23168
|
+
return `${pubKey.slice(0, 8)}...${pubKey.slice(-8)}`;
|
|
23169
|
+
}
|
|
23170
|
+
function shortenNpub(npub2) {
|
|
23171
|
+
if (!npub2 || npub2.length <= 20) return npub2 || "";
|
|
23172
|
+
return `${npub2.slice(0, 12)}...${npub2.slice(-8)}`;
|
|
23173
|
+
}
|
|
23174
|
+
function getPublicKey$1(privateKey) {
|
|
23175
|
+
return getPublicKey$2(hexToBytes(privateKey));
|
|
23176
|
+
}
|
|
23177
|
+
async function encryptNIP04(privateKey, recipientPubKey, content) {
|
|
23178
|
+
return await nip04.encrypt(privateKey, recipientPubKey, content);
|
|
23179
|
+
}
|
|
23180
|
+
async function decryptNIP04(privateKey, senderPubKey, encryptedContent) {
|
|
23181
|
+
return await nip04.decrypt(privateKey, senderPubKey, encryptedContent);
|
|
23182
|
+
}
|
|
23183
|
+
function encryptNIP44(privateKey, recipientPubKey, content) {
|
|
23184
|
+
const privKeyBytes = hexToBytes(privateKey);
|
|
23185
|
+
const conversationKey = nip44.v2.utils.getConversationKey(privKeyBytes, recipientPubKey);
|
|
23186
|
+
return nip44.v2.encrypt(content, conversationKey);
|
|
23187
|
+
}
|
|
23188
|
+
function decryptNIP44(privateKey, senderPubKey, encryptedContent) {
|
|
23189
|
+
const privKeyBytes = hexToBytes(privateKey);
|
|
23190
|
+
const conversationKey = nip44.v2.utils.getConversationKey(privKeyBytes, senderPubKey);
|
|
23191
|
+
return nip44.v2.decrypt(encryptedContent, conversationKey);
|
|
23192
|
+
}
|
|
23193
|
+
function createSignedEvent(kind2, content, tags, privateKey) {
|
|
23194
|
+
const event = {
|
|
23195
|
+
kind: kind2,
|
|
23196
|
+
content,
|
|
23197
|
+
tags,
|
|
23198
|
+
created_at: Math.floor(Date.now() / 1e3)
|
|
23199
|
+
};
|
|
23200
|
+
return finalizeEvent(event, hexToBytes(privateKey));
|
|
23201
|
+
}
|
|
23202
|
+
function createDMEvent(recipientPubKey, encryptedContent, privateKey) {
|
|
23203
|
+
return createSignedEvent(
|
|
23204
|
+
4,
|
|
23205
|
+
// NIP-04 encrypted DM
|
|
23206
|
+
encryptedContent,
|
|
23207
|
+
[["p", recipientPubKey]],
|
|
23208
|
+
privateKey
|
|
23209
|
+
);
|
|
23210
|
+
}
|
|
23211
|
+
function isValidHexPubKey(str2) {
|
|
23212
|
+
return typeof str2 === "string" && /^[0-9a-fA-F]{64}$/.test(str2);
|
|
23213
|
+
}
|
|
23214
|
+
function isValidNpub(str2) {
|
|
23215
|
+
if (typeof str2 !== "string" || !str2.startsWith("npub1")) {
|
|
23216
|
+
return false;
|
|
23217
|
+
}
|
|
23218
|
+
try {
|
|
23219
|
+
const decoded = nip19.decode(str2);
|
|
23220
|
+
return decoded.type === "npub";
|
|
23221
|
+
} catch {
|
|
23222
|
+
return false;
|
|
23223
|
+
}
|
|
23224
|
+
}
|
|
23225
|
+
function generateNonce$1() {
|
|
23226
|
+
return Date.now().toString(36) + Math.random().toString(36).substring(2, 15);
|
|
23227
|
+
}
|
|
23228
|
+
function signEvent(event, privateKey) {
|
|
23229
|
+
const eventToSign = {
|
|
23230
|
+
kind: event.kind,
|
|
23231
|
+
content: event.content,
|
|
23232
|
+
tags: event.tags || [],
|
|
23233
|
+
created_at: event.created_at || Math.floor(Date.now() / 1e3)
|
|
23234
|
+
};
|
|
23235
|
+
return finalizeEvent(eventToSign, hexToBytes(privateKey));
|
|
23236
|
+
}
|
|
23237
|
+
function verifyEvent(event) {
|
|
23238
|
+
return verifyEvent$1(event);
|
|
23239
|
+
}
|
|
23240
|
+
const nostrUtils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
23241
|
+
__proto__: null,
|
|
23242
|
+
NDK,
|
|
23243
|
+
NDKEvent,
|
|
23244
|
+
NDKPrivateKeySigner,
|
|
23245
|
+
NDKSubscriptionCacheUsage,
|
|
23246
|
+
NDKUser,
|
|
23247
|
+
bytesToHex,
|
|
23248
|
+
createDMEvent,
|
|
23249
|
+
createSignedEvent,
|
|
23250
|
+
decryptNIP04,
|
|
23251
|
+
decryptNIP44,
|
|
23252
|
+
encryptNIP04,
|
|
23253
|
+
encryptNIP44,
|
|
23254
|
+
generateNonce: generateNonce$1,
|
|
23255
|
+
getPublicKey: getPublicKey$1,
|
|
23256
|
+
hexToBytes,
|
|
23257
|
+
hexToNpub,
|
|
23258
|
+
isValidHexPubKey,
|
|
23259
|
+
isValidNpub,
|
|
23260
|
+
npubToHex,
|
|
23261
|
+
parseNpubOrHex,
|
|
23262
|
+
shortenNpub,
|
|
23263
|
+
shortenPubKey,
|
|
23264
|
+
signEvent,
|
|
23265
|
+
verifyEvent
|
|
23266
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
23267
|
+
function generateLensKey() {
|
|
23268
|
+
return randomBytes$1(32);
|
|
23269
|
+
}
|
|
23270
|
+
function uint8ToBase64(bytes2) {
|
|
23271
|
+
if (typeof btoa === "function") {
|
|
23272
|
+
return btoa(String.fromCharCode(...bytes2));
|
|
23273
|
+
}
|
|
23274
|
+
return Buffer.from(bytes2).toString("base64");
|
|
23275
|
+
}
|
|
23276
|
+
function base64ToUint8(base64) {
|
|
23277
|
+
if (typeof atob === "function") {
|
|
23278
|
+
const binary = atob(base64);
|
|
23279
|
+
const bytes2 = new Uint8Array(binary.length);
|
|
23280
|
+
for (let i = 0; i < binary.length; i++) {
|
|
23281
|
+
bytes2[i] = binary.charCodeAt(i);
|
|
23282
|
+
}
|
|
23283
|
+
return bytes2;
|
|
23284
|
+
}
|
|
23285
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
23286
|
+
}
|
|
23287
|
+
function encryptWithKey(key, plaintext) {
|
|
23288
|
+
const iv = randomBytes$1(12);
|
|
23289
|
+
const aes = gcm(key, iv);
|
|
23290
|
+
const plaintextBytes = new TextEncoder().encode(plaintext);
|
|
23291
|
+
const ciphertext = aes.encrypt(plaintextBytes);
|
|
23292
|
+
const combined = new Uint8Array(iv.length + ciphertext.length);
|
|
23293
|
+
combined.set(iv, 0);
|
|
23294
|
+
combined.set(ciphertext, iv.length);
|
|
23295
|
+
return uint8ToBase64(combined);
|
|
23296
|
+
}
|
|
23297
|
+
function decryptWithKey(key, ciphertext) {
|
|
23298
|
+
const data = base64ToUint8(ciphertext);
|
|
23299
|
+
const iv = data.slice(0, 12);
|
|
23300
|
+
const encrypted = data.slice(12);
|
|
23301
|
+
const aes = gcm(key, iv);
|
|
23302
|
+
const decrypted = aes.decrypt(encrypted);
|
|
23303
|
+
return new TextDecoder().decode(decrypted);
|
|
23304
|
+
}
|
|
23305
|
+
function wrapKeyForRecipient(lensKey, senderPrivKey, recipientPubKey) {
|
|
23306
|
+
const keyBase64 = uint8ToBase64(lensKey);
|
|
23307
|
+
return encryptNIP44(senderPrivKey, recipientPubKey, keyBase64);
|
|
23308
|
+
}
|
|
23309
|
+
function unwrapKey(wrappedKey, recipientPrivKey, senderPubKey) {
|
|
23310
|
+
const keyBase64 = decryptNIP44(recipientPrivKey, senderPubKey, wrappedKey);
|
|
23311
|
+
return base64ToUint8(keyBase64);
|
|
23312
|
+
}
|
|
23313
|
+
function serializeLensKey(lensKey, ownerPrivKey, ownerPubKey) {
|
|
23314
|
+
return {
|
|
23315
|
+
wrappedKey: wrapKeyForRecipient(lensKey, ownerPrivKey, ownerPubKey),
|
|
23316
|
+
algorithm: "aes-256-gcm",
|
|
23317
|
+
keyWrap: "nip44",
|
|
23318
|
+
version: "1.0"
|
|
23319
|
+
};
|
|
23320
|
+
}
|
|
23321
|
+
function deserializeLensKey(keyData, ownerPrivKey, ownerPubKey) {
|
|
23322
|
+
if (keyData.version !== "1.0") {
|
|
23323
|
+
throw new Error(`Unsupported lens key version: ${keyData.version}`);
|
|
23324
|
+
}
|
|
23325
|
+
return unwrapKey(keyData.wrappedKey, ownerPrivKey, ownerPubKey);
|
|
23326
|
+
}
|
|
23327
|
+
function isEncrypted(content) {
|
|
23328
|
+
if (!content || typeof content !== "string") return false;
|
|
23329
|
+
const trimmed = content.trim();
|
|
23330
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
23331
|
+
return false;
|
|
23332
|
+
}
|
|
23333
|
+
if (content.length < 38) return false;
|
|
23334
|
+
try {
|
|
23335
|
+
const decoded = base64ToUint8(content);
|
|
23336
|
+
return decoded.length >= 29;
|
|
23337
|
+
} catch {
|
|
23338
|
+
return false;
|
|
23339
|
+
}
|
|
23340
|
+
}
|
|
23341
|
+
const lensKeys = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
23342
|
+
__proto__: null,
|
|
23343
|
+
decryptWithKey,
|
|
23344
|
+
deserializeLensKey,
|
|
23345
|
+
encryptWithKey,
|
|
23346
|
+
generateLensKey,
|
|
23347
|
+
isEncrypted,
|
|
23348
|
+
serializeLensKey,
|
|
23349
|
+
unwrapKey,
|
|
23350
|
+
wrapKeyForRecipient
|
|
23351
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
22583
23352
|
const globalSubscriptions = /* @__PURE__ */ new Map();
|
|
22584
23353
|
const singlePathSubscriptions = /* @__PURE__ */ new Map();
|
|
22585
23354
|
const pendingQueries = /* @__PURE__ */ new Map();
|
|
22586
23355
|
const QUERY_DEDUP_WINDOW = 2e3;
|
|
23356
|
+
function _parseEventContent(event, lensKey = null) {
|
|
23357
|
+
if (!event || !event.content) return null;
|
|
23358
|
+
try {
|
|
23359
|
+
const encryptedTag = event.tags?.find((t) => t[0] === "encrypted");
|
|
23360
|
+
const isSymmetricEncrypted = encryptedTag && encryptedTag[1] === "symmetric";
|
|
23361
|
+
if (isSymmetricEncrypted) {
|
|
23362
|
+
if (!lensKey) {
|
|
23363
|
+
return null;
|
|
23364
|
+
}
|
|
23365
|
+
const decrypted = decryptWithKey(lensKey, event.content);
|
|
23366
|
+
return JSON.parse(decrypted);
|
|
23367
|
+
} else {
|
|
23368
|
+
return JSON.parse(event.content);
|
|
23369
|
+
}
|
|
23370
|
+
} catch (error) {
|
|
23371
|
+
return null;
|
|
23372
|
+
}
|
|
23373
|
+
}
|
|
22587
23374
|
async function nostrPut(client, path, data, kindOrOptions = 3e4) {
|
|
22588
23375
|
const options = typeof kindOrOptions === "number" ? { kind: kindOrOptions } : kindOrOptions;
|
|
22589
23376
|
const kind2 = options.kind || 3e4;
|
|
23377
|
+
const { lensKey } = options;
|
|
23378
|
+
let content;
|
|
23379
|
+
const tags = [["d", path]];
|
|
23380
|
+
if (lensKey) {
|
|
23381
|
+
content = encryptWithKey(lensKey, JSON.stringify(data));
|
|
23382
|
+
tags.push(["encrypted", "symmetric"]);
|
|
23383
|
+
} else {
|
|
23384
|
+
content = JSON.stringify(data);
|
|
23385
|
+
}
|
|
22590
23386
|
const dataEvent = {
|
|
22591
23387
|
kind: kind2,
|
|
22592
23388
|
created_at: Math.floor(Date.now() / 1e3),
|
|
22593
|
-
tags
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
23389
|
+
tags,
|
|
23390
|
+
content
|
|
23391
|
+
};
|
|
23392
|
+
const publishOptions = {
|
|
23393
|
+
...options.signingKey && { signingKey: options.signingKey },
|
|
23394
|
+
...options.waitForRelays && { waitForRelays: options.waitForRelays }
|
|
22598
23395
|
};
|
|
22599
|
-
const publishOptions = options.signingKey ? { signingKey: options.signingKey } : {};
|
|
22600
23396
|
const result = await client.publish(dataEvent, publishOptions);
|
|
22601
23397
|
return result;
|
|
22602
23398
|
}
|
|
22603
23399
|
async function nostrGet(client, path, kind2 = 3e4, options = {}) {
|
|
22604
23400
|
const timeout = options.timeout !== void 0 ? options.timeout : 3e4;
|
|
22605
23401
|
const authors = options.authors || [client.publicKey];
|
|
23402
|
+
const { lensKey } = options;
|
|
22606
23403
|
if (!options.skipCache && client.getCachedByPath) {
|
|
22607
23404
|
const cachedEvent = client.getCachedByPath(path, kind2);
|
|
22608
23405
|
if (cachedEvent && cachedEvent.content) {
|
|
22609
23406
|
if (authors.includes(cachedEvent.pubkey)) {
|
|
22610
|
-
|
|
22611
|
-
|
|
22612
|
-
|
|
22613
|
-
|
|
22614
|
-
|
|
22615
|
-
|
|
22616
|
-
data._author = cachedEvent.pubkey;
|
|
22617
|
-
}
|
|
22618
|
-
return data;
|
|
22619
|
-
} catch (error) {
|
|
23407
|
+
const data = _parseEventContent(cachedEvent, lensKey);
|
|
23408
|
+
if (!data || data._deleted) {
|
|
23409
|
+
return null;
|
|
23410
|
+
}
|
|
23411
|
+
if (options.includeAuthor) {
|
|
23412
|
+
data._author = cachedEvent.pubkey;
|
|
22620
23413
|
}
|
|
23414
|
+
return data;
|
|
22621
23415
|
}
|
|
22622
23416
|
}
|
|
22623
23417
|
}
|
|
@@ -22626,21 +23420,20 @@ async function nostrGet(client, path, kind2 = 3e4, options = {}) {
|
|
|
22626
23420
|
if (persistedEvent && persistedEvent.content) {
|
|
22627
23421
|
if (!authors.includes(persistedEvent.pubkey)) ;
|
|
22628
23422
|
else {
|
|
22629
|
-
|
|
22630
|
-
|
|
22631
|
-
if (
|
|
23423
|
+
const data = _parseEventContent(persistedEvent, lensKey);
|
|
23424
|
+
if (!data || data._deleted) {
|
|
23425
|
+
if (persistedEvent.tags?.find((t) => t[0] === "encrypted") && !lensKey) {
|
|
22632
23426
|
return null;
|
|
22633
23427
|
}
|
|
22634
|
-
|
|
22635
|
-
|
|
22636
|
-
|
|
22637
|
-
|
|
22638
|
-
client.refreshPathInBackground(path, kind2, { authors, timeout });
|
|
22639
|
-
}
|
|
22640
|
-
return data;
|
|
22641
|
-
} catch (error) {
|
|
22642
|
-
console.warn("[nostrGet] Failed to parse persisted event:", error);
|
|
23428
|
+
return null;
|
|
23429
|
+
}
|
|
23430
|
+
if (options.includeAuthor) {
|
|
23431
|
+
data._author = persistedEvent.pubkey;
|
|
22643
23432
|
}
|
|
23433
|
+
if (client.refreshPathInBackground) {
|
|
23434
|
+
client.refreshPathInBackground(path, kind2, { authors, timeout });
|
|
23435
|
+
}
|
|
23436
|
+
return data;
|
|
22644
23437
|
}
|
|
22645
23438
|
}
|
|
22646
23439
|
}
|
|
@@ -22665,6 +23458,7 @@ async function nostrGet(client, path, kind2 = 3e4, options = {}) {
|
|
|
22665
23458
|
return queryPromise;
|
|
22666
23459
|
}
|
|
22667
23460
|
async function _executeNostrGet(client, path, kind2, authors, timeout, options) {
|
|
23461
|
+
const { lensKey } = options;
|
|
22668
23462
|
const filter = {
|
|
22669
23463
|
kinds: [kind2],
|
|
22670
23464
|
authors,
|
|
@@ -22673,29 +23467,26 @@ async function _executeNostrGet(client, path, kind2, authors, timeout, options)
|
|
|
22673
23467
|
limit: authors.length
|
|
22674
23468
|
// Increase limit to get events from all authors
|
|
22675
23469
|
};
|
|
22676
|
-
const events = await client.query(filter, { timeout });
|
|
23470
|
+
const events = await client.query(filter, { timeout, forceRelay: options.forceRelay });
|
|
22677
23471
|
const authoredEvents = events.filter((event2) => authors.includes(event2.pubkey));
|
|
22678
23472
|
if (authoredEvents.length === 0) {
|
|
22679
23473
|
return null;
|
|
22680
23474
|
}
|
|
22681
23475
|
const event = authoredEvents.sort((a, b2) => b2.created_at - a.created_at)[0];
|
|
22682
|
-
|
|
22683
|
-
|
|
22684
|
-
if (!data || data._deleted) {
|
|
22685
|
-
return null;
|
|
22686
|
-
}
|
|
22687
|
-
if (options.includeAuthor) {
|
|
22688
|
-
data._author = event.pubkey;
|
|
22689
|
-
}
|
|
22690
|
-
return data;
|
|
22691
|
-
} catch (error) {
|
|
23476
|
+
const data = _parseEventContent(event, lensKey);
|
|
23477
|
+
if (!data || data._deleted) {
|
|
22692
23478
|
return null;
|
|
22693
23479
|
}
|
|
23480
|
+
if (options.includeAuthor) {
|
|
23481
|
+
data._author = event.pubkey;
|
|
23482
|
+
}
|
|
23483
|
+
return data;
|
|
22694
23484
|
}
|
|
22695
23485
|
async function nostrGetAll(client, pathPrefix, kind2 = 3e4, options = {}) {
|
|
22696
23486
|
const timeout = options.timeout !== void 0 ? options.timeout : 3e4;
|
|
22697
23487
|
const limit2 = options.limit || 1e3;
|
|
22698
23488
|
const authors = options.authors || [client.publicKey];
|
|
23489
|
+
const { lensKey } = options;
|
|
22699
23490
|
if (!options.skipPersistent && client.persistentGetAll) {
|
|
22700
23491
|
const persistedEvents = await client.persistentGetAll(pathPrefix);
|
|
22701
23492
|
if (persistedEvents.length > 0) {
|
|
@@ -22708,18 +23499,15 @@ async function nostrGetAll(client, pathPrefix, kind2 = 3e4, options = {}) {
|
|
|
22708
23499
|
const path = dTag[1];
|
|
22709
23500
|
const existing = byPath.get(path);
|
|
22710
23501
|
if (!existing || event.created_at > existing.created_at) {
|
|
22711
|
-
|
|
22712
|
-
|
|
22713
|
-
|
|
22714
|
-
|
|
22715
|
-
continue;
|
|
22716
|
-
}
|
|
22717
|
-
if (options.includeAuthor) {
|
|
22718
|
-
data._author = event.pubkey;
|
|
22719
|
-
}
|
|
22720
|
-
byPath.set(path, { data, created_at: event.created_at });
|
|
22721
|
-
} catch (error) {
|
|
23502
|
+
const data = _parseEventContent(event, lensKey);
|
|
23503
|
+
if (!data || data._deleted) {
|
|
23504
|
+
byPath.delete(path);
|
|
23505
|
+
continue;
|
|
22722
23506
|
}
|
|
23507
|
+
if (options.includeAuthor) {
|
|
23508
|
+
data._author = event.pubkey;
|
|
23509
|
+
}
|
|
23510
|
+
byPath.set(path, { data, created_at: event.created_at });
|
|
22723
23511
|
}
|
|
22724
23512
|
}
|
|
22725
23513
|
if (client.refreshPrefixInBackground) {
|
|
@@ -22749,6 +23537,7 @@ async function nostrGetAll(client, pathPrefix, kind2 = 3e4, options = {}) {
|
|
|
22749
23537
|
return queryPromise;
|
|
22750
23538
|
}
|
|
22751
23539
|
async function _executeNostrGetAll(client, pathPrefix, kind2, authors, timeout, limit2, options) {
|
|
23540
|
+
const { lensKey } = options;
|
|
22752
23541
|
const filter = {
|
|
22753
23542
|
kinds: [kind2],
|
|
22754
23543
|
authors,
|
|
@@ -22766,18 +23555,15 @@ async function _executeNostrGetAll(client, pathPrefix, kind2, authors, timeout,
|
|
|
22766
23555
|
const dTag = event.tags.find((t) => t[0] === "d")[1];
|
|
22767
23556
|
const existing = byPath.get(dTag);
|
|
22768
23557
|
if (!existing || event.created_at > existing.created_at) {
|
|
22769
|
-
|
|
22770
|
-
|
|
22771
|
-
|
|
22772
|
-
|
|
22773
|
-
continue;
|
|
22774
|
-
}
|
|
22775
|
-
if (options.includeAuthor) {
|
|
22776
|
-
data._author = event.pubkey;
|
|
22777
|
-
}
|
|
22778
|
-
byPath.set(dTag, { data, created_at: event.created_at });
|
|
22779
|
-
} catch (error) {
|
|
23558
|
+
const data = _parseEventContent(event, lensKey);
|
|
23559
|
+
if (!data || data._deleted) {
|
|
23560
|
+
byPath.delete(dTag);
|
|
23561
|
+
continue;
|
|
22780
23562
|
}
|
|
23563
|
+
if (options.includeAuthor) {
|
|
23564
|
+
data._author = event.pubkey;
|
|
23565
|
+
}
|
|
23566
|
+
byPath.set(dTag, { data, created_at: event.created_at });
|
|
22781
23567
|
}
|
|
22782
23568
|
}
|
|
22783
23569
|
return Array.from(byPath.values()).map((item) => item.data);
|
|
@@ -23014,7 +23800,9 @@ async function nostrSubscribe(client, path, callback, options = {}) {
|
|
|
23014
23800
|
}
|
|
23015
23801
|
async function nostrSubscribeMany(client, pathPrefix, callback, options = {}) {
|
|
23016
23802
|
const kind2 = options.kind || 3e4;
|
|
23017
|
-
const
|
|
23803
|
+
const pathParts = pathPrefix.split("/");
|
|
23804
|
+
const targetAuthor = pathParts[1] || client.publicKey;
|
|
23805
|
+
const subscriptionKey = `${targetAuthor}:${kind2}:${pathPrefix}`;
|
|
23018
23806
|
const existingSub = globalSubscriptions.get(subscriptionKey);
|
|
23019
23807
|
if (existingSub) {
|
|
23020
23808
|
existingSub.callbacks.push(callback);
|
|
@@ -23042,14 +23830,14 @@ async function nostrSubscribeMany(client, pathPrefix, callback, options = {}) {
|
|
|
23042
23830
|
return;
|
|
23043
23831
|
}
|
|
23044
23832
|
seenEventIds.add(event.id);
|
|
23045
|
-
if (event.pubkey !==
|
|
23833
|
+
if (event.pubkey !== targetAuthor) {
|
|
23046
23834
|
rejectedCount++;
|
|
23047
23835
|
const now = Date.now();
|
|
23048
23836
|
if (now - lastLogTime > LOG_INTERVAL) {
|
|
23049
23837
|
console.warn("[nostrSubscribeMany] ⚠️ Relay not respecting authors filter!", {
|
|
23050
23838
|
rejectedCount,
|
|
23051
23839
|
acceptedCount,
|
|
23052
|
-
expected:
|
|
23840
|
+
expected: targetAuthor,
|
|
23053
23841
|
message: "Consider using a different relay or implementing private relay"
|
|
23054
23842
|
});
|
|
23055
23843
|
lastLogTime = now;
|
|
@@ -23076,10 +23864,12 @@ async function nostrSubscribeMany(client, pathPrefix, callback, options = {}) {
|
|
|
23076
23864
|
const subscriptionStartTime = Math.floor(Date.now() / 1e3);
|
|
23077
23865
|
const dataFilter = {
|
|
23078
23866
|
kinds: [kind2],
|
|
23079
|
-
authors: [
|
|
23867
|
+
authors: [targetAuthor],
|
|
23868
|
+
// Use target author (holon ID), not client pubkey
|
|
23080
23869
|
since: subscriptionStartTime
|
|
23081
23870
|
// Only get new events after subscription
|
|
23082
23871
|
};
|
|
23872
|
+
console.log("[nostrSubscribeMany] Subscribing to:", { pathPrefix, targetAuthor: targetAuthor.slice(0, 12), clientPubKey: client.publicKey.slice(0, 12) });
|
|
23083
23873
|
const dataSubscription = await client.subscribe(dataFilter, handleDataEvent);
|
|
23084
23874
|
let isDocumentHidden = typeof document !== "undefined" && document.hidden;
|
|
23085
23875
|
let lastVisibilityCheck = subscriptionStartTime;
|
|
@@ -23140,7 +23930,10 @@ function encodePathComponent(component) {
|
|
|
23140
23930
|
}
|
|
23141
23931
|
async function write$1(client, path, data, options = {}) {
|
|
23142
23932
|
try {
|
|
23143
|
-
const putOptions =
|
|
23933
|
+
const putOptions = {
|
|
23934
|
+
...options.signingKey && { signingKey: options.signingKey },
|
|
23935
|
+
...options.waitForRelays && { waitForRelays: options.waitForRelays }
|
|
23936
|
+
};
|
|
23144
23937
|
const result = await nostrPut(client, path, data, putOptions);
|
|
23145
23938
|
const success = result.results.length === 0 ? true : result.results.some((r) => r.status === "fulfilled");
|
|
23146
23939
|
return success;
|
|
@@ -23222,14 +24015,14 @@ async function subscribe$1(client, path, callback, options = {}) {
|
|
|
23222
24015
|
function buildPath(appName, holonId, lensName, key = null) {
|
|
23223
24016
|
return buildPath$1(appName, holonId, lensName, key);
|
|
23224
24017
|
}
|
|
23225
|
-
async function write(client, path, data) {
|
|
24018
|
+
async function write(client, path, data, options = {}) {
|
|
23226
24019
|
if (client.write && client.gun) {
|
|
23227
|
-
return client.write(path, data);
|
|
24020
|
+
return client.write(path, data, options);
|
|
23228
24021
|
}
|
|
23229
24022
|
if (client.gun) {
|
|
23230
24023
|
return write$2(client.gun, path, data);
|
|
23231
24024
|
}
|
|
23232
|
-
return write$1(client, path, data);
|
|
24025
|
+
return write$1(client, path, data, options);
|
|
23233
24026
|
}
|
|
23234
24027
|
async function read(client, path, options = {}) {
|
|
23235
24028
|
if (client.read && client.gun) {
|
|
@@ -23432,77 +24225,6 @@ const assert$1 = {
|
|
|
23432
24225
|
exists: exists$1,
|
|
23433
24226
|
output: output$1
|
|
23434
24227
|
};
|
|
23435
|
-
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
23436
|
-
const u8a$1 = (a) => a instanceof Uint8Array;
|
|
23437
|
-
const createView$1 = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
23438
|
-
const rotr$1 = (word, shift) => word << 32 - shift | word >>> shift;
|
|
23439
|
-
const isLE$1 = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68;
|
|
23440
|
-
if (!isLE$1)
|
|
23441
|
-
throw new Error("Non little-endian hardware is not supported");
|
|
23442
|
-
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, "0"));
|
|
23443
|
-
function bytesToHex$1(bytes2) {
|
|
23444
|
-
if (!u8a$1(bytes2))
|
|
23445
|
-
throw new Error("Uint8Array expected");
|
|
23446
|
-
let hex2 = "";
|
|
23447
|
-
for (let i = 0; i < bytes2.length; i++) {
|
|
23448
|
-
hex2 += hexes[bytes2[i]];
|
|
23449
|
-
}
|
|
23450
|
-
return hex2;
|
|
23451
|
-
}
|
|
23452
|
-
function hexToBytes$1(hex2) {
|
|
23453
|
-
if (typeof hex2 !== "string")
|
|
23454
|
-
throw new Error("hex string expected, got " + typeof hex2);
|
|
23455
|
-
const len = hex2.length;
|
|
23456
|
-
if (len % 2)
|
|
23457
|
-
throw new Error("padded hex string expected, got unpadded hex of length " + len);
|
|
23458
|
-
const array = new Uint8Array(len / 2);
|
|
23459
|
-
for (let i = 0; i < array.length; i++) {
|
|
23460
|
-
const j = i * 2;
|
|
23461
|
-
const hexByte = hex2.slice(j, j + 2);
|
|
23462
|
-
const byte = Number.parseInt(hexByte, 16);
|
|
23463
|
-
if (Number.isNaN(byte) || byte < 0)
|
|
23464
|
-
throw new Error("Invalid byte sequence");
|
|
23465
|
-
array[i] = byte;
|
|
23466
|
-
}
|
|
23467
|
-
return array;
|
|
23468
|
-
}
|
|
23469
|
-
function utf8ToBytes$1(str2) {
|
|
23470
|
-
if (typeof str2 !== "string")
|
|
23471
|
-
throw new Error(`utf8ToBytes expected string, got ${typeof str2}`);
|
|
23472
|
-
return new Uint8Array(new TextEncoder().encode(str2));
|
|
23473
|
-
}
|
|
23474
|
-
function toBytes$1(data) {
|
|
23475
|
-
if (typeof data === "string")
|
|
23476
|
-
data = utf8ToBytes$1(data);
|
|
23477
|
-
if (!u8a$1(data))
|
|
23478
|
-
throw new Error(`expected Uint8Array, got ${typeof data}`);
|
|
23479
|
-
return data;
|
|
23480
|
-
}
|
|
23481
|
-
function concatBytes$1(...arrays) {
|
|
23482
|
-
const r = new Uint8Array(arrays.reduce((sum, a) => sum + a.length, 0));
|
|
23483
|
-
let pad = 0;
|
|
23484
|
-
arrays.forEach((a) => {
|
|
23485
|
-
if (!u8a$1(a))
|
|
23486
|
-
throw new Error("Uint8Array expected");
|
|
23487
|
-
r.set(a, pad);
|
|
23488
|
-
pad += a.length;
|
|
23489
|
-
});
|
|
23490
|
-
return r;
|
|
23491
|
-
}
|
|
23492
|
-
let Hash$1 = class Hash5 {
|
|
23493
|
-
// Safe version that clones internal state
|
|
23494
|
-
clone() {
|
|
23495
|
-
return this._cloneInto();
|
|
23496
|
-
}
|
|
23497
|
-
};
|
|
23498
|
-
function wrapConstructor$1(hashCons) {
|
|
23499
|
-
const hashC = (msg) => hashCons().update(toBytes$1(msg)).digest();
|
|
23500
|
-
const tmp = hashCons();
|
|
23501
|
-
hashC.outputLen = tmp.outputLen;
|
|
23502
|
-
hashC.blockLen = tmp.blockLen;
|
|
23503
|
-
hashC.create = () => hashCons();
|
|
23504
|
-
return hashC;
|
|
23505
|
-
}
|
|
23506
24228
|
function setBigUint64(view, byteOffset, value, isLE2) {
|
|
23507
24229
|
if (typeof view.setBigUint64 === "function")
|
|
23508
24230
|
return view.setBigUint64(byteOffset, value, isLE2);
|
|
@@ -23766,15 +24488,15 @@ class SHA224 extends SHA256 {
|
|
|
23766
24488
|
}
|
|
23767
24489
|
const sha256 = wrapConstructor$1(() => new SHA256());
|
|
23768
24490
|
wrapConstructor$1(() => new SHA224());
|
|
23769
|
-
function getPublicKey
|
|
23770
|
-
const pubKey =
|
|
24491
|
+
function getPublicKey(privateKey) {
|
|
24492
|
+
const pubKey = schnorr.getPublicKey(privateKey);
|
|
23771
24493
|
return bytesToHex$1(pubKey);
|
|
23772
24494
|
}
|
|
23773
24495
|
async function sign(content, privateKey) {
|
|
23774
24496
|
try {
|
|
23775
24497
|
const msgHash = hashContent(content);
|
|
23776
|
-
const signature =
|
|
23777
|
-
return signature
|
|
24498
|
+
const signature = schnorr.sign(msgHash, privateKey);
|
|
24499
|
+
return bytesToHex$1(signature);
|
|
23778
24500
|
} catch (error) {
|
|
23779
24501
|
throw new Error(`Signature generation failed: ${error.message}`);
|
|
23780
24502
|
}
|
|
@@ -23785,7 +24507,7 @@ async function verify(content, signature, publicKey) {
|
|
|
23785
24507
|
throw new Error("Invalid public key format");
|
|
23786
24508
|
}
|
|
23787
24509
|
const msgHash = hashContent(content);
|
|
23788
|
-
const isValid =
|
|
24510
|
+
const isValid = schnorr.verify(signature, msgHash, publicKey);
|
|
23789
24511
|
return isValid;
|
|
23790
24512
|
} catch (error) {
|
|
23791
24513
|
if (error.message === "Invalid public key format") {
|
|
@@ -23840,12 +24562,12 @@ async function issueCapability$1(permissions, scope, recipient, options = {}) {
|
|
|
23840
24562
|
issuer,
|
|
23841
24563
|
isSelfCapability: selfCap,
|
|
23842
24564
|
// Mark self-capabilities for clarity
|
|
23843
|
-
nonce: generateNonce
|
|
24565
|
+
nonce: generateNonce(),
|
|
23844
24566
|
issued: Date.now(),
|
|
23845
24567
|
expires: Date.now() + expiresIn
|
|
23846
24568
|
};
|
|
23847
24569
|
const payload = JSON.stringify(token);
|
|
23848
|
-
const encoded =
|
|
24570
|
+
const encoded = typeof btoa === "function" ? btoa(payload) : Buffer.from(payload).toString("base64");
|
|
23849
24571
|
if (issuerKey) {
|
|
23850
24572
|
const signature = await sign(payload, issuerKey);
|
|
23851
24573
|
return `${encoded}.${signature}`;
|
|
@@ -23867,11 +24589,28 @@ async function verifyCapability$1(token, requiredPermission, scope) {
|
|
|
23867
24589
|
if (token && typeof token === "object" && token.token) {
|
|
23868
24590
|
token = token.token;
|
|
23869
24591
|
}
|
|
24592
|
+
if (token && typeof token === "object" && token.type === "Buffer" && Array.isArray(token.data)) {
|
|
24593
|
+
try {
|
|
24594
|
+
token = String.fromCharCode.apply(null, token.data);
|
|
24595
|
+
} catch (e) {
|
|
24596
|
+
console.log("[verifyCapability] ❌ Failed to decode Buffer object:", e.message);
|
|
24597
|
+
return false;
|
|
24598
|
+
}
|
|
24599
|
+
}
|
|
24600
|
+
if (typeof token === "string" && /^\d+(,\d+)+$/.test(token.substring(0, 50))) {
|
|
24601
|
+
try {
|
|
24602
|
+
const bytes2 = token.split(",").map(Number);
|
|
24603
|
+
token = String.fromCharCode.apply(null, bytes2);
|
|
24604
|
+
} catch (e) {
|
|
24605
|
+
console.log("[verifyCapability] ❌ Failed to decode byte string:", e.message);
|
|
24606
|
+
return false;
|
|
24607
|
+
}
|
|
24608
|
+
}
|
|
23870
24609
|
if (typeof token === "string") {
|
|
23871
24610
|
if (token.startsWith("ey") || !token.includes(".") && !token.startsWith("{")) {
|
|
23872
24611
|
try {
|
|
23873
24612
|
const payload = token.includes(".") ? token.split(".")[0] : token;
|
|
23874
|
-
const decoded =
|
|
24613
|
+
const decoded = typeof atob === "function" ? atob(payload) : Buffer.from(payload, "base64").toString("utf8");
|
|
23875
24614
|
tokenObj = JSON.parse(decoded);
|
|
23876
24615
|
} catch (e) {
|
|
23877
24616
|
console.log("[verifyCapability] ❌ Token is not valid base64 JSON:", {
|
|
@@ -23933,7 +24672,86 @@ async function verifyCapability$1(token, requiredPermission, scope) {
|
|
|
23933
24672
|
return false;
|
|
23934
24673
|
}
|
|
23935
24674
|
}
|
|
23936
|
-
function
|
|
24675
|
+
async function verifyCapabilityIssuer(token, expectedIssuer) {
|
|
24676
|
+
try {
|
|
24677
|
+
if (token && typeof token === "object" && token.token) {
|
|
24678
|
+
token = token.token;
|
|
24679
|
+
}
|
|
24680
|
+
if (token && typeof token === "object" && token.type === "Buffer" && Array.isArray(token.data)) {
|
|
24681
|
+
token = String.fromCharCode.apply(null, token.data);
|
|
24682
|
+
}
|
|
24683
|
+
if (typeof token !== "string") {
|
|
24684
|
+
console.log("[verifyCapabilityIssuer] ❌ Token is not a string");
|
|
24685
|
+
return false;
|
|
24686
|
+
}
|
|
24687
|
+
let payload;
|
|
24688
|
+
let signature;
|
|
24689
|
+
let tokenObj;
|
|
24690
|
+
if (token.includes(".")) {
|
|
24691
|
+
const parts = token.split(".");
|
|
24692
|
+
const encodedPayload = parts[0];
|
|
24693
|
+
signature = parts[1];
|
|
24694
|
+
const decoded = typeof atob === "function" ? atob(encodedPayload) : Buffer.from(encodedPayload, "base64").toString("utf8");
|
|
24695
|
+
payload = decoded;
|
|
24696
|
+
tokenObj = JSON.parse(decoded);
|
|
24697
|
+
} else if (token.startsWith("ey")) {
|
|
24698
|
+
const decoded = typeof atob === "function" ? atob(token) : Buffer.from(token, "base64").toString("utf8");
|
|
24699
|
+
payload = decoded;
|
|
24700
|
+
tokenObj = JSON.parse(decoded);
|
|
24701
|
+
} else if (token.startsWith("{")) {
|
|
24702
|
+
payload = token;
|
|
24703
|
+
tokenObj = JSON.parse(token);
|
|
24704
|
+
} else {
|
|
24705
|
+
console.log("[verifyCapabilityIssuer] ❌ Unknown token format");
|
|
24706
|
+
return false;
|
|
24707
|
+
}
|
|
24708
|
+
if (tokenObj.issuer !== expectedIssuer) {
|
|
24709
|
+
console.log("[verifyCapabilityIssuer] ❌ Issuer mismatch:", {
|
|
24710
|
+
tokenIssuer: tokenObj.issuer?.slice(0, 12) + "...",
|
|
24711
|
+
expectedIssuer: expectedIssuer?.slice(0, 12) + "..."
|
|
24712
|
+
});
|
|
24713
|
+
return false;
|
|
24714
|
+
}
|
|
24715
|
+
if (signature) {
|
|
24716
|
+
const signatureValid = await verify(payload, signature, expectedIssuer);
|
|
24717
|
+
if (!signatureValid) {
|
|
24718
|
+
console.log("[verifyCapabilityIssuer] ❌ Signature verification failed - capability not signed by claimed issuer");
|
|
24719
|
+
return false;
|
|
24720
|
+
}
|
|
24721
|
+
} else {
|
|
24722
|
+
console.log("[verifyCapabilityIssuer] ⚠️ Token has no signature - cannot verify cryptographically");
|
|
24723
|
+
}
|
|
24724
|
+
console.log("[verifyCapabilityIssuer] ✅ Issuer verified");
|
|
24725
|
+
return true;
|
|
24726
|
+
} catch (error) {
|
|
24727
|
+
console.log("[verifyCapabilityIssuer] ❌ Error:", error.message);
|
|
24728
|
+
return false;
|
|
24729
|
+
}
|
|
24730
|
+
}
|
|
24731
|
+
function decodeCapability(token) {
|
|
24732
|
+
try {
|
|
24733
|
+
if (token && typeof token === "object" && token.token) {
|
|
24734
|
+
token = token.token;
|
|
24735
|
+
}
|
|
24736
|
+
if (token && typeof token === "object" && token.type === "Buffer" && Array.isArray(token.data)) {
|
|
24737
|
+
token = String.fromCharCode.apply(null, token.data);
|
|
24738
|
+
}
|
|
24739
|
+
if (typeof token !== "string") {
|
|
24740
|
+
return null;
|
|
24741
|
+
}
|
|
24742
|
+
if (token.startsWith("ey") || !token.includes(".") && !token.startsWith("{")) {
|
|
24743
|
+
const payload = token.includes(".") ? token.split(".")[0] : token;
|
|
24744
|
+
const decoded = typeof atob === "function" ? atob(payload) : Buffer.from(payload, "base64").toString("utf8");
|
|
24745
|
+
return JSON.parse(decoded);
|
|
24746
|
+
} else if (token.startsWith("{")) {
|
|
24747
|
+
return JSON.parse(token);
|
|
24748
|
+
}
|
|
24749
|
+
return null;
|
|
24750
|
+
} catch (error) {
|
|
24751
|
+
return null;
|
|
24752
|
+
}
|
|
24753
|
+
}
|
|
24754
|
+
function generateNonce() {
|
|
23937
24755
|
return Date.now().toString(36) + Math.random().toString(36).substring(2, 15);
|
|
23938
24756
|
}
|
|
23939
24757
|
function hashContent(content) {
|
|
@@ -23945,18 +24763,45 @@ function hashContent(content) {
|
|
|
23945
24763
|
}
|
|
23946
24764
|
const secp256k1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
23947
24765
|
__proto__: null,
|
|
23948
|
-
|
|
24766
|
+
decodeCapability,
|
|
24767
|
+
getPublicKey,
|
|
23949
24768
|
issueCapability: issueCapability$1,
|
|
23950
24769
|
issueSelfCapability,
|
|
23951
24770
|
matchScope,
|
|
23952
24771
|
sign,
|
|
23953
24772
|
verify,
|
|
23954
|
-
verifyCapability: verifyCapability$1
|
|
24773
|
+
verifyCapability: verifyCapability$1,
|
|
24774
|
+
verifyCapabilityIssuer
|
|
23955
24775
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
23956
24776
|
const FEDERATION_TABLE = "federations";
|
|
23957
|
-
|
|
23958
|
-
|
|
23959
|
-
|
|
24777
|
+
const registryCache = /* @__PURE__ */ new Map();
|
|
24778
|
+
const CACHE_TTL_MS = 1e4;
|
|
24779
|
+
function buildPartnerIndex(federatedWith) {
|
|
24780
|
+
const index = /* @__PURE__ */ new Map();
|
|
24781
|
+
for (const partner of federatedWith) {
|
|
24782
|
+
if (partner.pubKey) {
|
|
24783
|
+
index.set(partner.pubKey, partner);
|
|
24784
|
+
}
|
|
24785
|
+
}
|
|
24786
|
+
return index;
|
|
24787
|
+
}
|
|
24788
|
+
function getCacheKey(client, appname) {
|
|
24789
|
+
return `${client.publicKey}:${appname}`;
|
|
24790
|
+
}
|
|
24791
|
+
function invalidateRegistryCache(client, appname) {
|
|
24792
|
+
const key = getCacheKey(client, appname);
|
|
24793
|
+
registryCache.delete(key);
|
|
24794
|
+
}
|
|
24795
|
+
async function getFederationRegistry(client, appname, options = {}) {
|
|
24796
|
+
const cacheKey = getCacheKey(client, appname);
|
|
24797
|
+
const now = Date.now();
|
|
24798
|
+
if (!options.skipCache) {
|
|
24799
|
+
const cached = registryCache.get(cacheKey);
|
|
24800
|
+
if (cached && now - cached.timestamp < CACHE_TTL_MS) {
|
|
24801
|
+
return cached.registry;
|
|
24802
|
+
}
|
|
24803
|
+
}
|
|
24804
|
+
const registry2 = await readGlobal(client, appname, FEDERATION_TABLE, client.publicKey) || {
|
|
23960
24805
|
id: client.publicKey,
|
|
23961
24806
|
federatedWith: [],
|
|
23962
24807
|
discoveryEnabled: false,
|
|
@@ -23964,8 +24809,25 @@ async function getFederationRegistry(client, appname) {
|
|
|
23964
24809
|
defaultScope: { holonId: "*", lensName: "*" },
|
|
23965
24810
|
defaultPermissions: ["read"]
|
|
23966
24811
|
};
|
|
24812
|
+
const partnerIndex = buildPartnerIndex(registry2.federatedWith || []);
|
|
24813
|
+
registryCache.set(cacheKey, {
|
|
24814
|
+
registry: registry2,
|
|
24815
|
+
partnerIndex,
|
|
24816
|
+
timestamp: now
|
|
24817
|
+
});
|
|
24818
|
+
return registry2;
|
|
24819
|
+
}
|
|
24820
|
+
async function getPartnerFromIndex(client, appname, partnerPubKey) {
|
|
24821
|
+
const cacheKey = getCacheKey(client, appname);
|
|
24822
|
+
await getFederationRegistry(client, appname);
|
|
24823
|
+
const cached = registryCache.get(cacheKey);
|
|
24824
|
+
if (cached && cached.partnerIndex) {
|
|
24825
|
+
return cached.partnerIndex.get(partnerPubKey) || null;
|
|
24826
|
+
}
|
|
24827
|
+
return null;
|
|
23967
24828
|
}
|
|
23968
24829
|
async function saveFederationRegistry(client, appname, registry2) {
|
|
24830
|
+
invalidateRegistryCache(client, appname);
|
|
23969
24831
|
return writeGlobal(client, appname, FEDERATION_TABLE, registry2);
|
|
23970
24832
|
}
|
|
23971
24833
|
async function addFederatedPartner(client, appname, partnerPubKey, options = {}) {
|
|
@@ -23977,10 +24839,16 @@ async function addFederatedPartner(client, appname, partnerPubKey, options = {})
|
|
|
23977
24839
|
existing.alias = options.alias;
|
|
23978
24840
|
}
|
|
23979
24841
|
if (options.inboundCapabilities) {
|
|
23980
|
-
|
|
24842
|
+
const allCaps = [
|
|
23981
24843
|
...existing.inboundCapabilities || [],
|
|
23982
24844
|
...options.inboundCapabilities
|
|
23983
24845
|
];
|
|
24846
|
+
const seen = /* @__PURE__ */ new Map();
|
|
24847
|
+
for (const cap of allCaps) {
|
|
24848
|
+
const scopeKey = `${cap.scope?.holonId}:${cap.scope?.lensName}:${cap.scope?.dataId}`;
|
|
24849
|
+
seen.set(scopeKey, cap);
|
|
24850
|
+
}
|
|
24851
|
+
existing.inboundCapabilities = Array.from(seen.values());
|
|
23984
24852
|
}
|
|
23985
24853
|
existing.updatedAt = Date.now();
|
|
23986
24854
|
registry2.federatedWith[existingIndex] = existing;
|
|
@@ -24010,65 +24878,31 @@ async function getFederatedAuthors(client, appname) {
|
|
|
24010
24878
|
return registry2.federatedWith.map((p) => p.pubKey);
|
|
24011
24879
|
}
|
|
24012
24880
|
async function getFederatedPartner(client, appname, partnerPubKey) {
|
|
24013
|
-
|
|
24014
|
-
return registry2.federatedWith.find((p) => p.pubKey === partnerPubKey) || null;
|
|
24881
|
+
return getPartnerFromIndex(client, appname, partnerPubKey);
|
|
24015
24882
|
}
|
|
24016
24883
|
async function getCapabilityForAuthor(client, appname, authorPubKey, scope) {
|
|
24017
|
-
const
|
|
24018
|
-
console.log("[Registry] 🔍 getCapabilityForAuthor called:", {
|
|
24019
|
-
authorPubKey: authorPubKey?.slice(0, 12) + "...",
|
|
24020
|
-
scope,
|
|
24021
|
-
federatedWithCount: registry2.federatedWith?.length || 0,
|
|
24022
|
-
federatedPartners: registry2.federatedWith?.map((p) => p.pubKey?.slice(0, 12) + "...") || []
|
|
24023
|
-
});
|
|
24024
|
-
const partner = registry2.federatedWith.find((p) => p.pubKey === authorPubKey);
|
|
24884
|
+
const partner = await getPartnerFromIndex(client, appname, authorPubKey);
|
|
24025
24885
|
if (!partner) {
|
|
24026
|
-
console.log("[Registry] ❌ Partner not found in federatedWith");
|
|
24027
24886
|
return null;
|
|
24028
24887
|
}
|
|
24029
24888
|
if (!partner.inboundCapabilities || partner.inboundCapabilities.length === 0) {
|
|
24030
|
-
console.log("[Registry] ❌ Partner has no inboundCapabilities:", {
|
|
24031
|
-
partnerPubKey: authorPubKey?.slice(0, 12) + "...",
|
|
24032
|
-
hasInboundCaps: !!partner.inboundCapabilities,
|
|
24033
|
-
inboundCapsLength: partner.inboundCapabilities?.length || 0
|
|
24034
|
-
});
|
|
24035
24889
|
return null;
|
|
24036
24890
|
}
|
|
24037
|
-
|
|
24038
|
-
|
|
24039
|
-
|
|
24040
|
-
|
|
24041
|
-
|
|
24042
|
-
|
|
24043
|
-
|
|
24044
|
-
console.log("[Registry] Capability expired:", { expires: cap.expires, now: Date.now() });
|
|
24045
|
-
return false;
|
|
24891
|
+
const now = Date.now();
|
|
24892
|
+
for (const cap of partner.inboundCapabilities) {
|
|
24893
|
+
if (cap.expires && cap.expires < now) {
|
|
24894
|
+
continue;
|
|
24895
|
+
}
|
|
24896
|
+
if (matchScope(cap.scope, scope)) {
|
|
24897
|
+
return cap;
|
|
24046
24898
|
}
|
|
24047
|
-
const matches = matchScope(cap.scope, scope);
|
|
24048
|
-
console.log("[Registry] Scope match check:", {
|
|
24049
|
-
capScope: cap.scope,
|
|
24050
|
-
requestedScope: scope,
|
|
24051
|
-
matches
|
|
24052
|
-
});
|
|
24053
|
-
return matches;
|
|
24054
|
-
}) || null;
|
|
24055
|
-
if (result) {
|
|
24056
|
-
console.log("[Registry] ✅ Matching capability found");
|
|
24057
|
-
} else {
|
|
24058
|
-
console.log("[Registry] ❌ No matching capability found");
|
|
24059
24899
|
}
|
|
24060
|
-
return
|
|
24900
|
+
return null;
|
|
24061
24901
|
}
|
|
24062
24902
|
async function storeInboundCapability(client, appname, partnerPubKey, capabilityInfo) {
|
|
24063
|
-
console.log("[Registry] 📥 storeInboundCapability called:", {
|
|
24064
|
-
partnerPubKey: partnerPubKey?.slice(0, 12) + "...",
|
|
24065
|
-
scope: capabilityInfo?.scope,
|
|
24066
|
-
hasToken: !!capabilityInfo?.token
|
|
24067
|
-
});
|
|
24068
24903
|
const registry2 = await getFederationRegistry(client, appname);
|
|
24069
24904
|
const partner = registry2.federatedWith.find((p) => p.pubKey === partnerPubKey);
|
|
24070
24905
|
if (!partner) {
|
|
24071
|
-
console.log("[Registry] Partner not in registry, auto-adding with capability");
|
|
24072
24906
|
return addFederatedPartner(client, appname, partnerPubKey, {
|
|
24073
24907
|
addedVia: "capability_received",
|
|
24074
24908
|
inboundCapabilities: [capabilityInfo]
|
|
@@ -24081,21 +24915,17 @@ async function storeInboundCapability(client, appname, partnerPubKey, capability
|
|
|
24081
24915
|
(cap) => JSON.stringify(cap.scope) === JSON.stringify(capabilityInfo.scope)
|
|
24082
24916
|
);
|
|
24083
24917
|
if (existingIndex >= 0) {
|
|
24084
|
-
console.log("[Registry] Updating existing capability at index:", existingIndex);
|
|
24085
24918
|
partner.inboundCapabilities[existingIndex] = {
|
|
24086
24919
|
...capabilityInfo,
|
|
24087
24920
|
updatedAt: Date.now()
|
|
24088
24921
|
};
|
|
24089
24922
|
} else {
|
|
24090
|
-
console.log("[Registry] Adding new capability, total will be:", partner.inboundCapabilities.length + 1);
|
|
24091
24923
|
partner.inboundCapabilities.push({
|
|
24092
24924
|
...capabilityInfo,
|
|
24093
24925
|
receivedAt: Date.now()
|
|
24094
24926
|
});
|
|
24095
24927
|
}
|
|
24096
|
-
|
|
24097
|
-
console.log("[Registry] ✅ Capability stored, registry saved:", result);
|
|
24098
|
-
return result;
|
|
24928
|
+
return saveFederationRegistry(client, appname, registry2);
|
|
24099
24929
|
}
|
|
24100
24930
|
async function storeOutboundCapability(client, appname, partnerPubKey, capabilityInfo) {
|
|
24101
24931
|
const registry2 = await getFederationRegistry(client, appname);
|
|
@@ -24253,19 +25083,412 @@ async function getSelfCapability(client, appname, scope) {
|
|
|
24253
25083
|
function isSelfCapability(capabilityInfo) {
|
|
24254
25084
|
return capabilityInfo && capabilityInfo.isSelfCapability === true;
|
|
24255
25085
|
}
|
|
25086
|
+
async function grantWriteAccessToHolon(client, appname, holonId, lensName, options = {}) {
|
|
25087
|
+
console.warn("[Deprecated] grantWriteAccessToHolon() is deprecated. Use grantAccess() instead.");
|
|
25088
|
+
const registry2 = await getFederationRegistry(client, appname);
|
|
25089
|
+
let partner = registry2.federatedWith.find((p) => p.pubKey === holonId);
|
|
25090
|
+
if (!partner) {
|
|
25091
|
+
partner = {
|
|
25092
|
+
pubKey: holonId,
|
|
25093
|
+
alias: null,
|
|
25094
|
+
addedAt: Date.now(),
|
|
25095
|
+
addedVia: "write_grant",
|
|
25096
|
+
inboundCapabilities: [],
|
|
25097
|
+
outboundCapabilities: [],
|
|
25098
|
+
writeGrants: []
|
|
25099
|
+
};
|
|
25100
|
+
registry2.federatedWith.push(partner);
|
|
25101
|
+
}
|
|
25102
|
+
if (!partner.writeGrants) {
|
|
25103
|
+
partner.writeGrants = [];
|
|
25104
|
+
}
|
|
25105
|
+
const existingIndex = partner.writeGrants.findIndex((g) => g.lensName === lensName);
|
|
25106
|
+
const grant = {
|
|
25107
|
+
lensName,
|
|
25108
|
+
grantedAt: Date.now(),
|
|
25109
|
+
expiresAt: options.expiresAt || null
|
|
25110
|
+
};
|
|
25111
|
+
if (existingIndex >= 0) {
|
|
25112
|
+
partner.writeGrants[existingIndex] = grant;
|
|
25113
|
+
} else {
|
|
25114
|
+
partner.writeGrants.push(grant);
|
|
25115
|
+
}
|
|
25116
|
+
return saveFederationRegistry(client, appname, registry2);
|
|
25117
|
+
}
|
|
25118
|
+
async function revokeWriteAccess(client, appname, holonId, lensName) {
|
|
25119
|
+
console.warn("[Deprecated] revokeWriteAccess() is deprecated. Use revokeAccess() instead.");
|
|
25120
|
+
const registry2 = await getFederationRegistry(client, appname);
|
|
25121
|
+
const partner = registry2.federatedWith.find((p) => p.pubKey === holonId);
|
|
25122
|
+
if (!partner || !partner.writeGrants) {
|
|
25123
|
+
return false;
|
|
25124
|
+
}
|
|
25125
|
+
const initialLength = partner.writeGrants.length;
|
|
25126
|
+
partner.writeGrants = partner.writeGrants.filter((g) => g.lensName !== lensName);
|
|
25127
|
+
if (partner.writeGrants.length === initialLength) {
|
|
25128
|
+
return false;
|
|
25129
|
+
}
|
|
25130
|
+
return saveFederationRegistry(client, appname, registry2);
|
|
25131
|
+
}
|
|
25132
|
+
async function getWriteGrantsForHolon(client, appname, holonId) {
|
|
25133
|
+
console.warn("[Deprecated] getWriteGrantsForHolon() is deprecated. Use getAccessGrants() instead.");
|
|
25134
|
+
const registry2 = await getFederationRegistry(client, appname);
|
|
25135
|
+
const partner = registry2.federatedWith.find((p) => p.pubKey === holonId);
|
|
25136
|
+
if (!partner || !partner.writeGrants) {
|
|
25137
|
+
return [];
|
|
25138
|
+
}
|
|
25139
|
+
const now = Date.now();
|
|
25140
|
+
return partner.writeGrants.filter((g) => !g.expiresAt || g.expiresAt > now);
|
|
25141
|
+
}
|
|
25142
|
+
async function hasWriteAccess(client, appname, holonId, lensName) {
|
|
25143
|
+
console.warn("[Deprecated] hasWriteAccess() is deprecated. Use findAccessGrant() instead.");
|
|
25144
|
+
const grants = await getWriteGrantsForHolon(client, appname, holonId);
|
|
25145
|
+
return grants.some((g) => g.lensName === lensName || g.lensName === "*");
|
|
25146
|
+
}
|
|
25147
|
+
async function getAllWriteGrants(client, appname) {
|
|
25148
|
+
console.warn("[Deprecated] getAllWriteGrants() is deprecated. Use getAccessGrants() instead.");
|
|
25149
|
+
const registry2 = await getFederationRegistry(client, appname);
|
|
25150
|
+
const now = Date.now();
|
|
25151
|
+
const results = [];
|
|
25152
|
+
for (const partner of registry2.federatedWith) {
|
|
25153
|
+
if (partner.writeGrants && partner.writeGrants.length > 0) {
|
|
25154
|
+
const validGrants = partner.writeGrants.filter((g) => !g.expiresAt || g.expiresAt > now);
|
|
25155
|
+
if (validGrants.length > 0) {
|
|
25156
|
+
results.push({
|
|
25157
|
+
holonId: partner.pubKey,
|
|
25158
|
+
alias: partner.alias,
|
|
25159
|
+
writeGrants: validGrants
|
|
25160
|
+
});
|
|
25161
|
+
}
|
|
25162
|
+
}
|
|
25163
|
+
}
|
|
25164
|
+
return results;
|
|
25165
|
+
}
|
|
25166
|
+
async function grantAccess(client, appname, partnerPubKey, scope, permissions, options = {}) {
|
|
25167
|
+
const registry2 = await getFederationRegistry(client, appname);
|
|
25168
|
+
const { expiresAt = null, capabilityToken = null, direction = "inbound" } = options;
|
|
25169
|
+
let partner = registry2.federatedWith.find((p) => p.pubKey === partnerPubKey);
|
|
25170
|
+
if (!partner) {
|
|
25171
|
+
partner = {
|
|
25172
|
+
pubKey: partnerPubKey,
|
|
25173
|
+
alias: null,
|
|
25174
|
+
addedAt: Date.now(),
|
|
25175
|
+
addedVia: "access_grant",
|
|
25176
|
+
inboundCapabilities: [],
|
|
25177
|
+
outboundCapabilities: [],
|
|
25178
|
+
writeGrants: [],
|
|
25179
|
+
accessGrants: []
|
|
25180
|
+
// New unified array
|
|
25181
|
+
};
|
|
25182
|
+
registry2.federatedWith.push(partner);
|
|
25183
|
+
}
|
|
25184
|
+
if (!partner.accessGrants) {
|
|
25185
|
+
partner.accessGrants = [];
|
|
25186
|
+
}
|
|
25187
|
+
const normalizedScope = {
|
|
25188
|
+
holonId: scope.holonId || "*",
|
|
25189
|
+
lensName: scope.lensName || "*"
|
|
25190
|
+
};
|
|
25191
|
+
const existingIndex = partner.accessGrants.findIndex(
|
|
25192
|
+
(g) => g.scope.holonId === normalizedScope.holonId && g.scope.lensName === normalizedScope.lensName && g.direction === direction
|
|
25193
|
+
);
|
|
25194
|
+
const grant = {
|
|
25195
|
+
scope: normalizedScope,
|
|
25196
|
+
permissions: [...permissions],
|
|
25197
|
+
grantedAt: Date.now(),
|
|
25198
|
+
expiresAt,
|
|
25199
|
+
capabilityToken,
|
|
25200
|
+
direction
|
|
25201
|
+
};
|
|
25202
|
+
if (existingIndex >= 0) {
|
|
25203
|
+
partner.accessGrants[existingIndex] = grant;
|
|
25204
|
+
} else {
|
|
25205
|
+
partner.accessGrants.push(grant);
|
|
25206
|
+
}
|
|
25207
|
+
return saveFederationRegistry(client, appname, registry2);
|
|
25208
|
+
}
|
|
25209
|
+
async function revokeAccess(client, appname, partnerPubKey, scope, permissions = null, options = {}) {
|
|
25210
|
+
const registry2 = await getFederationRegistry(client, appname);
|
|
25211
|
+
const { direction = "inbound" } = options;
|
|
25212
|
+
const partner = registry2.federatedWith.find((p) => p.pubKey === partnerPubKey);
|
|
25213
|
+
if (!partner || !partner.accessGrants) {
|
|
25214
|
+
return false;
|
|
25215
|
+
}
|
|
25216
|
+
const normalizedScope = {
|
|
25217
|
+
holonId: scope.holonId || "*",
|
|
25218
|
+
lensName: scope.lensName || "*"
|
|
25219
|
+
};
|
|
25220
|
+
const grantIndex = partner.accessGrants.findIndex(
|
|
25221
|
+
(g) => g.scope.holonId === normalizedScope.holonId && g.scope.lensName === normalizedScope.lensName && g.direction === direction
|
|
25222
|
+
);
|
|
25223
|
+
if (grantIndex < 0) {
|
|
25224
|
+
return false;
|
|
25225
|
+
}
|
|
25226
|
+
if (permissions === null) {
|
|
25227
|
+
partner.accessGrants.splice(grantIndex, 1);
|
|
25228
|
+
} else {
|
|
25229
|
+
const grant = partner.accessGrants[grantIndex];
|
|
25230
|
+
grant.permissions = grant.permissions.filter((p) => !permissions.includes(p));
|
|
25231
|
+
if (grant.permissions.length === 0) {
|
|
25232
|
+
partner.accessGrants.splice(grantIndex, 1);
|
|
25233
|
+
}
|
|
25234
|
+
}
|
|
25235
|
+
return saveFederationRegistry(client, appname, registry2);
|
|
25236
|
+
}
|
|
25237
|
+
async function findAccessGrant(client, appname, partnerPubKey, scope, permission, options = {}) {
|
|
25238
|
+
const registry2 = await getFederationRegistry(client, appname);
|
|
25239
|
+
const { direction = "inbound" } = options;
|
|
25240
|
+
const now = Date.now();
|
|
25241
|
+
const partner = registry2.federatedWith.find((p) => p.pubKey === partnerPubKey);
|
|
25242
|
+
if (!partner) {
|
|
25243
|
+
return null;
|
|
25244
|
+
}
|
|
25245
|
+
if (partner.accessGrants && partner.accessGrants.length > 0) {
|
|
25246
|
+
for (const grant of partner.accessGrants) {
|
|
25247
|
+
if (grant.expiresAt && grant.expiresAt < now) {
|
|
25248
|
+
continue;
|
|
25249
|
+
}
|
|
25250
|
+
if (grant.direction !== direction) {
|
|
25251
|
+
continue;
|
|
25252
|
+
}
|
|
25253
|
+
if (!grant.permissions.includes(permission)) {
|
|
25254
|
+
continue;
|
|
25255
|
+
}
|
|
25256
|
+
if (matchScope(grant.scope, scope)) {
|
|
25257
|
+
return grant;
|
|
25258
|
+
}
|
|
25259
|
+
}
|
|
25260
|
+
}
|
|
25261
|
+
if (permission === "read" && direction === "inbound") {
|
|
25262
|
+
if (partner.inboundCapabilities) {
|
|
25263
|
+
for (const cap of partner.inboundCapabilities) {
|
|
25264
|
+
if (cap.expires && cap.expires < now) continue;
|
|
25265
|
+
if (cap.permissions && !cap.permissions.includes("read")) continue;
|
|
25266
|
+
if (matchScope(cap.scope, scope)) {
|
|
25267
|
+
return {
|
|
25268
|
+
scope: cap.scope,
|
|
25269
|
+
permissions: cap.permissions || ["read"],
|
|
25270
|
+
grantedAt: cap.receivedAt || Date.now(),
|
|
25271
|
+
expiresAt: cap.expires || null,
|
|
25272
|
+
capabilityToken: cap.token,
|
|
25273
|
+
direction: "inbound",
|
|
25274
|
+
_legacy: true
|
|
25275
|
+
};
|
|
25276
|
+
}
|
|
25277
|
+
}
|
|
25278
|
+
}
|
|
25279
|
+
}
|
|
25280
|
+
if (permission === "write" && direction === "inbound") {
|
|
25281
|
+
if (partner.writeGrants) {
|
|
25282
|
+
for (const wg of partner.writeGrants) {
|
|
25283
|
+
if (wg.expiresAt && wg.expiresAt < now) continue;
|
|
25284
|
+
if (wg.lensName === scope.lensName || wg.lensName === "*") {
|
|
25285
|
+
return {
|
|
25286
|
+
scope: { holonId: "*", lensName: wg.lensName },
|
|
25287
|
+
permissions: ["write"],
|
|
25288
|
+
grantedAt: wg.grantedAt || Date.now(),
|
|
25289
|
+
expiresAt: wg.expiresAt || null,
|
|
25290
|
+
direction: "inbound",
|
|
25291
|
+
_legacy: true
|
|
25292
|
+
};
|
|
25293
|
+
}
|
|
25294
|
+
}
|
|
25295
|
+
}
|
|
25296
|
+
}
|
|
25297
|
+
return null;
|
|
25298
|
+
}
|
|
25299
|
+
async function getAccessGrants(client, appname, partnerPubKey, options = {}) {
|
|
25300
|
+
const registry2 = await getFederationRegistry(client, appname);
|
|
25301
|
+
const { direction = "all", includeLegacy = true } = options;
|
|
25302
|
+
const now = Date.now();
|
|
25303
|
+
const partner = registry2.federatedWith.find((p) => p.pubKey === partnerPubKey);
|
|
25304
|
+
if (!partner) {
|
|
25305
|
+
return [];
|
|
25306
|
+
}
|
|
25307
|
+
const results = [];
|
|
25308
|
+
if (partner.accessGrants) {
|
|
25309
|
+
for (const grant of partner.accessGrants) {
|
|
25310
|
+
if (grant.expiresAt && grant.expiresAt < now) continue;
|
|
25311
|
+
if (direction !== "all" && grant.direction !== direction) continue;
|
|
25312
|
+
results.push(grant);
|
|
25313
|
+
}
|
|
25314
|
+
}
|
|
25315
|
+
if (includeLegacy) {
|
|
25316
|
+
if (partner.inboundCapabilities && (direction === "all" || direction === "inbound")) {
|
|
25317
|
+
for (const cap of partner.inboundCapabilities) {
|
|
25318
|
+
if (cap.expires && cap.expires < now) continue;
|
|
25319
|
+
results.push({
|
|
25320
|
+
scope: cap.scope,
|
|
25321
|
+
permissions: cap.permissions || ["read"],
|
|
25322
|
+
grantedAt: cap.receivedAt || Date.now(),
|
|
25323
|
+
expiresAt: cap.expires || null,
|
|
25324
|
+
capabilityToken: cap.token,
|
|
25325
|
+
direction: "inbound",
|
|
25326
|
+
_legacy: true
|
|
25327
|
+
});
|
|
25328
|
+
}
|
|
25329
|
+
}
|
|
25330
|
+
if (partner.writeGrants && (direction === "all" || direction === "inbound")) {
|
|
25331
|
+
for (const wg of partner.writeGrants) {
|
|
25332
|
+
if (wg.expiresAt && wg.expiresAt < now) continue;
|
|
25333
|
+
results.push({
|
|
25334
|
+
scope: { holonId: "*", lensName: wg.lensName },
|
|
25335
|
+
permissions: ["write"],
|
|
25336
|
+
grantedAt: wg.grantedAt || Date.now(),
|
|
25337
|
+
expiresAt: wg.expiresAt || null,
|
|
25338
|
+
direction: "inbound",
|
|
25339
|
+
_legacy: true
|
|
25340
|
+
});
|
|
25341
|
+
}
|
|
25342
|
+
}
|
|
25343
|
+
}
|
|
25344
|
+
return results;
|
|
25345
|
+
}
|
|
25346
|
+
async function migrateToUnifiedGrants(client, appname) {
|
|
25347
|
+
const registry2 = await getFederationRegistry(client, appname);
|
|
25348
|
+
let migratedPartners = 0;
|
|
25349
|
+
let migratedGrants = 0;
|
|
25350
|
+
for (const partner of registry2.federatedWith) {
|
|
25351
|
+
let partnerModified = false;
|
|
25352
|
+
if (!partner.accessGrants) {
|
|
25353
|
+
partner.accessGrants = [];
|
|
25354
|
+
}
|
|
25355
|
+
if (partner.inboundCapabilities && partner.inboundCapabilities.length > 0) {
|
|
25356
|
+
for (const cap of partner.inboundCapabilities) {
|
|
25357
|
+
const alreadyMigrated = partner.accessGrants.some(
|
|
25358
|
+
(g) => g.scope.holonId === cap.scope?.holonId && g.scope.lensName === cap.scope?.lensName && g.direction === "inbound" && g._migratedFrom === "inboundCapability"
|
|
25359
|
+
);
|
|
25360
|
+
if (!alreadyMigrated && cap.scope) {
|
|
25361
|
+
partner.accessGrants.push({
|
|
25362
|
+
scope: { holonId: cap.scope.holonId || "*", lensName: cap.scope.lensName || "*" },
|
|
25363
|
+
permissions: cap.permissions || ["read"],
|
|
25364
|
+
grantedAt: cap.receivedAt || Date.now(),
|
|
25365
|
+
expiresAt: cap.expires || null,
|
|
25366
|
+
capabilityToken: cap.token,
|
|
25367
|
+
direction: "inbound",
|
|
25368
|
+
_migratedFrom: "inboundCapability"
|
|
25369
|
+
});
|
|
25370
|
+
migratedGrants++;
|
|
25371
|
+
partnerModified = true;
|
|
25372
|
+
}
|
|
25373
|
+
}
|
|
25374
|
+
}
|
|
25375
|
+
if (partner.writeGrants && partner.writeGrants.length > 0) {
|
|
25376
|
+
for (const wg of partner.writeGrants) {
|
|
25377
|
+
const alreadyMigrated = partner.accessGrants.some(
|
|
25378
|
+
(g) => g.scope.lensName === wg.lensName && g.permissions.includes("write") && g.direction === "inbound" && g._migratedFrom === "writeGrant"
|
|
25379
|
+
);
|
|
25380
|
+
if (!alreadyMigrated) {
|
|
25381
|
+
partner.accessGrants.push({
|
|
25382
|
+
scope: { holonId: "*", lensName: wg.lensName },
|
|
25383
|
+
permissions: ["write"],
|
|
25384
|
+
grantedAt: wg.grantedAt || Date.now(),
|
|
25385
|
+
expiresAt: wg.expiresAt || null,
|
|
25386
|
+
direction: "inbound",
|
|
25387
|
+
_migratedFrom: "writeGrant"
|
|
25388
|
+
});
|
|
25389
|
+
migratedGrants++;
|
|
25390
|
+
partnerModified = true;
|
|
25391
|
+
}
|
|
25392
|
+
}
|
|
25393
|
+
}
|
|
25394
|
+
if (partnerModified) {
|
|
25395
|
+
migratedPartners++;
|
|
25396
|
+
}
|
|
25397
|
+
}
|
|
25398
|
+
if (migratedGrants > 0) {
|
|
25399
|
+
await saveFederationRegistry(client, appname, registry2);
|
|
25400
|
+
}
|
|
25401
|
+
return { migratedPartners, migratedGrants };
|
|
25402
|
+
}
|
|
25403
|
+
function convertLegacyLensConfig(legacyConfig) {
|
|
25404
|
+
if (!legacyConfig) return { version: "2.0", permissions: {} };
|
|
25405
|
+
const permissions = {};
|
|
25406
|
+
for (const lens of legacyConfig.inbound || []) {
|
|
25407
|
+
if (!permissions[lens]) {
|
|
25408
|
+
permissions[lens] = { receive: [], share: [] };
|
|
25409
|
+
}
|
|
25410
|
+
if (!permissions[lens].receive.includes("read")) {
|
|
25411
|
+
permissions[lens].receive.push("read");
|
|
25412
|
+
}
|
|
25413
|
+
}
|
|
25414
|
+
for (const lens of legacyConfig.outbound || []) {
|
|
25415
|
+
if (!permissions[lens]) {
|
|
25416
|
+
permissions[lens] = { receive: [], share: [] };
|
|
25417
|
+
}
|
|
25418
|
+
if (!permissions[lens].share.includes("read")) {
|
|
25419
|
+
permissions[lens].share.push("read");
|
|
25420
|
+
}
|
|
25421
|
+
}
|
|
25422
|
+
for (const lens of legacyConfig.writeInbound || []) {
|
|
25423
|
+
if (!permissions[lens]) {
|
|
25424
|
+
permissions[lens] = { receive: [], share: [] };
|
|
25425
|
+
}
|
|
25426
|
+
if (!permissions[lens].receive.includes("write")) {
|
|
25427
|
+
permissions[lens].receive.push("write");
|
|
25428
|
+
}
|
|
25429
|
+
}
|
|
25430
|
+
for (const lens of legacyConfig.writeOutbound || []) {
|
|
25431
|
+
if (!permissions[lens]) {
|
|
25432
|
+
permissions[lens] = { receive: [], share: [] };
|
|
25433
|
+
}
|
|
25434
|
+
if (!permissions[lens].share.includes("write")) {
|
|
25435
|
+
permissions[lens].share.push("write");
|
|
25436
|
+
}
|
|
25437
|
+
}
|
|
25438
|
+
return { version: "2.0", permissions };
|
|
25439
|
+
}
|
|
25440
|
+
function convertToLegacyLensConfig(unifiedConfig) {
|
|
25441
|
+
if (!unifiedConfig || !unifiedConfig.permissions) {
|
|
25442
|
+
return { inbound: [], outbound: [], writeInbound: [], writeOutbound: [] };
|
|
25443
|
+
}
|
|
25444
|
+
const legacy = {
|
|
25445
|
+
inbound: [],
|
|
25446
|
+
outbound: [],
|
|
25447
|
+
writeInbound: [],
|
|
25448
|
+
writeOutbound: []
|
|
25449
|
+
};
|
|
25450
|
+
for (const [lensName, perms] of Object.entries(unifiedConfig.permissions)) {
|
|
25451
|
+
if (perms.receive?.includes("read")) {
|
|
25452
|
+
legacy.inbound.push(lensName);
|
|
25453
|
+
}
|
|
25454
|
+
if (perms.share?.includes("read")) {
|
|
25455
|
+
legacy.outbound.push(lensName);
|
|
25456
|
+
}
|
|
25457
|
+
if (perms.receive?.includes("write")) {
|
|
25458
|
+
legacy.writeInbound.push(lensName);
|
|
25459
|
+
}
|
|
25460
|
+
if (perms.share?.includes("write")) {
|
|
25461
|
+
legacy.writeOutbound.push(lensName);
|
|
25462
|
+
}
|
|
25463
|
+
}
|
|
25464
|
+
return legacy;
|
|
25465
|
+
}
|
|
24256
25466
|
const registry = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
24257
25467
|
__proto__: null,
|
|
24258
25468
|
addFederatedPartner,
|
|
24259
25469
|
cleanupExpiredCapabilities,
|
|
25470
|
+
convertLegacyLensConfig,
|
|
25471
|
+
convertToLegacyLensConfig,
|
|
25472
|
+
findAccessGrant,
|
|
25473
|
+
getAccessGrants,
|
|
25474
|
+
getAllWriteGrants,
|
|
24260
25475
|
getCapabilityForAuthor,
|
|
24261
25476
|
getFederatedAuthors,
|
|
24262
25477
|
getFederatedAuthorsForScope,
|
|
24263
25478
|
getFederatedPartner,
|
|
24264
25479
|
getFederationRegistry,
|
|
24265
25480
|
getSelfCapability,
|
|
25481
|
+
getWriteGrantsForHolon,
|
|
25482
|
+
grantAccess,
|
|
25483
|
+
grantWriteAccessToHolon,
|
|
25484
|
+
hasWriteAccess,
|
|
25485
|
+
invalidateRegistryCache,
|
|
24266
25486
|
isSelfCapability,
|
|
25487
|
+
migrateToUnifiedGrants,
|
|
24267
25488
|
removeFederatedPartner,
|
|
25489
|
+
revokeAccess,
|
|
24268
25490
|
revokeOutboundCapability,
|
|
25491
|
+
revokeWriteAccess,
|
|
24269
25492
|
saveFederationRegistry,
|
|
24270
25493
|
storeInboundCapability,
|
|
24271
25494
|
storeOutboundCapability,
|
|
@@ -24334,6 +25557,7 @@ function createHologram(sourceHolon, targetHolon, lensName, dataId, appname, opt
|
|
|
24334
25557
|
// Always the token string (normalized above)
|
|
24335
25558
|
_meta: {
|
|
24336
25559
|
created: Date.now(),
|
|
25560
|
+
lastUpdated: Date.now(),
|
|
24337
25561
|
sourceHolon,
|
|
24338
25562
|
source: sourceHolon,
|
|
24339
25563
|
// Alias for compatibility
|
|
@@ -24346,6 +25570,8 @@ function createHologram(sourceHolon, targetHolon, lensName, dataId, appname, opt
|
|
|
24346
25570
|
async function createHologramWithCapability(client, sourceHolon, targetHolon, lensName, dataId, appname, options = {}) {
|
|
24347
25571
|
const {
|
|
24348
25572
|
sourceAuthorPubKey = client.publicKey,
|
|
25573
|
+
sourceAuthorPrivKey = client.privateKey,
|
|
25574
|
+
// Allow passing author's private key for signing
|
|
24349
25575
|
targetAuthorPubKey = client.publicKey,
|
|
24350
25576
|
capability: providedCapability = null,
|
|
24351
25577
|
permissions = ["read"],
|
|
@@ -24362,7 +25588,8 @@ async function createHologramWithCapability(client, sourceHolon, targetHolon, le
|
|
|
24362
25588
|
expiresIn: expiresIn !== null ? expiresIn : isSelfCapability2 ? 365 * 24 * 60 * 60 * 1e3 : 36e5,
|
|
24363
25589
|
// 1 year for self, 1 hour for cross
|
|
24364
25590
|
issuer: sourceAuthorPubKey,
|
|
24365
|
-
issuerKey:
|
|
25591
|
+
issuerKey: sourceAuthorPrivKey
|
|
25592
|
+
// Use passed private key (holon's key when federated via holonsbot)
|
|
24366
25593
|
}
|
|
24367
25594
|
);
|
|
24368
25595
|
if (!isSelfCapability2 && client.privateKey) {
|
|
@@ -24442,6 +25669,18 @@ async function resolveHologram(client, hologram2, visited = /* @__PURE__ */ new
|
|
|
24442
25669
|
console.warn(`❌ Hologram missing capability: ${soul}`);
|
|
24443
25670
|
return null;
|
|
24444
25671
|
}
|
|
25672
|
+
if (authorPubKey) {
|
|
25673
|
+
const issuerMatch = await verifyCapabilityIssuer(capability, authorPubKey);
|
|
25674
|
+
if (!issuerMatch) {
|
|
25675
|
+
console.warn(`❌ Capability issuer does not match authorPubKey for hologram: ${soul}`);
|
|
25676
|
+
console.warn(` Expected author: ${authorPubKey?.slice(0, 12)}...`);
|
|
25677
|
+
const decoded = decodeCapability(capability);
|
|
25678
|
+
if (decoded) {
|
|
25679
|
+
console.warn(` Actual issuer: ${decoded.issuer?.slice(0, 12)}...`);
|
|
25680
|
+
}
|
|
25681
|
+
return null;
|
|
25682
|
+
}
|
|
25683
|
+
}
|
|
24445
25684
|
const isValid = await verifyCapability$1(
|
|
24446
25685
|
capability,
|
|
24447
25686
|
"read",
|
|
@@ -24636,13 +25875,6 @@ async function propagateData(client, appname, data, sourceHolon, targetHolon, le
|
|
|
24636
25875
|
const sourceAuthorPubKey = options.sourceAuthorPubKey || client.publicKey;
|
|
24637
25876
|
const isPubkey2 = typeof targetHolon === "string" && /^[0-9a-f]{64}$/i.test(targetHolon);
|
|
24638
25877
|
const targetAuthorPubKey = options.targetAuthorPubKey || (isPubkey2 ? targetHolon : client.publicKey);
|
|
24639
|
-
console.log("[propagateData] Creating hologram with capability:", {
|
|
24640
|
-
sourceHolon: sourceHolon?.slice(0, 12) + "...",
|
|
24641
|
-
targetHolon: targetHolon?.slice(0, 12) + "...",
|
|
24642
|
-
sourceAuthorPubKey: sourceAuthorPubKey?.slice(0, 12) + "...",
|
|
24643
|
-
targetAuthorPubKey: targetAuthorPubKey?.slice(0, 12) + "...",
|
|
24644
|
-
hasProvidedCapability: !!options.capability
|
|
24645
|
-
});
|
|
24646
25878
|
const hologram2 = await createHologramWithCapability(
|
|
24647
25879
|
client,
|
|
24648
25880
|
sourceHolon,
|
|
@@ -24652,6 +25884,8 @@ async function propagateData(client, appname, data, sourceHolon, targetHolon, le
|
|
|
24652
25884
|
appname,
|
|
24653
25885
|
{
|
|
24654
25886
|
sourceAuthorPubKey,
|
|
25887
|
+
sourceAuthorPrivKey: options.sourceAuthorPrivKey,
|
|
25888
|
+
// Pass through for holon-signed capabilities
|
|
24655
25889
|
targetAuthorPubKey,
|
|
24656
25890
|
capability: options.capability,
|
|
24657
25891
|
permissions: ["read"]
|
|
@@ -24716,7 +25950,6 @@ async function addActiveHologram(client, appname, sourceHolon, lensName, dataId,
|
|
|
24716
25950
|
let sourcePath = buildPath(currentAppname, currentHolon, currentLens, currentDataId);
|
|
24717
25951
|
let sourceData = await read(client, sourcePath);
|
|
24718
25952
|
if (!sourceData) {
|
|
24719
|
-
console.warn(`Source data not found at ${sourcePath}`);
|
|
24720
25953
|
return false;
|
|
24721
25954
|
}
|
|
24722
25955
|
let depth = 0;
|
|
@@ -24730,13 +25963,9 @@ async function addActiveHologram(client, appname, sourceHolon, lensName, dataId,
|
|
|
24730
25963
|
sourcePath = buildPath(currentAppname, currentHolon, currentLens, currentDataId);
|
|
24731
25964
|
sourceData = await read(client, sourcePath);
|
|
24732
25965
|
if (!sourceData) {
|
|
24733
|
-
console.warn(`Real source data not found at ${sourcePath}`);
|
|
24734
25966
|
return false;
|
|
24735
25967
|
}
|
|
24736
25968
|
}
|
|
24737
|
-
if (depth > 0) {
|
|
24738
|
-
console.info(`📍 Followed hologram chain (depth: ${depth}) to real source: ${currentHolon}/${currentLens}/${currentDataId}`);
|
|
24739
|
-
}
|
|
24740
25969
|
if (!sourceData._meta) {
|
|
24741
25970
|
sourceData._meta = {};
|
|
24742
25971
|
}
|
|
@@ -24804,7 +26033,6 @@ async function updateActiveHologramPlatform(client, appname, sourceHolon, lensNa
|
|
|
24804
26033
|
let sourcePath = buildPath(currentAppname, currentHolon, currentLens, currentDataId);
|
|
24805
26034
|
let sourceData = await read(client, sourcePath);
|
|
24806
26035
|
if (!sourceData) {
|
|
24807
|
-
console.warn(`Source data not found at ${sourcePath}`);
|
|
24808
26036
|
return false;
|
|
24809
26037
|
}
|
|
24810
26038
|
let depth = 0;
|
|
@@ -24818,7 +26046,6 @@ async function updateActiveHologramPlatform(client, appname, sourceHolon, lensNa
|
|
|
24818
26046
|
sourcePath = buildPath(currentAppname, currentHolon, currentLens, currentDataId);
|
|
24819
26047
|
sourceData = await read(client, sourcePath);
|
|
24820
26048
|
if (!sourceData) {
|
|
24821
|
-
console.warn(`Real source data not found at ${sourcePath}`);
|
|
24822
26049
|
return false;
|
|
24823
26050
|
}
|
|
24824
26051
|
}
|
|
@@ -25086,161 +26313,6 @@ const hologram = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
|
|
|
25086
26313
|
updateHologramOverrides,
|
|
25087
26314
|
wouldCreateCircularHologram
|
|
25088
26315
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
25089
|
-
function hexToBytes(hex2) {
|
|
25090
|
-
const bytes2 = new Uint8Array(hex2.length / 2);
|
|
25091
|
-
for (let i = 0; i < hex2.length; i += 2) {
|
|
25092
|
-
bytes2[i / 2] = parseInt(hex2.substr(i, 2), 16);
|
|
25093
|
-
}
|
|
25094
|
-
return bytes2;
|
|
25095
|
-
}
|
|
25096
|
-
function bytesToHex(bytes2) {
|
|
25097
|
-
return Array.from(bytes2).map((b2) => b2.toString(16).padStart(2, "0")).join("");
|
|
25098
|
-
}
|
|
25099
|
-
function parseNpubOrHex(input) {
|
|
25100
|
-
const trimmed = input?.trim();
|
|
25101
|
-
if (!trimmed) {
|
|
25102
|
-
return { valid: false, error: "Public key is required" };
|
|
25103
|
-
}
|
|
25104
|
-
if (/^[0-9a-fA-F]{64}$/.test(trimmed)) {
|
|
25105
|
-
return { valid: true, hexPubKey: trimmed.toLowerCase() };
|
|
25106
|
-
}
|
|
25107
|
-
let npubString = trimmed;
|
|
25108
|
-
if (npubString.startsWith("nostr:")) {
|
|
25109
|
-
npubString = npubString.slice(6);
|
|
25110
|
-
}
|
|
25111
|
-
if (npubString.startsWith("npub1")) {
|
|
25112
|
-
try {
|
|
25113
|
-
const decoded = nip19.decode(npubString);
|
|
25114
|
-
if (decoded.type === "npub") {
|
|
25115
|
-
return { valid: true, hexPubKey: decoded.data };
|
|
25116
|
-
}
|
|
25117
|
-
return { valid: false, error: "Invalid npub format" };
|
|
25118
|
-
} catch (e) {
|
|
25119
|
-
return { valid: false, error: "Invalid npub: unable to decode" };
|
|
25120
|
-
}
|
|
25121
|
-
}
|
|
25122
|
-
return { valid: false, error: "Enter a valid npub (npub1...) or 64-character hex public key" };
|
|
25123
|
-
}
|
|
25124
|
-
function hexToNpub(hexPubKey) {
|
|
25125
|
-
try {
|
|
25126
|
-
return nip19.npubEncode(hexPubKey);
|
|
25127
|
-
} catch (e) {
|
|
25128
|
-
console.error("Failed to encode hex to npub:", e);
|
|
25129
|
-
return hexPubKey;
|
|
25130
|
-
}
|
|
25131
|
-
}
|
|
25132
|
-
function npubToHex(npub2) {
|
|
25133
|
-
try {
|
|
25134
|
-
const decoded = nip19.decode(npub2);
|
|
25135
|
-
if (decoded.type === "npub") {
|
|
25136
|
-
return decoded.data;
|
|
25137
|
-
}
|
|
25138
|
-
return null;
|
|
25139
|
-
} catch (e) {
|
|
25140
|
-
return null;
|
|
25141
|
-
}
|
|
25142
|
-
}
|
|
25143
|
-
function shortenPubKey(pubKey) {
|
|
25144
|
-
if (!pubKey || pubKey.length <= 20) return pubKey || "";
|
|
25145
|
-
return `${pubKey.slice(0, 8)}...${pubKey.slice(-8)}`;
|
|
25146
|
-
}
|
|
25147
|
-
function shortenNpub(npub2) {
|
|
25148
|
-
if (!npub2 || npub2.length <= 20) return npub2 || "";
|
|
25149
|
-
return `${npub2.slice(0, 12)}...${npub2.slice(-8)}`;
|
|
25150
|
-
}
|
|
25151
|
-
function getPublicKey(privateKey) {
|
|
25152
|
-
return getPublicKey$2(hexToBytes(privateKey));
|
|
25153
|
-
}
|
|
25154
|
-
async function encryptNIP04(privateKey, recipientPubKey, content) {
|
|
25155
|
-
return await nip04.encrypt(privateKey, recipientPubKey, content);
|
|
25156
|
-
}
|
|
25157
|
-
async function decryptNIP04(privateKey, senderPubKey, encryptedContent) {
|
|
25158
|
-
return await nip04.decrypt(privateKey, senderPubKey, encryptedContent);
|
|
25159
|
-
}
|
|
25160
|
-
function encryptNIP44(privateKey, recipientPubKey, content) {
|
|
25161
|
-
const privKeyBytes = hexToBytes(privateKey);
|
|
25162
|
-
const conversationKey = nip44.v2.utils.getConversationKey(privKeyBytes, recipientPubKey);
|
|
25163
|
-
return nip44.v2.encrypt(content, conversationKey);
|
|
25164
|
-
}
|
|
25165
|
-
function decryptNIP44(privateKey, senderPubKey, encryptedContent) {
|
|
25166
|
-
const privKeyBytes = hexToBytes(privateKey);
|
|
25167
|
-
const conversationKey = nip44.v2.utils.getConversationKey(privKeyBytes, senderPubKey);
|
|
25168
|
-
return nip44.v2.decrypt(encryptedContent, conversationKey);
|
|
25169
|
-
}
|
|
25170
|
-
function createSignedEvent(kind2, content, tags, privateKey) {
|
|
25171
|
-
const event = {
|
|
25172
|
-
kind: kind2,
|
|
25173
|
-
content,
|
|
25174
|
-
tags,
|
|
25175
|
-
created_at: Math.floor(Date.now() / 1e3)
|
|
25176
|
-
};
|
|
25177
|
-
return finalizeEvent(event, hexToBytes(privateKey));
|
|
25178
|
-
}
|
|
25179
|
-
function createDMEvent(recipientPubKey, encryptedContent, privateKey) {
|
|
25180
|
-
return createSignedEvent(
|
|
25181
|
-
4,
|
|
25182
|
-
// NIP-04 encrypted DM
|
|
25183
|
-
encryptedContent,
|
|
25184
|
-
[["p", recipientPubKey]],
|
|
25185
|
-
privateKey
|
|
25186
|
-
);
|
|
25187
|
-
}
|
|
25188
|
-
function isValidHexPubKey(str2) {
|
|
25189
|
-
return typeof str2 === "string" && /^[0-9a-fA-F]{64}$/.test(str2);
|
|
25190
|
-
}
|
|
25191
|
-
function isValidNpub(str2) {
|
|
25192
|
-
if (typeof str2 !== "string" || !str2.startsWith("npub1")) {
|
|
25193
|
-
return false;
|
|
25194
|
-
}
|
|
25195
|
-
try {
|
|
25196
|
-
const decoded = nip19.decode(str2);
|
|
25197
|
-
return decoded.type === "npub";
|
|
25198
|
-
} catch {
|
|
25199
|
-
return false;
|
|
25200
|
-
}
|
|
25201
|
-
}
|
|
25202
|
-
function generateNonce() {
|
|
25203
|
-
return Date.now().toString(36) + Math.random().toString(36).substring(2, 15);
|
|
25204
|
-
}
|
|
25205
|
-
function signEvent(event, privateKey) {
|
|
25206
|
-
const eventToSign = {
|
|
25207
|
-
kind: event.kind,
|
|
25208
|
-
content: event.content,
|
|
25209
|
-
tags: event.tags || [],
|
|
25210
|
-
created_at: event.created_at || Math.floor(Date.now() / 1e3)
|
|
25211
|
-
};
|
|
25212
|
-
return finalizeEvent(eventToSign, hexToBytes(privateKey));
|
|
25213
|
-
}
|
|
25214
|
-
function verifyEvent(event) {
|
|
25215
|
-
return verifyEvent$1(event);
|
|
25216
|
-
}
|
|
25217
|
-
const nostrUtils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
25218
|
-
__proto__: null,
|
|
25219
|
-
NDK,
|
|
25220
|
-
NDKEvent,
|
|
25221
|
-
NDKPrivateKeySigner,
|
|
25222
|
-
NDKSubscriptionCacheUsage,
|
|
25223
|
-
NDKUser,
|
|
25224
|
-
bytesToHex,
|
|
25225
|
-
createDMEvent,
|
|
25226
|
-
createSignedEvent,
|
|
25227
|
-
decryptNIP04,
|
|
25228
|
-
decryptNIP44,
|
|
25229
|
-
encryptNIP04,
|
|
25230
|
-
encryptNIP44,
|
|
25231
|
-
generateNonce,
|
|
25232
|
-
getPublicKey,
|
|
25233
|
-
hexToBytes,
|
|
25234
|
-
hexToNpub,
|
|
25235
|
-
isValidHexPubKey,
|
|
25236
|
-
isValidNpub,
|
|
25237
|
-
npubToHex,
|
|
25238
|
-
parseNpubOrHex,
|
|
25239
|
-
shortenNpub,
|
|
25240
|
-
shortenPubKey,
|
|
25241
|
-
signEvent,
|
|
25242
|
-
verifyEvent
|
|
25243
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
25244
26316
|
const HOLON_REGISTRY_TABLE = "_holon-registry";
|
|
25245
26317
|
function isPubkey(str2) {
|
|
25246
26318
|
return typeof str2 === "string" && /^[0-9a-f]{64}$/i.test(str2);
|
|
@@ -25848,23 +26920,309 @@ const cardStorage$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defin
|
|
|
25848
26920
|
setLastDMFetchTime,
|
|
25849
26921
|
updateCardStatus
|
|
25850
26922
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
26923
|
+
class LensKeyStore {
|
|
26924
|
+
/**
|
|
26925
|
+
* Creates a new LensKeyStore instance.
|
|
26926
|
+
*/
|
|
26927
|
+
constructor() {
|
|
26928
|
+
this.ownKeys = /* @__PURE__ */ new Map();
|
|
26929
|
+
this.receivedKeys = /* @__PURE__ */ new Map();
|
|
26930
|
+
this.partnerWrappedKeys = /* @__PURE__ */ new Map();
|
|
26931
|
+
}
|
|
26932
|
+
/**
|
|
26933
|
+
* Store a lens key we own (we generated it).
|
|
26934
|
+
*
|
|
26935
|
+
* @param {string} lensPath - Path identifying the lens (appName/holonId/lensName)
|
|
26936
|
+
* @param {Uint8Array} key - 32-byte symmetric key
|
|
26937
|
+
* @param {string} wrappedForSelf - NIP-44 wrapped key for self-recovery
|
|
26938
|
+
*/
|
|
26939
|
+
storeOwnKey(lensPath, key, wrappedForSelf) {
|
|
26940
|
+
this.ownKeys.set(lensPath, {
|
|
26941
|
+
key,
|
|
26942
|
+
wrappedForSelf,
|
|
26943
|
+
createdAt: Date.now()
|
|
26944
|
+
});
|
|
26945
|
+
}
|
|
26946
|
+
/**
|
|
26947
|
+
* Store a lens key we received from a federation partner.
|
|
26948
|
+
*
|
|
26949
|
+
* @param {string} lensPath - Path identifying the lens
|
|
26950
|
+
* @param {Uint8Array} key - 32-byte symmetric key
|
|
26951
|
+
* @param {string} fromPubKey - Public key of the partner who shared the key
|
|
26952
|
+
*/
|
|
26953
|
+
storeReceivedKey(lensPath, key, fromPubKey) {
|
|
26954
|
+
this.receivedKeys.set(lensPath, {
|
|
26955
|
+
key,
|
|
26956
|
+
fromPubKey,
|
|
26957
|
+
receivedAt: Date.now()
|
|
26958
|
+
});
|
|
26959
|
+
}
|
|
26960
|
+
/**
|
|
26961
|
+
* Get the key for a lens (own or received).
|
|
26962
|
+
* Own keys take precedence over received keys.
|
|
26963
|
+
*
|
|
26964
|
+
* @param {string} lensPath - Path identifying the lens
|
|
26965
|
+
* @returns {Uint8Array|null} 32-byte symmetric key or null if not found
|
|
26966
|
+
*/
|
|
26967
|
+
getKey(lensPath) {
|
|
26968
|
+
const own = this.ownKeys.get(lensPath);
|
|
26969
|
+
if (own) return own.key;
|
|
26970
|
+
const received = this.receivedKeys.get(lensPath);
|
|
26971
|
+
if (received) return received.key;
|
|
26972
|
+
return null;
|
|
26973
|
+
}
|
|
26974
|
+
/**
|
|
26975
|
+
* Check if we have a key for a lens.
|
|
26976
|
+
*
|
|
26977
|
+
* @param {string} lensPath - Path identifying the lens
|
|
26978
|
+
* @returns {boolean} True if key exists
|
|
26979
|
+
*/
|
|
26980
|
+
hasKey(lensPath) {
|
|
26981
|
+
return this.ownKeys.has(lensPath) || this.receivedKeys.has(lensPath);
|
|
26982
|
+
}
|
|
26983
|
+
/**
|
|
26984
|
+
* Check if we own the key for a lens (vs received it).
|
|
26985
|
+
*
|
|
26986
|
+
* @param {string} lensPath - Path identifying the lens
|
|
26987
|
+
* @returns {boolean} True if we own the key
|
|
26988
|
+
*/
|
|
26989
|
+
isOwnKey(lensPath) {
|
|
26990
|
+
return this.ownKeys.has(lensPath);
|
|
26991
|
+
}
|
|
26992
|
+
/**
|
|
26993
|
+
* Get or create a key for a lens.
|
|
26994
|
+
* If key doesn't exist, generates a new one.
|
|
26995
|
+
*
|
|
26996
|
+
* @param {string} lensPath - Path identifying the lens
|
|
26997
|
+
* @param {string} ownerPrivKey - Owner's private key for wrapping
|
|
26998
|
+
* @param {string} ownerPubKey - Owner's public key
|
|
26999
|
+
* @returns {Object} { key: Uint8Array, isNew: boolean, wrappedForSelf: string }
|
|
27000
|
+
*/
|
|
27001
|
+
getOrCreateKey(lensPath, ownerPrivKey, ownerPubKey) {
|
|
27002
|
+
const existing = this.ownKeys.get(lensPath);
|
|
27003
|
+
if (existing) {
|
|
27004
|
+
return {
|
|
27005
|
+
key: existing.key,
|
|
27006
|
+
isNew: false,
|
|
27007
|
+
wrappedForSelf: existing.wrappedForSelf
|
|
27008
|
+
};
|
|
27009
|
+
}
|
|
27010
|
+
if (this.receivedKeys.has(lensPath)) {
|
|
27011
|
+
const received = this.receivedKeys.get(lensPath);
|
|
27012
|
+
return {
|
|
27013
|
+
key: received.key,
|
|
27014
|
+
isNew: false,
|
|
27015
|
+
wrappedForSelf: null
|
|
27016
|
+
// We don't have wrapped version for received keys
|
|
27017
|
+
};
|
|
27018
|
+
}
|
|
27019
|
+
const key = generateLensKey();
|
|
27020
|
+
const wrappedForSelf = wrapKeyForRecipient(key, ownerPrivKey, ownerPubKey);
|
|
27021
|
+
this.storeOwnKey(lensPath, key, wrappedForSelf);
|
|
27022
|
+
return { key, isNew: true, wrappedForSelf };
|
|
27023
|
+
}
|
|
27024
|
+
/**
|
|
27025
|
+
* Wrap a lens key for a partner (for federation sharing).
|
|
27026
|
+
*
|
|
27027
|
+
* @param {string} lensPath - Path identifying the lens
|
|
27028
|
+
* @param {string} ownerPrivKey - Owner's private key
|
|
27029
|
+
* @param {string} partnerPubKey - Partner's public key
|
|
27030
|
+
* @returns {string|null} Wrapped key for partner or null if no key exists
|
|
27031
|
+
*/
|
|
27032
|
+
wrapKeyForPartner(lensPath, ownerPrivKey, partnerPubKey) {
|
|
27033
|
+
const key = this.getKey(lensPath);
|
|
27034
|
+
if (!key) return null;
|
|
27035
|
+
const wrappedKey = wrapKeyForRecipient(key, ownerPrivKey, partnerPubKey);
|
|
27036
|
+
if (!this.partnerWrappedKeys.has(lensPath)) {
|
|
27037
|
+
this.partnerWrappedKeys.set(lensPath, /* @__PURE__ */ new Map());
|
|
27038
|
+
}
|
|
27039
|
+
this.partnerWrappedKeys.get(lensPath).set(partnerPubKey, wrappedKey);
|
|
27040
|
+
return wrappedKey;
|
|
27041
|
+
}
|
|
27042
|
+
/**
|
|
27043
|
+
* Get all partners who have been given wrapped keys for a lens.
|
|
27044
|
+
*
|
|
27045
|
+
* @param {string} lensPath - Path identifying the lens
|
|
27046
|
+
* @returns {string[]} Array of partner public keys
|
|
27047
|
+
*/
|
|
27048
|
+
getPartnersWithAccess(lensPath) {
|
|
27049
|
+
const partners = this.partnerWrappedKeys.get(lensPath);
|
|
27050
|
+
return partners ? Array.from(partners.keys()) : [];
|
|
27051
|
+
}
|
|
27052
|
+
/**
|
|
27053
|
+
* Revoke a partner's access by removing their wrapped key.
|
|
27054
|
+
* Note: This doesn't prevent them from using their cached key.
|
|
27055
|
+
* For true revocation, you need to re-key the lens.
|
|
27056
|
+
*
|
|
27057
|
+
* @param {string} lensPath - Path identifying the lens
|
|
27058
|
+
* @param {string} partnerPubKey - Partner's public key
|
|
27059
|
+
* @returns {boolean} True if partner was found and removed
|
|
27060
|
+
*/
|
|
27061
|
+
revokePartnerAccess(lensPath, partnerPubKey) {
|
|
27062
|
+
const partners = this.partnerWrappedKeys.get(lensPath);
|
|
27063
|
+
if (partners) {
|
|
27064
|
+
return partners.delete(partnerPubKey);
|
|
27065
|
+
}
|
|
27066
|
+
return false;
|
|
27067
|
+
}
|
|
27068
|
+
/**
|
|
27069
|
+
* Re-key a lens (generate new key and re-wrap for remaining partners).
|
|
27070
|
+
* Used for key rotation or access revocation.
|
|
27071
|
+
*
|
|
27072
|
+
* @param {string} lensPath - Path identifying the lens
|
|
27073
|
+
* @param {string} ownerPrivKey - Owner's private key
|
|
27074
|
+
* @param {string} ownerPubKey - Owner's public key
|
|
27075
|
+
* @param {string[]} [excludePartners=[]] - Partners to exclude from re-wrapping
|
|
27076
|
+
* @returns {Object} { newKey, wrappedForSelf, partnerKeys: Map<pubKey, wrappedKey> }
|
|
27077
|
+
*/
|
|
27078
|
+
rekeyLens(lensPath, ownerPrivKey, ownerPubKey, excludePartners = []) {
|
|
27079
|
+
const currentPartners = this.getPartnersWithAccess(lensPath);
|
|
27080
|
+
const remainingPartners = currentPartners.filter((p) => !excludePartners.includes(p));
|
|
27081
|
+
const newKey = generateLensKey();
|
|
27082
|
+
const wrappedForSelf = wrapKeyForRecipient(newKey, ownerPrivKey, ownerPubKey);
|
|
27083
|
+
this.storeOwnKey(lensPath, newKey, wrappedForSelf);
|
|
27084
|
+
const partnerKeys = /* @__PURE__ */ new Map();
|
|
27085
|
+
this.partnerWrappedKeys.set(lensPath, /* @__PURE__ */ new Map());
|
|
27086
|
+
for (const partnerPubKey of remainingPartners) {
|
|
27087
|
+
const wrappedKey = wrapKeyForRecipient(newKey, ownerPrivKey, partnerPubKey);
|
|
27088
|
+
this.partnerWrappedKeys.get(lensPath).set(partnerPubKey, wrappedKey);
|
|
27089
|
+
partnerKeys.set(partnerPubKey, wrappedKey);
|
|
27090
|
+
}
|
|
27091
|
+
return { newKey, wrappedForSelf, partnerKeys };
|
|
27092
|
+
}
|
|
27093
|
+
/**
|
|
27094
|
+
* Receive and unwrap a key from a partner.
|
|
27095
|
+
*
|
|
27096
|
+
* @param {string} lensPath - Path identifying the lens
|
|
27097
|
+
* @param {string} wrappedKey - NIP-44 wrapped key from partner
|
|
27098
|
+
* @param {string} recipientPrivKey - Our private key
|
|
27099
|
+
* @param {string} senderPubKey - Partner's public key
|
|
27100
|
+
* @returns {Uint8Array} Unwrapped 32-byte symmetric key
|
|
27101
|
+
*/
|
|
27102
|
+
receiveKey(lensPath, wrappedKey, recipientPrivKey, senderPubKey) {
|
|
27103
|
+
const key = unwrapKey(wrappedKey, recipientPrivKey, senderPubKey);
|
|
27104
|
+
this.storeReceivedKey(lensPath, key, senderPubKey);
|
|
27105
|
+
return key;
|
|
27106
|
+
}
|
|
27107
|
+
/**
|
|
27108
|
+
* Export own keys for persistent storage.
|
|
27109
|
+
* Returns serializable format that can be stored in Nostr.
|
|
27110
|
+
*
|
|
27111
|
+
* @returns {Object[]} Array of { lensPath, wrappedForSelf, createdAt }
|
|
27112
|
+
*/
|
|
27113
|
+
exportOwnKeys() {
|
|
27114
|
+
const exported = [];
|
|
27115
|
+
for (const [lensPath, data] of this.ownKeys.entries()) {
|
|
27116
|
+
exported.push({
|
|
27117
|
+
lensPath,
|
|
27118
|
+
wrappedForSelf: data.wrappedForSelf,
|
|
27119
|
+
createdAt: data.createdAt
|
|
27120
|
+
});
|
|
27121
|
+
}
|
|
27122
|
+
return exported;
|
|
27123
|
+
}
|
|
27124
|
+
/**
|
|
27125
|
+
* Import own keys from persistent storage.
|
|
27126
|
+
*
|
|
27127
|
+
* @param {Object[]} keyData - Array of exported key data
|
|
27128
|
+
* @param {string} ownerPrivKey - Owner's private key for unwrapping
|
|
27129
|
+
* @param {string} ownerPubKey - Owner's public key
|
|
27130
|
+
*/
|
|
27131
|
+
importOwnKeys(keyData, ownerPrivKey, ownerPubKey) {
|
|
27132
|
+
for (const data of keyData) {
|
|
27133
|
+
const key = unwrapKey(data.wrappedForSelf, ownerPrivKey, ownerPubKey);
|
|
27134
|
+
this.ownKeys.set(data.lensPath, {
|
|
27135
|
+
key,
|
|
27136
|
+
wrappedForSelf: data.wrappedForSelf,
|
|
27137
|
+
createdAt: data.createdAt
|
|
27138
|
+
});
|
|
27139
|
+
}
|
|
27140
|
+
}
|
|
27141
|
+
/**
|
|
27142
|
+
* Clear all keys from the store.
|
|
27143
|
+
* Use with caution - this will remove all access to encrypted data.
|
|
27144
|
+
*/
|
|
27145
|
+
clear() {
|
|
27146
|
+
this.ownKeys.clear();
|
|
27147
|
+
this.receivedKeys.clear();
|
|
27148
|
+
this.partnerWrappedKeys.clear();
|
|
27149
|
+
}
|
|
27150
|
+
/**
|
|
27151
|
+
* Get statistics about the key store.
|
|
27152
|
+
*
|
|
27153
|
+
* @returns {Object} { ownKeyCount, receivedKeyCount, totalPartnerGrants }
|
|
27154
|
+
*/
|
|
27155
|
+
getStats() {
|
|
27156
|
+
let totalPartnerGrants = 0;
|
|
27157
|
+
for (const partners of this.partnerWrappedKeys.values()) {
|
|
27158
|
+
totalPartnerGrants += partners.size;
|
|
27159
|
+
}
|
|
27160
|
+
return {
|
|
27161
|
+
ownKeyCount: this.ownKeys.size,
|
|
27162
|
+
receivedKeyCount: this.receivedKeys.size,
|
|
27163
|
+
totalPartnerGrants
|
|
27164
|
+
};
|
|
27165
|
+
}
|
|
27166
|
+
}
|
|
27167
|
+
function buildLensPath(appName, holonId, lensName) {
|
|
27168
|
+
return `${appName}/${holonId}/${lensName}`;
|
|
27169
|
+
}
|
|
27170
|
+
function parseLensPath(lensPath) {
|
|
27171
|
+
const parts = lensPath.split("/");
|
|
27172
|
+
if (parts.length < 3) {
|
|
27173
|
+
throw new Error(`Invalid lens path: ${lensPath}`);
|
|
27174
|
+
}
|
|
27175
|
+
return {
|
|
27176
|
+
appName: parts[0],
|
|
27177
|
+
holonId: parts[1],
|
|
27178
|
+
lensName: parts[2]
|
|
27179
|
+
};
|
|
27180
|
+
}
|
|
27181
|
+
function isV2LensConfig(lensConfig) {
|
|
27182
|
+
return lensConfig && lensConfig.version === "2.0" && lensConfig.permissions;
|
|
27183
|
+
}
|
|
27184
|
+
function normalizeLensConfig(lensConfig) {
|
|
27185
|
+
if (!lensConfig) {
|
|
27186
|
+
return { inbound: [], outbound: [], writeInbound: [], writeOutbound: [] };
|
|
27187
|
+
}
|
|
27188
|
+
if (isV2LensConfig(lensConfig)) {
|
|
27189
|
+
return convertToLegacyLensConfig(lensConfig);
|
|
27190
|
+
}
|
|
27191
|
+
return {
|
|
27192
|
+
inbound: lensConfig.inbound || [],
|
|
27193
|
+
outbound: lensConfig.outbound || [],
|
|
27194
|
+
writeInbound: lensConfig.writeInbound || [],
|
|
27195
|
+
writeOutbound: lensConfig.writeOutbound || []
|
|
27196
|
+
};
|
|
27197
|
+
}
|
|
27198
|
+
function toUnifiedPermissions(lensConfig) {
|
|
27199
|
+
if (!lensConfig) {
|
|
27200
|
+
return { version: "2.0", permissions: {} };
|
|
27201
|
+
}
|
|
27202
|
+
if (isV2LensConfig(lensConfig)) {
|
|
27203
|
+
return lensConfig;
|
|
27204
|
+
}
|
|
27205
|
+
return convertLegacyLensConfig(lensConfig);
|
|
27206
|
+
}
|
|
25851
27207
|
function createFederationRequest({
|
|
25852
27208
|
senderHolonId,
|
|
25853
27209
|
senderHolonName,
|
|
25854
27210
|
senderPubKey,
|
|
25855
|
-
lensConfig = { inbound: [], outbound: [] },
|
|
27211
|
+
lensConfig = { inbound: [], outbound: [], writeInbound: [], writeOutbound: [] },
|
|
25856
27212
|
capabilities: capabilities2 = [],
|
|
25857
|
-
message
|
|
27213
|
+
message,
|
|
27214
|
+
useV2 = false
|
|
25858
27215
|
}) {
|
|
27216
|
+
const normalizedLensConfig = useV2 ? toUnifiedPermissions(lensConfig) : normalizeLensConfig(lensConfig);
|
|
25859
27217
|
return {
|
|
25860
27218
|
type: "federation_request",
|
|
25861
|
-
version: "1.0",
|
|
25862
|
-
requestId: generateNonce(),
|
|
27219
|
+
version: useV2 ? "2.0" : "1.0",
|
|
27220
|
+
requestId: generateNonce$1(),
|
|
25863
27221
|
timestamp: Date.now(),
|
|
25864
27222
|
senderHolonId,
|
|
25865
27223
|
senderHolonName,
|
|
25866
27224
|
senderNpub: hexToNpub(senderPubKey),
|
|
25867
|
-
lensConfig,
|
|
27225
|
+
lensConfig: normalizedLensConfig,
|
|
25868
27226
|
capabilities: capabilities2,
|
|
25869
27227
|
message
|
|
25870
27228
|
};
|
|
@@ -25877,18 +27235,23 @@ function createFederationResponse({
|
|
|
25877
27235
|
responderPubKey,
|
|
25878
27236
|
lensConfig,
|
|
25879
27237
|
capabilities: capabilities2,
|
|
25880
|
-
message
|
|
27238
|
+
message,
|
|
27239
|
+
useV2 = false
|
|
25881
27240
|
}) {
|
|
27241
|
+
let normalizedLensConfig;
|
|
27242
|
+
if (lensConfig) {
|
|
27243
|
+
normalizedLensConfig = useV2 ? toUnifiedPermissions(lensConfig) : normalizeLensConfig(lensConfig);
|
|
27244
|
+
}
|
|
25882
27245
|
return {
|
|
25883
27246
|
type: "federation_response",
|
|
25884
|
-
version: "1.0",
|
|
27247
|
+
version: useV2 ? "2.0" : "1.0",
|
|
25885
27248
|
requestId,
|
|
25886
27249
|
timestamp: Date.now(),
|
|
25887
27250
|
status,
|
|
25888
27251
|
responderHolonId,
|
|
25889
27252
|
responderHolonName,
|
|
25890
27253
|
responderNpub: responderPubKey ? hexToNpub(responderPubKey) : void 0,
|
|
25891
|
-
lensConfig,
|
|
27254
|
+
lensConfig: normalizedLensConfig,
|
|
25892
27255
|
capabilities: capabilities2,
|
|
25893
27256
|
message
|
|
25894
27257
|
};
|
|
@@ -25929,8 +27292,33 @@ async function sendFederationResponse(client, privateKey, recipientPubKey, respo
|
|
|
25929
27292
|
return false;
|
|
25930
27293
|
}
|
|
25931
27294
|
}
|
|
27295
|
+
async function sendLensKeyShare(client, privateKey, recipientPubKey, keyShare) {
|
|
27296
|
+
try {
|
|
27297
|
+
const payload = {
|
|
27298
|
+
type: "lens_key_share",
|
|
27299
|
+
version: "1.0",
|
|
27300
|
+
lensPath: keyShare.lensPath,
|
|
27301
|
+
wrappedKey: keyShare.wrappedKey,
|
|
27302
|
+
timestamp: Date.now()
|
|
27303
|
+
};
|
|
27304
|
+
const content = JSON.stringify(payload);
|
|
27305
|
+
const encrypted = encryptNIP44(privateKey, recipientPubKey, content);
|
|
27306
|
+
const event = createDMEvent(recipientPubKey, encrypted, privateKey);
|
|
27307
|
+
if (client?.publish) {
|
|
27308
|
+
await client.publish(event);
|
|
27309
|
+
console.log("[Handshake] Lens key share sent to:", recipientPubKey.substring(0, 8) + "...", "for lens:", keyShare.lensPath);
|
|
27310
|
+
return true;
|
|
27311
|
+
} else {
|
|
27312
|
+
console.error("[Handshake] No publish method on client");
|
|
27313
|
+
return false;
|
|
27314
|
+
}
|
|
27315
|
+
} catch (error) {
|
|
27316
|
+
console.error("[Handshake] Failed to send lens key share:", error);
|
|
27317
|
+
return false;
|
|
27318
|
+
}
|
|
27319
|
+
}
|
|
25932
27320
|
function subscribeToFederationDMs(client, privateKey, publicKey, handlers, options = {}) {
|
|
25933
|
-
const { onRequest, onResponse, onUpdate, onUpdateResponse } = handlers;
|
|
27321
|
+
const { onRequest, onResponse, onUpdate, onUpdateResponse, onKeyShare } = handlers;
|
|
25934
27322
|
const { appname } = options;
|
|
25935
27323
|
let isActive = true;
|
|
25936
27324
|
const processedEventIds = /* @__PURE__ */ new Set();
|
|
@@ -26027,6 +27415,19 @@ function subscribeToFederationDMs(client, privateKey, publicKey, handlers, optio
|
|
|
26027
27415
|
}
|
|
26028
27416
|
console.log("[Handshake] Received federation update response from:", event.pubkey.substring(0, 8) + "...");
|
|
26029
27417
|
onUpdateResponse?.(payload, event.pubkey);
|
|
27418
|
+
} else if (payload.type === "lens_key_share" && payload.version === "1.0") {
|
|
27419
|
+
const keyShareKey = `key_${payload.lensPath}_${event.pubkey}`;
|
|
27420
|
+
if (processedResponseIds.has(keyShareKey)) {
|
|
27421
|
+
console.log("[Handshake] Skipping already processed key share:", keyShareKey);
|
|
27422
|
+
return;
|
|
27423
|
+
}
|
|
27424
|
+
if (appname && client?.client) {
|
|
27425
|
+
await markDMProcessed(client.client, appname, event.id, "key_share");
|
|
27426
|
+
processedResponseIds.add(keyShareKey);
|
|
27427
|
+
processedDMIds.add(event.id);
|
|
27428
|
+
}
|
|
27429
|
+
console.log("[Handshake] Received lens key share from:", event.pubkey.substring(0, 8) + "...", "for lens:", payload.lensPath);
|
|
27430
|
+
onKeyShare?.(payload, event.pubkey);
|
|
26030
27431
|
}
|
|
26031
27432
|
} catch (error) {
|
|
26032
27433
|
}
|
|
@@ -26072,13 +27473,19 @@ async function initiateFederationHandshake(holosphere, privateKey, params) {
|
|
|
26072
27473
|
partnerPubKey,
|
|
26073
27474
|
holonId,
|
|
26074
27475
|
holonName,
|
|
26075
|
-
lensConfig = { inbound: [], outbound: [] },
|
|
27476
|
+
lensConfig = { inbound: [], outbound: [], writeInbound: [], writeOutbound: [] },
|
|
26076
27477
|
message
|
|
26077
27478
|
} = params;
|
|
26078
27479
|
try {
|
|
26079
|
-
const senderPubKey = getPublicKey(privateKey);
|
|
27480
|
+
const senderPubKey = getPublicKey$1(privateKey);
|
|
27481
|
+
const normalizedLensConfig = {
|
|
27482
|
+
inbound: lensConfig.inbound || [],
|
|
27483
|
+
outbound: lensConfig.outbound || [],
|
|
27484
|
+
writeInbound: lensConfig.writeInbound || [],
|
|
27485
|
+
writeOutbound: lensConfig.writeOutbound || []
|
|
27486
|
+
};
|
|
26080
27487
|
const capabilities2 = [];
|
|
26081
|
-
for (const lensName of
|
|
27488
|
+
for (const lensName of normalizedLensConfig.outbound) {
|
|
26082
27489
|
try {
|
|
26083
27490
|
const token = await issueCapability$1(
|
|
26084
27491
|
["read"],
|
|
@@ -26100,11 +27507,42 @@ async function initiateFederationHandshake(holosphere, privateKey, params) {
|
|
|
26100
27507
|
console.warn(`[Handshake] Failed to issue capability for ${lensName}:`, err.message);
|
|
26101
27508
|
}
|
|
26102
27509
|
}
|
|
27510
|
+
if (holosphere.client) {
|
|
27511
|
+
for (const lensName of normalizedLensConfig.writeOutbound) {
|
|
27512
|
+
try {
|
|
27513
|
+
await grantAccess(
|
|
27514
|
+
holosphere.client,
|
|
27515
|
+
holosphere.config.appName,
|
|
27516
|
+
partnerPubKey,
|
|
27517
|
+
{ holonId: "*", lensName },
|
|
27518
|
+
["write"],
|
|
27519
|
+
{ direction: "inbound" }
|
|
27520
|
+
);
|
|
27521
|
+
console.log(`[Handshake] Pre-granted write access to partner for lens "${lensName}"`);
|
|
27522
|
+
} catch (err) {
|
|
27523
|
+
console.warn(`[Handshake] Failed to grant write access for ${lensName}:`, err.message);
|
|
27524
|
+
}
|
|
27525
|
+
}
|
|
27526
|
+
for (const lensName of normalizedLensConfig.outbound) {
|
|
27527
|
+
try {
|
|
27528
|
+
await grantAccess(
|
|
27529
|
+
holosphere.client,
|
|
27530
|
+
holosphere.config.appName,
|
|
27531
|
+
partnerPubKey,
|
|
27532
|
+
{ holonId: "*", lensName },
|
|
27533
|
+
["read"],
|
|
27534
|
+
{ direction: "outbound" }
|
|
27535
|
+
);
|
|
27536
|
+
} catch (err) {
|
|
27537
|
+
console.warn(`[Handshake] Failed to grant read access for ${lensName}:`, err.message);
|
|
27538
|
+
}
|
|
27539
|
+
}
|
|
27540
|
+
}
|
|
26103
27541
|
const request = createFederationRequest({
|
|
26104
27542
|
senderHolonId: holonId,
|
|
26105
27543
|
senderHolonName: holonName,
|
|
26106
27544
|
senderPubKey,
|
|
26107
|
-
lensConfig,
|
|
27545
|
+
lensConfig: normalizedLensConfig,
|
|
26108
27546
|
capabilities: capabilities2,
|
|
26109
27547
|
message
|
|
26110
27548
|
});
|
|
@@ -26130,11 +27568,17 @@ async function acceptFederationRequest$1(holosphere, privateKey, params) {
|
|
|
26130
27568
|
senderPubKey,
|
|
26131
27569
|
holonId,
|
|
26132
27570
|
holonName,
|
|
26133
|
-
lensConfig = { inbound: [], outbound: [] },
|
|
27571
|
+
lensConfig = { inbound: [], outbound: [], writeInbound: [], writeOutbound: [] },
|
|
26134
27572
|
message
|
|
26135
27573
|
} = params;
|
|
26136
27574
|
try {
|
|
26137
|
-
const responderPubKey = getPublicKey(privateKey);
|
|
27575
|
+
const responderPubKey = getPublicKey$1(privateKey);
|
|
27576
|
+
const normalizedLensConfig = {
|
|
27577
|
+
inbound: lensConfig.inbound || [],
|
|
27578
|
+
outbound: lensConfig.outbound || [],
|
|
27579
|
+
writeInbound: lensConfig.writeInbound || [],
|
|
27580
|
+
writeOutbound: lensConfig.writeOutbound || []
|
|
27581
|
+
};
|
|
26138
27582
|
if (holosphere.client) {
|
|
26139
27583
|
await addFederatedPartner(holosphere.client, holosphere.config.appName, senderPubKey, {
|
|
26140
27584
|
alias: request.senderHolonName,
|
|
@@ -26145,6 +27589,39 @@ async function acceptFederationRequest$1(holosphere, privateKey, params) {
|
|
|
26145
27589
|
await storeInboundCapability(holosphere.client, holosphere.config.appName, senderPubKey, cap);
|
|
26146
27590
|
}
|
|
26147
27591
|
}
|
|
27592
|
+
const senderWriteOutbound = request.lensConfig?.writeOutbound || [];
|
|
27593
|
+
if (senderWriteOutbound.length > 0) {
|
|
27594
|
+
console.log("[Handshake] Sender is granting write access for lenses:", senderWriteOutbound);
|
|
27595
|
+
}
|
|
27596
|
+
for (const lensName of normalizedLensConfig.writeOutbound) {
|
|
27597
|
+
try {
|
|
27598
|
+
await grantAccess(
|
|
27599
|
+
holosphere.client,
|
|
27600
|
+
holosphere.config.appName,
|
|
27601
|
+
senderPubKey,
|
|
27602
|
+
{ holonId: "*", lensName },
|
|
27603
|
+
["write"],
|
|
27604
|
+
{ direction: "inbound" }
|
|
27605
|
+
);
|
|
27606
|
+
console.log(`[Handshake] Granted write access to sender for lens "${lensName}"`);
|
|
27607
|
+
} catch (err) {
|
|
27608
|
+
console.warn(`[Handshake] Failed to grant write access for ${lensName}:`, err.message);
|
|
27609
|
+
}
|
|
27610
|
+
}
|
|
27611
|
+
for (const lensName of normalizedLensConfig.outbound) {
|
|
27612
|
+
try {
|
|
27613
|
+
await grantAccess(
|
|
27614
|
+
holosphere.client,
|
|
27615
|
+
holosphere.config.appName,
|
|
27616
|
+
senderPubKey,
|
|
27617
|
+
{ holonId: "*", lensName },
|
|
27618
|
+
["read"],
|
|
27619
|
+
{ direction: "outbound" }
|
|
27620
|
+
);
|
|
27621
|
+
} catch (err) {
|
|
27622
|
+
console.warn(`[Handshake] Failed to grant read access for ${lensName}:`, err.message);
|
|
27623
|
+
}
|
|
27624
|
+
}
|
|
26148
27625
|
if (request.senderHolonId) {
|
|
26149
27626
|
await registerHolon(holosphere.client, holosphere.config.appName, request.senderHolonId, senderPubKey, {
|
|
26150
27627
|
alias: request.senderHolonName
|
|
@@ -26156,11 +27633,11 @@ async function acceptFederationRequest$1(holosphere, privateKey, params) {
|
|
|
26156
27633
|
}
|
|
26157
27634
|
}
|
|
26158
27635
|
await holosphere.federateHolon(holonId, senderPubKey, {
|
|
26159
|
-
lensConfig,
|
|
27636
|
+
lensConfig: normalizedLensConfig,
|
|
26160
27637
|
partnerName: request.senderHolonName
|
|
26161
27638
|
});
|
|
26162
27639
|
const capabilities2 = [];
|
|
26163
|
-
for (const lensName of
|
|
27640
|
+
for (const lensName of normalizedLensConfig.outbound) {
|
|
26164
27641
|
try {
|
|
26165
27642
|
const token = await issueCapability$1(
|
|
26166
27643
|
["read"],
|
|
@@ -26182,13 +27659,37 @@ async function acceptFederationRequest$1(holosphere, privateKey, params) {
|
|
|
26182
27659
|
console.warn(`[Handshake] Failed to issue capability for ${lensName}:`, err.message);
|
|
26183
27660
|
}
|
|
26184
27661
|
}
|
|
27662
|
+
if (holosphere.keyStore && holosphere.client?.privateKey) {
|
|
27663
|
+
for (const lensName of normalizedLensConfig.outbound) {
|
|
27664
|
+
const lensPath = buildLensPath(holosphere.config.appName, responderPubKey, lensName);
|
|
27665
|
+
const lensKey = holosphere.keyStore.getKey(lensPath);
|
|
27666
|
+
if (lensKey) {
|
|
27667
|
+
try {
|
|
27668
|
+
const wrappedKey = holosphere.keyStore.wrapKeyForPartner(
|
|
27669
|
+
lensPath,
|
|
27670
|
+
holosphere.client.privateKey,
|
|
27671
|
+
senderPubKey
|
|
27672
|
+
);
|
|
27673
|
+
if (wrappedKey) {
|
|
27674
|
+
await sendLensKeyShare(holosphere.client, privateKey, senderPubKey, {
|
|
27675
|
+
lensPath,
|
|
27676
|
+
wrappedKey
|
|
27677
|
+
});
|
|
27678
|
+
console.log(`[Handshake] Shared lens key for "${lensName}" with partner`);
|
|
27679
|
+
}
|
|
27680
|
+
} catch (err) {
|
|
27681
|
+
console.warn(`[Handshake] Failed to share lens key for ${lensName}:`, err.message);
|
|
27682
|
+
}
|
|
27683
|
+
}
|
|
27684
|
+
}
|
|
27685
|
+
}
|
|
26185
27686
|
const response = createFederationResponse({
|
|
26186
27687
|
requestId: request.requestId,
|
|
26187
27688
|
status: "accepted",
|
|
26188
27689
|
responderHolonId: holonId,
|
|
26189
27690
|
responderHolonName: holonName,
|
|
26190
27691
|
responderPubKey,
|
|
26191
|
-
lensConfig,
|
|
27692
|
+
lensConfig: normalizedLensConfig,
|
|
26192
27693
|
capabilities: capabilities2,
|
|
26193
27694
|
message
|
|
26194
27695
|
});
|
|
@@ -26199,7 +27700,7 @@ async function acceptFederationRequest$1(holosphere, privateKey, params) {
|
|
|
26199
27700
|
console.log("[Handshake] Request dismissed after acceptance:", request.requestId);
|
|
26200
27701
|
}
|
|
26201
27702
|
const receivedHolograms = {};
|
|
26202
|
-
const inboundLenses =
|
|
27703
|
+
const inboundLenses = normalizedLensConfig.inbound;
|
|
26203
27704
|
if (inboundLenses.length > 0 && holosphere.receiveFederatedLens) {
|
|
26204
27705
|
console.log("[Handshake] Receiving federated lens data as holograms...");
|
|
26205
27706
|
for (const lensName of inboundLenses) {
|
|
@@ -26312,7 +27813,7 @@ function createFederationUpdate({
|
|
|
26312
27813
|
return {
|
|
26313
27814
|
type: "federation_update",
|
|
26314
27815
|
version: "1.0",
|
|
26315
|
-
updateId: generateNonce(),
|
|
27816
|
+
updateId: generateNonce$1(),
|
|
26316
27817
|
timestamp: Date.now(),
|
|
26317
27818
|
senderHolonId,
|
|
26318
27819
|
senderHolonName,
|
|
@@ -26370,7 +27871,7 @@ async function sendFederationUpdateResponse(client, privateKey, recipientPubKey,
|
|
|
26370
27871
|
async function requestFederationUpdate(holosphere, privateKey, params) {
|
|
26371
27872
|
const { partnerPubKey, holonId, holonName, newLensConfig, message } = params;
|
|
26372
27873
|
try {
|
|
26373
|
-
const senderPubKey = getPublicKey(privateKey);
|
|
27874
|
+
const senderPubKey = getPublicKey$1(privateKey);
|
|
26374
27875
|
const update2 = createFederationUpdate({
|
|
26375
27876
|
senderHolonId: holonId,
|
|
26376
27877
|
senderHolonName: holonName,
|
|
@@ -26439,6 +27940,8 @@ const handshake = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
|
|
|
26439
27940
|
isFederationResponse,
|
|
26440
27941
|
isFederationUpdate,
|
|
26441
27942
|
isFederationUpdateResponse,
|
|
27943
|
+
isV2LensConfig,
|
|
27944
|
+
normalizeLensConfig,
|
|
26442
27945
|
processFederationResponse,
|
|
26443
27946
|
rejectFederationRequest,
|
|
26444
27947
|
rejectFederationUpdate,
|
|
@@ -26447,7 +27950,9 @@ const handshake = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
|
|
|
26447
27950
|
sendFederationResponse,
|
|
26448
27951
|
sendFederationUpdate,
|
|
26449
27952
|
sendFederationUpdateResponse,
|
|
26450
|
-
|
|
27953
|
+
sendLensKeyShare,
|
|
27954
|
+
subscribeToFederationDMs,
|
|
27955
|
+
toUnifiedPermissions
|
|
26451
27956
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
26452
27957
|
function validateNostrEvent(event, partial = true, throwOnError = false) {
|
|
26453
27958
|
if (!event || typeof event !== "object") {
|
|
@@ -26578,7 +28083,7 @@ async function createSubscription(client, path, callback, options = {}) {
|
|
|
26578
28083
|
if (!isActive) {
|
|
26579
28084
|
return;
|
|
26580
28085
|
}
|
|
26581
|
-
const uniqueKey = `${key}-${data?.id || ""}-${data?._meta?.timestamp || Date.now()}`;
|
|
28086
|
+
const uniqueKey = `${key}-${data?.id || ""}-${data?._meta?.lastUpdated || data?._meta?.timestamp || Date.now()}`;
|
|
26582
28087
|
if (seenKeys.has(uniqueKey)) {
|
|
26583
28088
|
return;
|
|
26584
28089
|
}
|
|
@@ -36839,7 +38344,7 @@ class ChainManager {
|
|
|
36839
38344
|
*/
|
|
36840
38345
|
async _loadEthers() {
|
|
36841
38346
|
if (!this.ethers) {
|
|
36842
|
-
const ethersModule = await import("./index-
|
|
38347
|
+
const ethersModule = await import("./index-BdnrGafX.js");
|
|
36843
38348
|
this.ethers = ethersModule;
|
|
36844
38349
|
}
|
|
36845
38350
|
return this.ethers;
|
|
@@ -45630,7 +47135,7 @@ class ContractDeployer {
|
|
|
45630
47135
|
*/
|
|
45631
47136
|
async _loadEthers() {
|
|
45632
47137
|
if (!this.ethers) {
|
|
45633
|
-
this.ethers = await import("./index-
|
|
47138
|
+
this.ethers = await import("./index-BdnrGafX.js");
|
|
45634
47139
|
}
|
|
45635
47140
|
return this.ethers;
|
|
45636
47141
|
}
|
|
@@ -46016,7 +47521,7 @@ class ContractOperations {
|
|
|
46016
47521
|
*/
|
|
46017
47522
|
async _loadEthers() {
|
|
46018
47523
|
if (!this.ethers) {
|
|
46019
|
-
this.ethers = await import("./index-
|
|
47524
|
+
this.ethers = await import("./index-BdnrGafX.js");
|
|
46020
47525
|
}
|
|
46021
47526
|
return this.ethers;
|
|
46022
47527
|
}
|
|
@@ -57066,8 +58571,9 @@ function withFederationMethods(Base) {
|
|
|
57066
58571
|
permissions = ["read"],
|
|
57067
58572
|
filter = null
|
|
57068
58573
|
} = options;
|
|
57069
|
-
const
|
|
57070
|
-
const
|
|
58574
|
+
const isPubkey2 = (str2) => typeof str2 === "string" && /^[0-9a-f]{64}$/i.test(str2);
|
|
58575
|
+
const normalizedSource = typeof source === "string" ? { holonId: source, authorPubKey: isPubkey2(source) ? source : this.client.publicKey } : { holonId: source.holonId, authorPubKey: source.authorPubKey || (isPubkey2(source.holonId) ? source.holonId : this.client.publicKey) };
|
|
58576
|
+
const normalizedTarget = typeof target === "string" ? { holonId: target, authorPubKey: isPubkey2(target) ? target : this.client.publicKey } : { holonId: target.holonId, authorPubKey: target.authorPubKey || (isPubkey2(target.holonId) ? target.holonId : this.client.publicKey) };
|
|
57071
58577
|
if (normalizedSource.holonId === normalizedTarget.holonId && normalizedSource.authorPubKey === normalizedTarget.authorPubKey) {
|
|
57072
58578
|
throw new ValidationError("Cannot federate a holon with itself");
|
|
57073
58579
|
}
|
|
@@ -57715,6 +59221,259 @@ function withFederationMethods(Base) {
|
|
|
57715
59221
|
}
|
|
57716
59222
|
return results;
|
|
57717
59223
|
}
|
|
59224
|
+
// === UNIFIED ACCESS CONTROL ===
|
|
59225
|
+
/**
|
|
59226
|
+
* Unified access verification method.
|
|
59227
|
+
* Single entry point for checking all access permissions (read, write, etc.).
|
|
59228
|
+
* Replaces and unifies the previous separate access checking methods.
|
|
59229
|
+
*
|
|
59230
|
+
* @param {string} targetHolonId - The holon being accessed
|
|
59231
|
+
* @param {string} lensName - The lens being accessed
|
|
59232
|
+
* @param {string} actorPubKey - The actor's public key
|
|
59233
|
+
* @param {string} permission - Permission to check ('read' or 'write')
|
|
59234
|
+
* @param {Object} [options={}] - Options
|
|
59235
|
+
* @param {string} [options.actingAsHolon] - The holon the actor is acting on behalf of
|
|
59236
|
+
* @param {string} [options.capabilityToken] - Explicit capability token to verify
|
|
59237
|
+
* @returns {Promise<Object>} { allowed: boolean, via: string, reason?: string, grant?: Object }
|
|
59238
|
+
*
|
|
59239
|
+
* @example
|
|
59240
|
+
* // Check if current user can read from a holon
|
|
59241
|
+
* const result = await hs.canAccess(targetHolonId, 'quests', myPubKey, 'read');
|
|
59242
|
+
* if (result.allowed) {
|
|
59243
|
+
* console.log('Access granted via:', result.via);
|
|
59244
|
+
* }
|
|
59245
|
+
*
|
|
59246
|
+
* @example
|
|
59247
|
+
* // Check write access with explicit capability
|
|
59248
|
+
* const result = await hs.canAccess(targetHolonId, 'quests', myPubKey, 'write', {
|
|
59249
|
+
* capabilityToken: myCapToken
|
|
59250
|
+
* });
|
|
59251
|
+
*/
|
|
59252
|
+
async canAccess(targetHolonId, lensName, actorPubKey, permission, options = {}) {
|
|
59253
|
+
const { actingAsHolon = null, capabilityToken = null } = options;
|
|
59254
|
+
if (actorPubKey === targetHolonId) {
|
|
59255
|
+
return { allowed: true, via: "owner" };
|
|
59256
|
+
}
|
|
59257
|
+
if (capabilityToken) {
|
|
59258
|
+
try {
|
|
59259
|
+
const valid = await this.verifyCapability(capabilityToken, permission, {
|
|
59260
|
+
holonId: targetHolonId,
|
|
59261
|
+
lensName
|
|
59262
|
+
});
|
|
59263
|
+
if (valid) {
|
|
59264
|
+
return { allowed: true, via: "capability" };
|
|
59265
|
+
}
|
|
59266
|
+
} catch (err) {
|
|
59267
|
+
console.log("[canAccess] Capability verification failed:", err.message);
|
|
59268
|
+
}
|
|
59269
|
+
}
|
|
59270
|
+
try {
|
|
59271
|
+
const members = await this.get(targetHolonId, "users");
|
|
59272
|
+
if (members && Array.isArray(members)) {
|
|
59273
|
+
const isMember = members.some(
|
|
59274
|
+
(m) => m.pubKey === actorPubKey || m.id === actorPubKey || m.nostrPubKey === actorPubKey
|
|
59275
|
+
);
|
|
59276
|
+
if (isMember) {
|
|
59277
|
+
return { allowed: true, via: "membership" };
|
|
59278
|
+
}
|
|
59279
|
+
}
|
|
59280
|
+
} catch (err) {
|
|
59281
|
+
console.log("[canAccess] Could not read users lens:", err.message);
|
|
59282
|
+
}
|
|
59283
|
+
const scope = { holonId: targetHolonId, lensName };
|
|
59284
|
+
if (actingAsHolon) {
|
|
59285
|
+
const actorHolons = await this.getHolonsForPubKey(actorPubKey);
|
|
59286
|
+
const isMemberOfActingHolon = actorHolons.some((h) => h.id === actingAsHolon);
|
|
59287
|
+
if (!isMemberOfActingHolon) {
|
|
59288
|
+
return { allowed: false, via: "none", reason: "Not a member of the acting holon" };
|
|
59289
|
+
}
|
|
59290
|
+
const grant = await findAccessGrant(
|
|
59291
|
+
this.client,
|
|
59292
|
+
this.config.appName,
|
|
59293
|
+
actingAsHolon,
|
|
59294
|
+
scope,
|
|
59295
|
+
permission,
|
|
59296
|
+
{ direction: "inbound" }
|
|
59297
|
+
);
|
|
59298
|
+
if (grant) {
|
|
59299
|
+
return {
|
|
59300
|
+
allowed: true,
|
|
59301
|
+
via: "federation",
|
|
59302
|
+
grant,
|
|
59303
|
+
reason: `Acting as ${actingAsHolon} with ${permission} grant`
|
|
59304
|
+
};
|
|
59305
|
+
}
|
|
59306
|
+
} else {
|
|
59307
|
+
const actorHolons = await this.getHolonsForPubKey(actorPubKey);
|
|
59308
|
+
for (const holon of actorHolons) {
|
|
59309
|
+
const grant = await findAccessGrant(
|
|
59310
|
+
this.client,
|
|
59311
|
+
this.config.appName,
|
|
59312
|
+
holon.id,
|
|
59313
|
+
scope,
|
|
59314
|
+
permission,
|
|
59315
|
+
{ direction: "inbound" }
|
|
59316
|
+
);
|
|
59317
|
+
if (grant) {
|
|
59318
|
+
return {
|
|
59319
|
+
allowed: true,
|
|
59320
|
+
via: "federation",
|
|
59321
|
+
grant,
|
|
59322
|
+
reason: `Member of ${holon.name || holon.id} which has ${permission} grant`,
|
|
59323
|
+
viaHolon: holon.id
|
|
59324
|
+
};
|
|
59325
|
+
}
|
|
59326
|
+
}
|
|
59327
|
+
}
|
|
59328
|
+
return { allowed: false, via: "none", reason: "No access" };
|
|
59329
|
+
}
|
|
59330
|
+
// === CROSS-HOLON WRITE ACCESS ===
|
|
59331
|
+
/**
|
|
59332
|
+
* Check if a writer can write to a target holon's lens.
|
|
59333
|
+
* This is a backward-compatible wrapper that delegates to canAccess().
|
|
59334
|
+
*
|
|
59335
|
+
* @param {string} targetHolonId - The holon being written to
|
|
59336
|
+
* @param {string} lensName - The lens being written to
|
|
59337
|
+
* @param {string} writerPubKey - The writer's public key
|
|
59338
|
+
* @param {Object} [options={}] - Options
|
|
59339
|
+
* @param {string} [options.actingAsHolon] - The holon the writer is acting on behalf of
|
|
59340
|
+
* @param {string} [options.capabilityToken] - Explicit capability token to verify
|
|
59341
|
+
* @returns {Promise<Object>} { canWrite: boolean, reason: string, accessType: string }
|
|
59342
|
+
*
|
|
59343
|
+
* @example
|
|
59344
|
+
* // Check if current user can write to another holon
|
|
59345
|
+
* const result = await hs.canWrite(targetHolonId, 'quests', myPubKey);
|
|
59346
|
+
* if (result.canWrite) {
|
|
59347
|
+
* console.log('Access granted:', result.accessType);
|
|
59348
|
+
* }
|
|
59349
|
+
*/
|
|
59350
|
+
async canWrite(targetHolonId, lensName, writerPubKey, options = {}) {
|
|
59351
|
+
const result = await this.canAccess(targetHolonId, lensName, writerPubKey, "write", options);
|
|
59352
|
+
return {
|
|
59353
|
+
canWrite: result.allowed,
|
|
59354
|
+
reason: result.reason || result.via,
|
|
59355
|
+
accessType: result.via,
|
|
59356
|
+
viaHolon: result.viaHolon,
|
|
59357
|
+
grant: result.grant
|
|
59358
|
+
};
|
|
59359
|
+
}
|
|
59360
|
+
/**
|
|
59361
|
+
* Check if an actor can read from a target holon's lens.
|
|
59362
|
+
* Convenience wrapper around canAccess() for read operations.
|
|
59363
|
+
*
|
|
59364
|
+
* @param {string} targetHolonId - The holon being read from
|
|
59365
|
+
* @param {string} lensName - The lens being read from
|
|
59366
|
+
* @param {string} readerPubKey - The reader's public key
|
|
59367
|
+
* @param {Object} [options={}] - Options
|
|
59368
|
+
* @param {string} [options.actingAsHolon] - The holon the reader is acting on behalf of
|
|
59369
|
+
* @param {string} [options.capabilityToken] - Explicit capability token to verify
|
|
59370
|
+
* @returns {Promise<Object>} { canRead: boolean, reason: string, accessType: string }
|
|
59371
|
+
*/
|
|
59372
|
+
async canRead(targetHolonId, lensName, readerPubKey, options = {}) {
|
|
59373
|
+
const result = await this.canAccess(targetHolonId, lensName, readerPubKey, "read", options);
|
|
59374
|
+
return {
|
|
59375
|
+
canRead: result.allowed,
|
|
59376
|
+
reason: result.reason || result.via,
|
|
59377
|
+
accessType: result.via,
|
|
59378
|
+
viaHolon: result.viaHolon,
|
|
59379
|
+
grant: result.grant
|
|
59380
|
+
};
|
|
59381
|
+
}
|
|
59382
|
+
/**
|
|
59383
|
+
* Get all holons that a public key is a member of.
|
|
59384
|
+
* Searches federation registry and users lenses.
|
|
59385
|
+
*
|
|
59386
|
+
* @param {string} pubKey - The public key to look up
|
|
59387
|
+
* @returns {Promise<Array>} Array of { id, name } for each holon
|
|
59388
|
+
*/
|
|
59389
|
+
async getHolonsForPubKey(pubKey) {
|
|
59390
|
+
const results = [];
|
|
59391
|
+
results.push({ id: pubKey, name: "Personal Holon" });
|
|
59392
|
+
try {
|
|
59393
|
+
const reg = await getFederationRegistry(this.client, this.config.appName);
|
|
59394
|
+
if (reg && reg.federatedWith) {
|
|
59395
|
+
for (const partner of reg.federatedWith) {
|
|
59396
|
+
try {
|
|
59397
|
+
const members = await this.get(partner.pubKey, "users");
|
|
59398
|
+
if (members && Array.isArray(members)) {
|
|
59399
|
+
const isMember = members.some(
|
|
59400
|
+
(m) => m.pubKey === pubKey || m.id === pubKey || m.nostrPubKey === pubKey
|
|
59401
|
+
);
|
|
59402
|
+
if (isMember) {
|
|
59403
|
+
results.push({ id: partner.pubKey, name: partner.alias || partner.pubKey });
|
|
59404
|
+
}
|
|
59405
|
+
}
|
|
59406
|
+
} catch (err) {
|
|
59407
|
+
}
|
|
59408
|
+
}
|
|
59409
|
+
}
|
|
59410
|
+
} catch (err) {
|
|
59411
|
+
console.warn("[getHolonsForPubKey] Error reading federation registry:", err.message);
|
|
59412
|
+
}
|
|
59413
|
+
return results;
|
|
59414
|
+
}
|
|
59415
|
+
/**
|
|
59416
|
+
* Grant write access to a federated holon for a specific lens.
|
|
59417
|
+
* Members of the granted holon will be able to write to this lens.
|
|
59418
|
+
*
|
|
59419
|
+
* @param {string} holonId - The holon to grant write access to
|
|
59420
|
+
* @param {string} lensName - The lens to grant write access for (or '*' for all)
|
|
59421
|
+
* @param {Object} [options={}] - Grant options
|
|
59422
|
+
* @param {number} [options.expiresAt] - Optional expiration timestamp
|
|
59423
|
+
* @returns {Promise<boolean>} Success indicator
|
|
59424
|
+
*
|
|
59425
|
+
* @example
|
|
59426
|
+
* // Grant holon B write access to quests lens
|
|
59427
|
+
* await hs.grantWriteAccess('holon-b-pubkey', 'quests');
|
|
59428
|
+
*/
|
|
59429
|
+
async grantWriteAccess(holonId, lensName, options = {}) {
|
|
59430
|
+
return grantWriteAccessToHolon(
|
|
59431
|
+
this.client,
|
|
59432
|
+
this.config.appName,
|
|
59433
|
+
holonId,
|
|
59434
|
+
lensName,
|
|
59435
|
+
options
|
|
59436
|
+
);
|
|
59437
|
+
}
|
|
59438
|
+
/**
|
|
59439
|
+
* Revoke write access from a federated holon for a specific lens.
|
|
59440
|
+
*
|
|
59441
|
+
* @param {string} holonId - The holon to revoke write access from
|
|
59442
|
+
* @param {string} lensName - The lens to revoke write access for
|
|
59443
|
+
* @returns {Promise<boolean>} Success indicator
|
|
59444
|
+
*/
|
|
59445
|
+
async revokeWriteAccess(holonId, lensName) {
|
|
59446
|
+
return revokeWriteAccess(
|
|
59447
|
+
this.client,
|
|
59448
|
+
this.config.appName,
|
|
59449
|
+
holonId,
|
|
59450
|
+
lensName
|
|
59451
|
+
);
|
|
59452
|
+
}
|
|
59453
|
+
/**
|
|
59454
|
+
* Get write grants for a specific holon.
|
|
59455
|
+
*
|
|
59456
|
+
* @param {string} holonId - The holon to get write grants for
|
|
59457
|
+
* @returns {Promise<Array>} Array of write grants { lensName, grantedAt, expiresAt }
|
|
59458
|
+
*/
|
|
59459
|
+
async getWriteGrantsForHolon(holonId) {
|
|
59460
|
+
return getWriteGrantsForHolon(
|
|
59461
|
+
this.client,
|
|
59462
|
+
this.config.appName,
|
|
59463
|
+
holonId
|
|
59464
|
+
);
|
|
59465
|
+
}
|
|
59466
|
+
/**
|
|
59467
|
+
* Get all write grants issued to federated holons.
|
|
59468
|
+
*
|
|
59469
|
+
* @returns {Promise<Array>} Array of { holonId, alias, writeGrants }
|
|
59470
|
+
*/
|
|
59471
|
+
async getAllWriteGrants() {
|
|
59472
|
+
return getAllWriteGrants(
|
|
59473
|
+
this.client,
|
|
59474
|
+
this.config.appName
|
|
59475
|
+
);
|
|
59476
|
+
}
|
|
57718
59477
|
};
|
|
57719
59478
|
}
|
|
57720
59479
|
function createAIServices(apiKey, holosphere = null, options = {}, openaiClient = null) {
|
|
@@ -57795,6 +59554,11 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
57795
59554
|
this._initializeAI(openaiKey, aiOptions);
|
|
57796
59555
|
}
|
|
57797
59556
|
this._contracts = null;
|
|
59557
|
+
this.keyStore = new LensKeyStore();
|
|
59558
|
+
this._encryptionConfig = {
|
|
59559
|
+
encryptByDefault: config.encryptByDefault ?? false,
|
|
59560
|
+
publicLenses: config.publicLenses || ["profile", "settings", "public"]
|
|
59561
|
+
};
|
|
57798
59562
|
}
|
|
57799
59563
|
/**
|
|
57800
59564
|
* Gets an environment variable value (Node.js only).
|
|
@@ -57969,6 +59733,7 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
57969
59733
|
* @param {Object} [options.propagationOptions] - Options for propagation
|
|
57970
59734
|
* @param {boolean} [options.blocking=false] - If true, wait for relay confirmation before returning
|
|
57971
59735
|
* @param {string} [options.signingKey] - Private key to sign with (hex format). If not provided, uses holosphere's default key.
|
|
59736
|
+
* @param {boolean} [options.encrypt] - Whether to encrypt this data. Defaults to encryptByDefault config.
|
|
57972
59737
|
* @returns {Promise<boolean>} True if write succeeded (or queued for optimistic writes)
|
|
57973
59738
|
* @throws {ValidationError} If holonId, lensName, or data is invalid
|
|
57974
59739
|
* @throws {AuthorizationError} If capability token is invalid
|
|
@@ -57985,8 +59750,61 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
57985
59750
|
if (!data || typeof data !== "object") {
|
|
57986
59751
|
throw new ValidationError$1("ValidationError: data must be an object");
|
|
57987
59752
|
}
|
|
59753
|
+
const isPubkeyHolon = typeof holonId === "string" && /^[0-9a-f]{64}$/i.test(holonId);
|
|
59754
|
+
let effectiveWriterPubKey = this.client?.publicKey;
|
|
59755
|
+
if (options.signingKey) {
|
|
59756
|
+
try {
|
|
59757
|
+
effectiveWriterPubKey = getPublicKey(options.signingKey);
|
|
59758
|
+
} catch (err) {
|
|
59759
|
+
this._log("WARN", "⚠️ Invalid signingKey provided, using client key", { error: err.message });
|
|
59760
|
+
}
|
|
59761
|
+
}
|
|
59762
|
+
const isOwnHolon = effectiveWriterPubKey && (holonId === effectiveWriterPubKey || !isPubkeyHolon);
|
|
57988
59763
|
const capToken = options.capabilityToken || options.capability;
|
|
57989
|
-
|
|
59764
|
+
const actingAsHolon = options.actingAs || options.actingAsHolon;
|
|
59765
|
+
if (!isOwnHolon) {
|
|
59766
|
+
if (capToken) {
|
|
59767
|
+
const authorized = await this.verifyCapability(capToken, "write", { holonId, lensName });
|
|
59768
|
+
if (!authorized) {
|
|
59769
|
+
this._log("WARN", "🚫 Write denied: Invalid capability token", { holonId, lensName });
|
|
59770
|
+
throw new AuthorizationError(
|
|
59771
|
+
"Invalid capability token for write operation",
|
|
59772
|
+
"write"
|
|
59773
|
+
);
|
|
59774
|
+
}
|
|
59775
|
+
this._log("DEBUG", "✅ Write authorized via capability token", { holonId, lensName });
|
|
59776
|
+
} else {
|
|
59777
|
+
const writerPubKey = effectiveWriterPubKey;
|
|
59778
|
+
if (!writerPubKey) {
|
|
59779
|
+
this._log("WARN", "🚫 Write denied: No authenticated user", { holonId, lensName });
|
|
59780
|
+
throw new AuthorizationError(
|
|
59781
|
+
"Authentication required for writing to federated holons",
|
|
59782
|
+
"write"
|
|
59783
|
+
);
|
|
59784
|
+
}
|
|
59785
|
+
const accessCheck = await this.canAccess(holonId, lensName, writerPubKey, "write", { actingAsHolon });
|
|
59786
|
+
if (!accessCheck.allowed) {
|
|
59787
|
+
this._log("WARN", "🚫 Write denied: No write access", {
|
|
59788
|
+
holonId,
|
|
59789
|
+
lensName,
|
|
59790
|
+
reason: accessCheck.reason,
|
|
59791
|
+
via: accessCheck.via,
|
|
59792
|
+
writerPubKey: writerPubKey?.slice(0, 12) + "..."
|
|
59793
|
+
});
|
|
59794
|
+
throw new AuthorizationError(
|
|
59795
|
+
`Write access denied: ${accessCheck.reason}`,
|
|
59796
|
+
"write"
|
|
59797
|
+
);
|
|
59798
|
+
}
|
|
59799
|
+
this._log("DEBUG", "✅ Write authorized via unified access control", {
|
|
59800
|
+
holonId,
|
|
59801
|
+
lensName,
|
|
59802
|
+
via: accessCheck.via,
|
|
59803
|
+
reason: accessCheck.reason,
|
|
59804
|
+
grant: accessCheck.grant ? "yes" : "no"
|
|
59805
|
+
});
|
|
59806
|
+
}
|
|
59807
|
+
} else if (capToken) {
|
|
57990
59808
|
const authorized = await this.verifyCapability(capToken, "write", { holonId, lensName });
|
|
57991
59809
|
if (!authorized) {
|
|
57992
59810
|
throw new AuthorizationError("AuthorizationError: Invalid capability token for write operation", "write");
|
|
@@ -58050,7 +59868,29 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58050
59868
|
this._log("DEBUG", "🔗 Syncing hologram write", { path, target: existingData.target });
|
|
58051
59869
|
return this._syncHologramWrite(existingData, data, path, lensName, options);
|
|
58052
59870
|
}
|
|
58053
|
-
const
|
|
59871
|
+
const shouldEncrypt = this._shouldEncryptLens(lensName, options);
|
|
59872
|
+
let lensKey = null;
|
|
59873
|
+
if (shouldEncrypt && this.client?.privateKey && this.client?.publicKey) {
|
|
59874
|
+
const lensPath = buildLensPath(this.config.appName, holonId, lensName);
|
|
59875
|
+
const keyResult = this.keyStore.getOrCreateKey(
|
|
59876
|
+
lensPath,
|
|
59877
|
+
this.client.privateKey,
|
|
59878
|
+
this.client.publicKey
|
|
59879
|
+
);
|
|
59880
|
+
lensKey = keyResult.key;
|
|
59881
|
+
if (keyResult.isNew) {
|
|
59882
|
+
this._log("DEBUG", "🔑 Generated new lens key", { lensPath });
|
|
59883
|
+
this._storeWrappedKey(lensPath, keyResult.wrappedForSelf).catch((err) => {
|
|
59884
|
+
this._log("WARN", "⚠️ Failed to persist wrapped key", { lensPath, error: err.message });
|
|
59885
|
+
});
|
|
59886
|
+
}
|
|
59887
|
+
}
|
|
59888
|
+
const writeOptions = {
|
|
59889
|
+
...options.signingKey ? { signingKey: options.signingKey } : {},
|
|
59890
|
+
...lensKey ? { lensKey } : {},
|
|
59891
|
+
waitForRelays: true
|
|
59892
|
+
// Always wait for relay confirmation when sync is called
|
|
59893
|
+
};
|
|
58054
59894
|
await write(this.client, path, data, writeOptions);
|
|
58055
59895
|
const endTime = Date.now();
|
|
58056
59896
|
const duration = endTime - startTime;
|
|
@@ -58118,6 +59958,22 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58118
59958
|
const startTime = Date.now();
|
|
58119
59959
|
await write(this.client, path, localData);
|
|
58120
59960
|
if (Object.keys(sourceUpdates).length > 0) {
|
|
59961
|
+
const capability = existingData.capability;
|
|
59962
|
+
if (!capability) {
|
|
59963
|
+
throw new Error("Hologram missing capability token for source update");
|
|
59964
|
+
}
|
|
59965
|
+
const hasWrite = await verifyCapability$1(
|
|
59966
|
+
capability,
|
|
59967
|
+
"write",
|
|
59968
|
+
{
|
|
59969
|
+
holonId: existingData.target.holonId,
|
|
59970
|
+
lensName: existingData.target.lensName,
|
|
59971
|
+
dataId: existingData.target.dataId
|
|
59972
|
+
}
|
|
59973
|
+
);
|
|
59974
|
+
if (!hasWrite) {
|
|
59975
|
+
throw new Error("Write capability required to update source data through hologram");
|
|
59976
|
+
}
|
|
58121
59977
|
const sourcePath = buildPath(
|
|
58122
59978
|
existingData.target.appname || this.config.appName,
|
|
58123
59979
|
existingData.target.holonId,
|
|
@@ -58162,6 +60018,156 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58162
60018
|
async _getCapabilityForAuthor(authorPubKey, scope) {
|
|
58163
60019
|
return getCapabilityForAuthor(this.client, this.config.appName, authorPubKey, scope);
|
|
58164
60020
|
}
|
|
60021
|
+
/**
|
|
60022
|
+
* Determine if a lens should be encrypted.
|
|
60023
|
+
*
|
|
60024
|
+
* @private
|
|
60025
|
+
* @param {string} lensName - Name of the lens
|
|
60026
|
+
* @param {Object} options - Write options
|
|
60027
|
+
* @returns {boolean} True if lens should be encrypted
|
|
60028
|
+
*/
|
|
60029
|
+
_shouldEncryptLens(lensName, options = {}) {
|
|
60030
|
+
if (options.encrypt !== void 0) {
|
|
60031
|
+
return options.encrypt;
|
|
60032
|
+
}
|
|
60033
|
+
if (this._encryptionConfig.publicLenses.includes(lensName)) {
|
|
60034
|
+
return false;
|
|
60035
|
+
}
|
|
60036
|
+
return this._encryptionConfig.encryptByDefault;
|
|
60037
|
+
}
|
|
60038
|
+
/**
|
|
60039
|
+
* Store a wrapped lens key in Nostr for persistence.
|
|
60040
|
+
* This allows recovering keys after restart.
|
|
60041
|
+
*
|
|
60042
|
+
* @private
|
|
60043
|
+
* @param {string} lensPath - Path identifying the lens
|
|
60044
|
+
* @param {string} wrappedKey - NIP-44 wrapped key for self
|
|
60045
|
+
* @returns {Promise<void>}
|
|
60046
|
+
*/
|
|
60047
|
+
async _storeWrappedKey(lensPath, wrappedKey) {
|
|
60048
|
+
const keyStorePath = buildPath(this.config.appName, "_keys", "lens-keys", lensPath.replace(/\//g, "_"));
|
|
60049
|
+
await write(this.client, keyStorePath, {
|
|
60050
|
+
id: lensPath.replace(/\//g, "_"),
|
|
60051
|
+
lensPath,
|
|
60052
|
+
wrappedKey,
|
|
60053
|
+
algorithm: "aes-256-gcm",
|
|
60054
|
+
keyWrap: "nip44",
|
|
60055
|
+
version: "1.0",
|
|
60056
|
+
createdAt: Date.now()
|
|
60057
|
+
});
|
|
60058
|
+
}
|
|
60059
|
+
/**
|
|
60060
|
+
* Load stored wrapped keys from Nostr and restore them to the keyStore.
|
|
60061
|
+
* Called during initialization to recover keys after restart.
|
|
60062
|
+
*
|
|
60063
|
+
* @private
|
|
60064
|
+
* @returns {Promise<number>} Number of keys restored
|
|
60065
|
+
*/
|
|
60066
|
+
async _loadStoredKeys() {
|
|
60067
|
+
if (!this.client?.privateKey || !this.client?.publicKey) {
|
|
60068
|
+
return 0;
|
|
60069
|
+
}
|
|
60070
|
+
const keyStorePath = buildPath(this.config.appName, "_keys", "lens-keys") + "/";
|
|
60071
|
+
const storedKeys = await readAll(this.client, keyStorePath);
|
|
60072
|
+
let restored = 0;
|
|
60073
|
+
for (const keyData of storedKeys) {
|
|
60074
|
+
if (keyData && keyData.lensPath && keyData.wrappedKey) {
|
|
60075
|
+
try {
|
|
60076
|
+
const key = unwrapKey(
|
|
60077
|
+
keyData.wrappedKey,
|
|
60078
|
+
this.client.privateKey,
|
|
60079
|
+
this.client.publicKey
|
|
60080
|
+
);
|
|
60081
|
+
this.keyStore.storeOwnKey(keyData.lensPath, key, keyData.wrappedKey);
|
|
60082
|
+
restored++;
|
|
60083
|
+
} catch (err) {
|
|
60084
|
+
this._log("WARN", "⚠️ Failed to restore lens key", { lensPath: keyData.lensPath, error: err.message });
|
|
60085
|
+
}
|
|
60086
|
+
}
|
|
60087
|
+
}
|
|
60088
|
+
if (restored > 0) {
|
|
60089
|
+
this._log("DEBUG", "🔑 Restored lens keys", { count: restored });
|
|
60090
|
+
}
|
|
60091
|
+
return restored;
|
|
60092
|
+
}
|
|
60093
|
+
/**
|
|
60094
|
+
* Get the lens key for reading encrypted data.
|
|
60095
|
+
* Returns the key from keyStore if available, otherwise null.
|
|
60096
|
+
*
|
|
60097
|
+
* @private
|
|
60098
|
+
* @param {string} holonId - Holon identifier
|
|
60099
|
+
* @param {string} lensName - Lens name
|
|
60100
|
+
* @returns {Buffer|null} Lens key or null if not available
|
|
60101
|
+
*/
|
|
60102
|
+
_getLensKey(holonId, lensName) {
|
|
60103
|
+
const lensPath = buildLensPath(this.config.appName, holonId, lensName);
|
|
60104
|
+
return this.keyStore.getKey(lensPath);
|
|
60105
|
+
}
|
|
60106
|
+
/**
|
|
60107
|
+
* Handle a received lens key share from a federation partner.
|
|
60108
|
+
* Call this from your onKeyShare handler in subscribeToFederationDMs.
|
|
60109
|
+
*
|
|
60110
|
+
* @param {Object} keyShare - Key share payload
|
|
60111
|
+
* @param {string} keyShare.lensPath - Path identifying the lens
|
|
60112
|
+
* @param {string} keyShare.wrappedKey - NIP-44 wrapped symmetric key
|
|
60113
|
+
* @param {string} senderPubKey - Sender's public key
|
|
60114
|
+
* @returns {boolean} True if key was successfully stored
|
|
60115
|
+
*/
|
|
60116
|
+
handleReceivedKeyShare(keyShare, senderPubKey) {
|
|
60117
|
+
if (!this.client?.privateKey) {
|
|
60118
|
+
this._log("WARN", "⚠️ Cannot receive key share: no private key");
|
|
60119
|
+
return false;
|
|
60120
|
+
}
|
|
60121
|
+
try {
|
|
60122
|
+
const key = this.keyStore.receiveKey(
|
|
60123
|
+
keyShare.lensPath,
|
|
60124
|
+
keyShare.wrappedKey,
|
|
60125
|
+
this.client.privateKey,
|
|
60126
|
+
senderPubKey
|
|
60127
|
+
);
|
|
60128
|
+
this._log("DEBUG", "🔑 Received and stored lens key", {
|
|
60129
|
+
lensPath: keyShare.lensPath,
|
|
60130
|
+
from: senderPubKey.slice(0, 12) + "..."
|
|
60131
|
+
});
|
|
60132
|
+
return true;
|
|
60133
|
+
} catch (error) {
|
|
60134
|
+
this._log("ERROR", "❌ Failed to receive key share", {
|
|
60135
|
+
lensPath: keyShare.lensPath,
|
|
60136
|
+
error: error.message
|
|
60137
|
+
});
|
|
60138
|
+
return false;
|
|
60139
|
+
}
|
|
60140
|
+
}
|
|
60141
|
+
/**
|
|
60142
|
+
* Get encryption statistics for the key store.
|
|
60143
|
+
*
|
|
60144
|
+
* @returns {Object} { ownKeyCount, receivedKeyCount, totalPartnerGrants }
|
|
60145
|
+
*/
|
|
60146
|
+
getEncryptionStats() {
|
|
60147
|
+
return this.keyStore.getStats();
|
|
60148
|
+
}
|
|
60149
|
+
/**
|
|
60150
|
+
* Check if a lens has an encryption key available.
|
|
60151
|
+
*
|
|
60152
|
+
* @param {string} holonId - Holon identifier
|
|
60153
|
+
* @param {string} lensName - Lens name
|
|
60154
|
+
* @returns {boolean} True if key is available for this lens
|
|
60155
|
+
*/
|
|
60156
|
+
hasLensKey(holonId, lensName) {
|
|
60157
|
+
const lensPath = buildLensPath(this.config.appName, holonId, lensName);
|
|
60158
|
+
return this.keyStore.hasKey(lensPath);
|
|
60159
|
+
}
|
|
60160
|
+
/**
|
|
60161
|
+
* Check if we own the key for a lens (vs received it from a partner).
|
|
60162
|
+
*
|
|
60163
|
+
* @param {string} holonId - Holon identifier
|
|
60164
|
+
* @param {string} lensName - Lens name
|
|
60165
|
+
* @returns {boolean} True if we own the key
|
|
60166
|
+
*/
|
|
60167
|
+
isOwnLensKey(holonId, lensName) {
|
|
60168
|
+
const lensPath = buildLensPath(this.config.appName, holonId, lensName);
|
|
60169
|
+
return this.keyStore.isOwnKey(lensPath);
|
|
60170
|
+
}
|
|
58165
60171
|
/**
|
|
58166
60172
|
* Recursively resolves holograms (references) to their source data.
|
|
58167
60173
|
* Handles circular reference detection and local overrides.
|
|
@@ -58292,9 +60298,17 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58292
60298
|
return cached.data;
|
|
58293
60299
|
}
|
|
58294
60300
|
}
|
|
58295
|
-
const targetPubkey = await this._resolveHolonToPubkey(holonId);
|
|
60301
|
+
const targetPubkey = options.skipResolve ? null : await this._resolveHolonToPubkey(holonId);
|
|
58296
60302
|
const isOtherAuthor = targetPubkey && targetPubkey !== this.client.publicKey;
|
|
60303
|
+
this._log("DEBUG", "🔍 READ: author check", {
|
|
60304
|
+
targetPubkey: targetPubkey?.slice(0, 12),
|
|
60305
|
+
clientPubkey: this.client?.publicKey?.slice(0, 12),
|
|
60306
|
+
isOtherAuthor,
|
|
60307
|
+
holonId: holonId?.slice(0, 12)
|
|
60308
|
+
});
|
|
58297
60309
|
let readOptions = {};
|
|
60310
|
+
if (options.forceRelay) readOptions.forceRelay = true;
|
|
60311
|
+
if (options.timeout) readOptions.timeout = options.timeout;
|
|
58298
60312
|
const capToken = options.capabilityToken || options.capability;
|
|
58299
60313
|
if (capToken) {
|
|
58300
60314
|
const authorized = await this.verifyCapability(capToken, "read", { holonId, lensName, dataId });
|
|
@@ -58328,18 +60342,23 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58328
60342
|
});
|
|
58329
60343
|
readOptions.authors = [targetPubkey];
|
|
58330
60344
|
}
|
|
60345
|
+
this._log("DEBUG", "🔍 Checking for federated authors", { isOtherAuthor, holonId: holonId?.slice(0, 12) });
|
|
58331
60346
|
if (!isOtherAuthor) {
|
|
58332
60347
|
try {
|
|
58333
|
-
|
|
60348
|
+
this._log("DEBUG", "🔍 Getting federated authors...");
|
|
60349
|
+
const fedAuthorsPromise = getFederatedAuthors(
|
|
58334
60350
|
this.client,
|
|
58335
|
-
this.config.appName
|
|
58336
|
-
{ holonId, lensName, dataId },
|
|
58337
|
-
"read"
|
|
60351
|
+
this.config.appName
|
|
58338
60352
|
);
|
|
60353
|
+
const fedAuthorsTimeout = new Promise(
|
|
60354
|
+
(resolve) => setTimeout(() => resolve([]), 5e3)
|
|
60355
|
+
// 5 second timeout, resolve with empty array
|
|
60356
|
+
);
|
|
60357
|
+
const federatedAuthors = await Promise.race([fedAuthorsPromise, fedAuthorsTimeout]);
|
|
60358
|
+
this._log("DEBUG", "✅ Got federated authors", { count: federatedAuthors?.length || 0 });
|
|
58339
60359
|
if (federatedAuthors.length > 0) {
|
|
58340
|
-
|
|
58341
|
-
|
|
58342
|
-
this._log("DEBUG", "Including federated authors", { count: partnerPubkeys.length });
|
|
60360
|
+
readOptions.authors = [this.client.publicKey, ...federatedAuthors];
|
|
60361
|
+
this._log("DEBUG", "Including federated authors", { count: federatedAuthors.length });
|
|
58343
60362
|
}
|
|
58344
60363
|
} catch (err) {
|
|
58345
60364
|
this._log("WARN", "Failed to get federated authors", { error: err.message });
|
|
@@ -58347,6 +60366,10 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58347
60366
|
}
|
|
58348
60367
|
const startTime = Date.now();
|
|
58349
60368
|
let result;
|
|
60369
|
+
const lensKey = this._getLensKey(holonId, lensName);
|
|
60370
|
+
if (lensKey) {
|
|
60371
|
+
readOptions.lensKey = lensKey;
|
|
60372
|
+
}
|
|
58350
60373
|
if (dataId) {
|
|
58351
60374
|
const path = buildPath(this.config.appName, holonId, lensName, dataId);
|
|
58352
60375
|
if (this._deleteCache.has(path)) {
|
|
@@ -58368,7 +60391,7 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58368
60391
|
});
|
|
58369
60392
|
result = cached.data;
|
|
58370
60393
|
} else {
|
|
58371
|
-
this._log("DEBUG", "📖 CACHE MISS: Reading from storage", { path, authors: readOptions.authors });
|
|
60394
|
+
this._log("DEBUG", "📖 CACHE MISS: Reading from storage", { path, authors: readOptions.authors, hasKey: !!lensKey });
|
|
58372
60395
|
result = await read(this.client, path, readOptions);
|
|
58373
60396
|
this._log("DEBUG", "💾 STORAGE READ", {
|
|
58374
60397
|
path,
|
|
@@ -58380,7 +60403,7 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58380
60403
|
}
|
|
58381
60404
|
} else {
|
|
58382
60405
|
const path = buildPath(this.config.appName, holonId, lensName);
|
|
58383
|
-
this._log("DEBUG", "readAll", { holonId, lensName, path, authors: readOptions.authors });
|
|
60406
|
+
this._log("DEBUG", "readAll", { holonId, lensName, path, authors: readOptions.authors, hasKey: !!lensKey });
|
|
58384
60407
|
const storageResult = await readAll(this.client, path, readOptions);
|
|
58385
60408
|
result = isOtherAuthor ? storageResult : this._mergeWithWriteCache(storageResult, path);
|
|
58386
60409
|
this._log("DEBUG", "readAll result", { count: Array.isArray(result) ? result.length : 0 });
|
|
@@ -58456,8 +60479,26 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58456
60479
|
if (!updates || typeof updates !== "object") {
|
|
58457
60480
|
throw new ValidationError$1("ValidationError: updates must be an object");
|
|
58458
60481
|
}
|
|
60482
|
+
const isPubkeyHolon = typeof holonId === "string" && /^[0-9a-f]{64}$/i.test(holonId);
|
|
60483
|
+
const isOwnHolon = this.client && (holonId === this.client.publicKey || !isPubkeyHolon);
|
|
58459
60484
|
const capToken = options.capabilityToken || options.capability;
|
|
58460
|
-
if (
|
|
60485
|
+
if (!isOwnHolon) {
|
|
60486
|
+
if (!capToken) {
|
|
60487
|
+
this._log("WARN", "🚫 Update denied: No capability token for federated holon", { holonId, lensName, dataId });
|
|
60488
|
+
throw new AuthorizationError(
|
|
60489
|
+
"Capability token required for writing to federated holons",
|
|
60490
|
+
"write"
|
|
60491
|
+
);
|
|
60492
|
+
}
|
|
60493
|
+
const authorized = await this.verifyCapability(capToken, "write", { holonId, lensName, dataId });
|
|
60494
|
+
if (!authorized) {
|
|
60495
|
+
this._log("WARN", "🚫 Update denied: Invalid capability token", { holonId, lensName, dataId });
|
|
60496
|
+
throw new AuthorizationError(
|
|
60497
|
+
"Invalid capability token for update operation",
|
|
60498
|
+
"write"
|
|
60499
|
+
);
|
|
60500
|
+
}
|
|
60501
|
+
} else if (capToken) {
|
|
58461
60502
|
const authorized = await this.verifyCapability(capToken, "write", { holonId, lensName, dataId });
|
|
58462
60503
|
if (!authorized) {
|
|
58463
60504
|
throw new AuthorizationError("AuthorizationError: Invalid capability token for update operation", "write");
|
|
@@ -58528,7 +60569,7 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58528
60569
|
});
|
|
58529
60570
|
if (existingData._meta && existingData._meta.activeHolograms) {
|
|
58530
60571
|
this._log("DEBUG", "🔗 Refreshing active holograms", { path });
|
|
58531
|
-
refreshActiveHolograms(this.client, this.config.appName, holonId, lensName, dataId).catch((err) => this._log("WARN", "⚠️ Hologram refresh failed", { path, error: err.message }));
|
|
60572
|
+
await refreshActiveHolograms(this.client, this.config.appName, holonId, lensName, dataId).catch((err) => this._log("WARN", "⚠️ Hologram refresh failed", { path, error: err.message }));
|
|
58532
60573
|
}
|
|
58533
60574
|
return true;
|
|
58534
60575
|
} catch (error) {
|
|
@@ -58571,6 +60612,16 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58571
60612
|
if (!dataId) {
|
|
58572
60613
|
throw new ValidationError$1("ValidationError: dataId must be a non-empty string");
|
|
58573
60614
|
}
|
|
60615
|
+
const isPubkeyHolon = typeof holonId === "string" && /^[0-9a-f]{64}$/i.test(holonId);
|
|
60616
|
+
const isOwnHolon = this.client && (holonId === this.client.publicKey || !isPubkeyHolon);
|
|
60617
|
+
const capToken = options.capabilityToken || options.capability;
|
|
60618
|
+
if (!isOwnHolon && !capToken) {
|
|
60619
|
+
this._log("WARN", "🚫 Delete denied: No capability token for federated holon", { holonId, lensName, dataId });
|
|
60620
|
+
throw new AuthorizationError(
|
|
60621
|
+
"Capability token required for writing to federated holons",
|
|
60622
|
+
"delete"
|
|
60623
|
+
);
|
|
60624
|
+
}
|
|
58574
60625
|
const path = buildPath(this.config.appName, holonId, lensName, dataId);
|
|
58575
60626
|
const cached = this._writeCache.get(path);
|
|
58576
60627
|
let existingData = cached ? cached.data : null;
|
|
@@ -58585,9 +60636,8 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58585
60636
|
}
|
|
58586
60637
|
this._log("DEBUG", "🗑️ DELETE: Found data", { path, source: dataSource });
|
|
58587
60638
|
const dataOwner = existingData.owner || existingData._creator;
|
|
58588
|
-
const
|
|
58589
|
-
|
|
58590
|
-
if (!isOwner) {
|
|
60639
|
+
const isDataOwner = !dataOwner || dataOwner === this.client.publicKey;
|
|
60640
|
+
if (!isDataOwner) {
|
|
58591
60641
|
if (!capToken) {
|
|
58592
60642
|
throw new AuthorizationError("AuthorizationError: Capability token required for delete operation", "delete");
|
|
58593
60643
|
}
|
|
@@ -58870,15 +60920,22 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
58870
60920
|
* @param {string} lensName - Name of the lens
|
|
58871
60921
|
* @param {Object} data - Data object containing the id to reference
|
|
58872
60922
|
* @param {string} [targetHolon=null] - Target holon for the hologram, defaults to sourceHolon
|
|
58873
|
-
* @returns {Object} Hologram object structure
|
|
60923
|
+
* @returns {Promise<Object>} Hologram object structure
|
|
58874
60924
|
*/
|
|
58875
|
-
createHologram(sourceHolon, lensName, data, targetHolon = null) {
|
|
58876
|
-
|
|
60925
|
+
async createHologram(sourceHolon, lensName, data, targetHolon = null) {
|
|
60926
|
+
const target = targetHolon || sourceHolon;
|
|
60927
|
+
return createHologramWithCapability(
|
|
60928
|
+
this.client,
|
|
58877
60929
|
sourceHolon,
|
|
58878
|
-
|
|
60930
|
+
target,
|
|
58879
60931
|
lensName,
|
|
58880
60932
|
data.id,
|
|
58881
|
-
this.config.appName
|
|
60933
|
+
this.config.appName,
|
|
60934
|
+
{
|
|
60935
|
+
sourceAuthorPubKey: this.client.publicKey,
|
|
60936
|
+
targetAuthorPubKey: this.client.publicKey,
|
|
60937
|
+
permissions: ["read"]
|
|
60938
|
+
}
|
|
58882
60939
|
);
|
|
58883
60940
|
}
|
|
58884
60941
|
/**
|
|
@@ -59187,6 +61244,8 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
59187
61244
|
federationData.lensConfig[targetHolon] = {
|
|
59188
61245
|
inbound: lensConfig.inbound || [],
|
|
59189
61246
|
outbound: lensConfig.outbound || [],
|
|
61247
|
+
writeInbound: lensConfig.writeInbound || [],
|
|
61248
|
+
writeOutbound: lensConfig.writeOutbound || [],
|
|
59190
61249
|
timestamp: Date.now()
|
|
59191
61250
|
};
|
|
59192
61251
|
const success = await this.writeGlobal("federation", federationData);
|
|
@@ -59323,7 +61382,7 @@ class HoloSphereBase extends HoloSphere$1 {
|
|
|
59323
61382
|
* @returns {Promise<string>} Hex-encoded public key
|
|
59324
61383
|
*/
|
|
59325
61384
|
async getPublicKey(privateKey) {
|
|
59326
|
-
return getPublicKey
|
|
61385
|
+
return getPublicKey(privateKey);
|
|
59327
61386
|
}
|
|
59328
61387
|
/**
|
|
59329
61388
|
* Signs content with a private key.
|
|
@@ -59789,26 +61848,30 @@ export {
|
|
|
59789
61848
|
networks as bA,
|
|
59790
61849
|
matchScope as bB,
|
|
59791
61850
|
createHologram as bC,
|
|
59792
|
-
|
|
59793
|
-
|
|
59794
|
-
|
|
59795
|
-
|
|
59796
|
-
|
|
59797
|
-
|
|
59798
|
-
|
|
59799
|
-
|
|
59800
|
-
|
|
59801
|
-
|
|
59802
|
-
|
|
59803
|
-
|
|
59804
|
-
|
|
59805
|
-
|
|
59806
|
-
|
|
59807
|
-
|
|
59808
|
-
|
|
59809
|
-
|
|
59810
|
-
|
|
59811
|
-
|
|
61851
|
+
LensKeyStore as bD,
|
|
61852
|
+
buildLensPath as bE,
|
|
61853
|
+
parseLensPath as bF,
|
|
61854
|
+
lensKeys as bG,
|
|
61855
|
+
createFederationCard as bH,
|
|
61856
|
+
getVisibleLenses as bI,
|
|
61857
|
+
getLensConfigForHandshake as bJ,
|
|
61858
|
+
toggleLens as bK,
|
|
61859
|
+
toggleCardExpansion as bL,
|
|
61860
|
+
dismissCard$1 as bM,
|
|
61861
|
+
saveCard as bN,
|
|
61862
|
+
getCard as bO,
|
|
61863
|
+
getDisplayableCards as bP,
|
|
61864
|
+
dismissRequest as bQ,
|
|
61865
|
+
markResponseProcessed as bR,
|
|
61866
|
+
isResponseProcessed as bS,
|
|
61867
|
+
createAIServices as bT,
|
|
61868
|
+
NETWORKS as bU,
|
|
61869
|
+
getNetwork as bV,
|
|
61870
|
+
getNetworksByType as bW,
|
|
61871
|
+
listNetworks as bX,
|
|
61872
|
+
isNetworkSupported as bY,
|
|
61873
|
+
getTxUrl as bZ,
|
|
61874
|
+
getAddressUrl as b_,
|
|
59812
61875
|
RelationshipDiscovery as ba,
|
|
59813
61876
|
TaskBreakdown as bb,
|
|
59814
61877
|
H3AI as bc,
|
|
@@ -59860,4 +61923,4 @@ export {
|
|
|
59860
61923
|
concatBytes as y,
|
|
59861
61924
|
dataLength as z
|
|
59862
61925
|
};
|
|
59863
|
-
//# sourceMappingURL=index-
|
|
61926
|
+
//# sourceMappingURL=index-C3Cag0SV.js.map
|