@weaveprotocol/cli 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -1
- package/dist/weave.mjs +847 -108
- package/package.json +11 -6
package/dist/weave.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import path6 from "node:path";
|
|
|
6
6
|
import { createInterface as createInterface2 } from "node:readline";
|
|
7
7
|
import { parseArgs } from "node:util";
|
|
8
8
|
|
|
9
|
-
// ../src/utils/encoding.ts
|
|
9
|
+
// ../core/src/utils/encoding.ts
|
|
10
10
|
var encoder = new TextEncoder();
|
|
11
11
|
var decoder = new TextDecoder();
|
|
12
12
|
function utf8Encode(str2) {
|
|
@@ -46,7 +46,7 @@ function bytesToHex(bytes) {
|
|
|
46
46
|
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
//
|
|
49
|
+
// ../../node_modules/@noble/hashes/_u64.js
|
|
50
50
|
var fromNumH = (n) => n / 2 ** 32 | 0;
|
|
51
51
|
var fromNumL = (n) => n >>> 0;
|
|
52
52
|
function setU64FromNum(view, byteOffset, n, isLE) {
|
|
@@ -56,7 +56,7 @@ function setU64FromNum(view, byteOffset, n, isLE) {
|
|
|
56
56
|
view.setUint32(byteOffset + 4, isLE ? h : l, isLE);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
//
|
|
59
|
+
// ../../node_modules/@noble/hashes/utils.js
|
|
60
60
|
function isBytes(a) {
|
|
61
61
|
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
|
|
62
62
|
}
|
|
@@ -221,7 +221,7 @@ var oidNist = (suffix) => ({
|
|
|
221
221
|
oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
-
//
|
|
224
|
+
// ../../node_modules/@noble/hashes/_md.js
|
|
225
225
|
function Chi(a, b, c) {
|
|
226
226
|
return a & b ^ ~a & c;
|
|
227
227
|
}
|
|
@@ -334,7 +334,7 @@ var SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
|
334
334
|
1541459225
|
|
335
335
|
]);
|
|
336
336
|
|
|
337
|
-
//
|
|
337
|
+
// ../../node_modules/@noble/hashes/sha2.js
|
|
338
338
|
var SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
339
339
|
1116352408,
|
|
340
340
|
1899447441,
|
|
@@ -499,7 +499,7 @@ var sha256 = /* @__PURE__ */ createHasher(
|
|
|
499
499
|
/* @__PURE__ */ oidNist(1)
|
|
500
500
|
);
|
|
501
501
|
|
|
502
|
-
//
|
|
502
|
+
// ../../node_modules/@noble/curves/utils.js
|
|
503
503
|
function aarray(item, title, inner = () => {
|
|
504
504
|
}) {
|
|
505
505
|
if (!Array.isArray(item))
|
|
@@ -681,7 +681,7 @@ function validateObject(object, fields = {}, optFields = {}, title = "object") {
|
|
|
681
681
|
iter(optFields, true);
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
-
//
|
|
684
|
+
// ../../node_modules/@noble/curves/abstract/modular.js
|
|
685
685
|
var _0n2 = /* @__PURE__ */ BigInt(0);
|
|
686
686
|
var _1n2 = /* @__PURE__ */ BigInt(1);
|
|
687
687
|
var _2n = /* @__PURE__ */ BigInt(2);
|
|
@@ -1131,7 +1131,7 @@ function mapHashToField(key3, fieldOrder, isLE = false) {
|
|
|
1131
1131
|
return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
|
|
1132
1132
|
}
|
|
1133
1133
|
|
|
1134
|
-
//
|
|
1134
|
+
// ../../node_modules/@noble/curves/abstract/curve.js
|
|
1135
1135
|
var _0n3 = /* @__PURE__ */ BigInt(0);
|
|
1136
1136
|
var _1n3 = /* @__PURE__ */ BigInt(1);
|
|
1137
1137
|
var _4n2 = /* @__PURE__ */ BigInt(4);
|
|
@@ -1518,7 +1518,7 @@ function createKeygen(randomSecretKey, getPublicKey) {
|
|
|
1518
1518
|
};
|
|
1519
1519
|
}
|
|
1520
1520
|
|
|
1521
|
-
//
|
|
1521
|
+
// ../../node_modules/@noble/hashes/hmac.js
|
|
1522
1522
|
var _HMAC = class {
|
|
1523
1523
|
oHash;
|
|
1524
1524
|
iHash;
|
|
@@ -1595,7 +1595,7 @@ var hmac = /* @__PURE__ */ (() => {
|
|
|
1595
1595
|
return hmac_;
|
|
1596
1596
|
})();
|
|
1597
1597
|
|
|
1598
|
-
//
|
|
1598
|
+
// ../../node_modules/@noble/curves/abstract/der.js
|
|
1599
1599
|
var _0n4 = /* @__PURE__ */ BigInt(0);
|
|
1600
1600
|
var DERErr = class extends Error {
|
|
1601
1601
|
constructor(m = "") {
|
|
@@ -1722,7 +1722,7 @@ var DER = /* @__PURE__ */ (() => {
|
|
|
1722
1722
|
return Object.freeze(_DER);
|
|
1723
1723
|
})();
|
|
1724
1724
|
|
|
1725
|
-
//
|
|
1725
|
+
// ../../node_modules/@noble/curves/abstract/weierstrass.js
|
|
1726
1726
|
var divNearest = (num, den) => (num + (num >= 0 ? den : -den) / _2n2) / den;
|
|
1727
1727
|
function _splitEndoScalar(k, basis, n) {
|
|
1728
1728
|
aInRange("scalar", k, _0n5, n);
|
|
@@ -2499,7 +2499,7 @@ function ecdsa(Point, hash2, ecdsaOpts = {}) {
|
|
|
2499
2499
|
const sig = drbg(seed, k2sig);
|
|
2500
2500
|
return sig.toBytes(opts2.format);
|
|
2501
2501
|
}
|
|
2502
|
-
function
|
|
2502
|
+
function verify2(signature, message, publicKey, opts2 = {}) {
|
|
2503
2503
|
const { lowS, prehash, format: format2 } = validateSigOpts(opts2, defaultSigOpts);
|
|
2504
2504
|
publicKey = abytes2(publicKey, void 0, "publicKey");
|
|
2505
2505
|
message = validateMsgAndHash(message, prehash);
|
|
@@ -2547,14 +2547,14 @@ function ecdsa(Point, hash2, ecdsaOpts = {}) {
|
|
|
2547
2547
|
lengths,
|
|
2548
2548
|
Point,
|
|
2549
2549
|
sign,
|
|
2550
|
-
verify,
|
|
2550
|
+
verify: verify2,
|
|
2551
2551
|
recoverPublicKey,
|
|
2552
2552
|
Signature,
|
|
2553
2553
|
hash: hash_
|
|
2554
2554
|
});
|
|
2555
2555
|
}
|
|
2556
2556
|
|
|
2557
|
-
//
|
|
2557
|
+
// ../../node_modules/@noble/curves/nist.js
|
|
2558
2558
|
var p256_CURVE = /* @__PURE__ */ (() => ({
|
|
2559
2559
|
p: BigInt("0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"),
|
|
2560
2560
|
n: BigInt("0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"),
|
|
@@ -2567,7 +2567,7 @@ var p256_CURVE = /* @__PURE__ */ (() => ({
|
|
|
2567
2567
|
var p256_Point = /* @__PURE__ */ weierstrass(p256_CURVE);
|
|
2568
2568
|
var p256 = /* @__PURE__ */ ecdsa(p256_Point, sha256);
|
|
2569
2569
|
|
|
2570
|
-
// ../src/identity/crypto-p256.ts
|
|
2570
|
+
// ../core/src/identity/crypto-p256.ts
|
|
2571
2571
|
var P256_SEED_BYTES = 48;
|
|
2572
2572
|
var P256_KEY_INFO = new TextEncoder().encode("weave/p256-identity-key/v1");
|
|
2573
2573
|
async function hkdf(ikm, info, length) {
|
|
@@ -2679,12 +2679,12 @@ function createP256Provider() {
|
|
|
2679
2679
|
});
|
|
2680
2680
|
}
|
|
2681
2681
|
|
|
2682
|
-
// ../src/utils/errors.ts
|
|
2682
|
+
// ../core/src/utils/errors.ts
|
|
2683
2683
|
function protocolError(code, message, hint) {
|
|
2684
2684
|
return Object.assign(new Error(message), { code, ...hint ? { hint } : {} });
|
|
2685
2685
|
}
|
|
2686
2686
|
|
|
2687
|
-
// ../src/identity/webauthn.ts
|
|
2687
|
+
// ../core/src/identity/webauthn.ts
|
|
2688
2688
|
var PRF_SALT = new TextEncoder().encode("weave-protocol-key-v1");
|
|
2689
2689
|
async function registerPasskey(options) {
|
|
2690
2690
|
if (!globalThis.navigator?.credentials) {
|
|
@@ -2780,10 +2780,10 @@ async function authenticatePasskey(credentialId, options) {
|
|
|
2780
2780
|
});
|
|
2781
2781
|
}
|
|
2782
2782
|
|
|
2783
|
-
// ../src/identity/passkey-diagnostics.ts
|
|
2783
|
+
// ../core/src/identity/passkey-diagnostics.ts
|
|
2784
2784
|
var PRF_SALT2 = new TextEncoder().encode("weave-protocol-key-v1");
|
|
2785
2785
|
|
|
2786
|
-
// ../src/identity/keys.ts
|
|
2786
|
+
// ../core/src/identity/keys.ts
|
|
2787
2787
|
async function deriveKeyPair(seed, provider) {
|
|
2788
2788
|
const keyPair = await provider.deriveKeyPairFromSeed(seed);
|
|
2789
2789
|
const publicKeyBytes = await provider.exportPublicKey(keyPair.publicKey);
|
|
@@ -2815,7 +2815,7 @@ async function deriveKeyFromPassword(password, salt, provider) {
|
|
|
2815
2815
|
return deriveKeyPair(new Uint8Array(derivedBits), provider);
|
|
2816
2816
|
}
|
|
2817
2817
|
|
|
2818
|
-
// ../src/identity/recovery-code.ts
|
|
2818
|
+
// ../core/src/identity/recovery-code.ts
|
|
2819
2819
|
var ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
2820
2820
|
var SUBSTITUTIONS = { O: "0", I: "1", L: "1", U: "V" };
|
|
2821
2821
|
var SEED_BYTES = 16;
|
|
@@ -2876,7 +2876,7 @@ function encodeBase32(bytes) {
|
|
|
2876
2876
|
return out;
|
|
2877
2877
|
}
|
|
2878
2878
|
|
|
2879
|
-
//
|
|
2879
|
+
// ../../node_modules/@scure/base/index.js
|
|
2880
2880
|
var freeze = (fn) => Object.freeze(fn());
|
|
2881
2881
|
function isBytes3(a) {
|
|
2882
2882
|
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
|
|
@@ -3082,7 +3082,7 @@ var radix58 = /* @__PURE__ */ radixBaseN(58, B58_GROUP);
|
|
|
3082
3082
|
var genBase58 = (abc) => genBaseN(radix58, abc);
|
|
3083
3083
|
var base58 = /* @__PURE__ */ freeze(() => genBase58("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"));
|
|
3084
3084
|
|
|
3085
|
-
// ../src/identity/did.ts
|
|
3085
|
+
// ../core/src/identity/did.ts
|
|
3086
3086
|
var P256_MULTICODEC = new Uint8Array([128, 36]);
|
|
3087
3087
|
function publicKeyToDid(publicKeyBytes, multicodecPrefix) {
|
|
3088
3088
|
const prefixedKey = concatBytes(multicodecPrefix, publicKeyBytes);
|
|
@@ -3103,7 +3103,7 @@ function didToPublicKey(did) {
|
|
|
3103
3103
|
return { multicodecPrefix, publicKeyBytes };
|
|
3104
3104
|
}
|
|
3105
3105
|
|
|
3106
|
-
// ../src/identity/identity-manager.ts
|
|
3106
|
+
// ../core/src/identity/identity-manager.ts
|
|
3107
3107
|
function prfUnsupported() {
|
|
3108
3108
|
return protocolError(
|
|
3109
3109
|
"PRF_UNSUPPORTED",
|
|
@@ -3194,7 +3194,7 @@ function createIdentityManager(config) {
|
|
|
3194
3194
|
});
|
|
3195
3195
|
}
|
|
3196
3196
|
|
|
3197
|
-
// ../src/storage/folder-adapter.ts
|
|
3197
|
+
// ../core/src/storage/folder-adapter.ts
|
|
3198
3198
|
var EXPRESSION_SUFFIX = ".json";
|
|
3199
3199
|
function encodeKey(key3) {
|
|
3200
3200
|
let out = "";
|
|
@@ -3365,7 +3365,7 @@ async function createFolderAdapter(root, path7) {
|
|
|
3365
3365
|
});
|
|
3366
3366
|
}
|
|
3367
3367
|
|
|
3368
|
-
// ../src/identity/folder-account.ts
|
|
3368
|
+
// ../core/src/identity/folder-account.ts
|
|
3369
3369
|
function createVault(params) {
|
|
3370
3370
|
return {
|
|
3371
3371
|
version: 2,
|
|
@@ -3376,7 +3376,7 @@ function createVault(params) {
|
|
|
3376
3376
|
};
|
|
3377
3377
|
}
|
|
3378
3378
|
|
|
3379
|
-
// ../src/identity/account-store.ts
|
|
3379
|
+
// ../core/src/identity/account-store.ts
|
|
3380
3380
|
var LIST_FILE = "accounts.json";
|
|
3381
3381
|
var ACCOUNTS_DIR = "accounts";
|
|
3382
3382
|
var VAULT_FILE = "account.json";
|
|
@@ -3491,7 +3491,7 @@ function createFolderAccountStore(dir) {
|
|
|
3491
3491
|
});
|
|
3492
3492
|
}
|
|
3493
3493
|
|
|
3494
|
-
// ../src/identity/account-vault.ts
|
|
3494
|
+
// ../core/src/identity/account-vault.ts
|
|
3495
3495
|
var IV_BYTES = 12;
|
|
3496
3496
|
var SALT_BYTES = 16;
|
|
3497
3497
|
var PASSPHRASE_ITERATIONS = 6e5;
|
|
@@ -3601,15 +3601,18 @@ async function deriveVaultKey(seed) {
|
|
|
3601
3601
|
["encrypt", "decrypt"]
|
|
3602
3602
|
);
|
|
3603
3603
|
}
|
|
3604
|
+
var CLI_PASSPHRASE_LABEL = "CLI passphrase";
|
|
3604
3605
|
function withWrap(vault, wrap) {
|
|
3605
3606
|
const superseded = (existing) => existing.kind === "device" && wrap.kind === "device" && existing.rpId === wrap.rpId;
|
|
3606
3607
|
return { ...vault, wraps: [...vault.wraps.filter((existing) => !superseded(existing)), wrap] };
|
|
3607
3608
|
}
|
|
3608
3609
|
|
|
3609
|
-
// ../src/identity/contact-key.ts
|
|
3610
|
+
// ../core/src/identity/contact-key.ts
|
|
3610
3611
|
var CONTACT_KEY_INFO = "weave/p256-contact-key/v1";
|
|
3611
3612
|
var SEAL_INFO = "weave/contact-seal/v1";
|
|
3612
3613
|
var MEMBER_KEY_INFO = "weave/p256-member-key/v1";
|
|
3614
|
+
var DOOR_KEY_INFO = "weave/p256-door-key/v1";
|
|
3615
|
+
var DOOR_SIGN_KEY_INFO = "weave/p256-door-sign-key/v1";
|
|
3613
3616
|
var P256_SEED_BYTES2 = 48;
|
|
3614
3617
|
var POINT_BYTES = 65;
|
|
3615
3618
|
var IV_BYTES2 = 12;
|
|
@@ -3628,6 +3631,39 @@ async function deriveContactKeyBytes(seed) {
|
|
|
3628
3631
|
async function deriveMemberKeyBytes(accountKey, spaceId) {
|
|
3629
3632
|
return p256.utils.randomSecretKey(await hkdf2(accountKey, `${MEMBER_KEY_INFO}|${spaceId}`, P256_SEED_BYTES2));
|
|
3630
3633
|
}
|
|
3634
|
+
async function deriveDoorKeyBytes(contactKey, doorId) {
|
|
3635
|
+
return p256.utils.randomSecretKey(await hkdf2(contactKey, `${DOOR_KEY_INFO}|${doorId}`, P256_SEED_BYTES2));
|
|
3636
|
+
}
|
|
3637
|
+
async function deriveDoorSignKeyBytes(contactKey, doorId) {
|
|
3638
|
+
return p256.utils.randomSecretKey(await hkdf2(contactKey, `${DOOR_SIGN_KEY_INFO}|${doorId}`, P256_SEED_BYTES2));
|
|
3639
|
+
}
|
|
3640
|
+
async function signWithScalar(secret, data) {
|
|
3641
|
+
const point = p256.getPublicKey(secret, false);
|
|
3642
|
+
const key3 = await globalThis.crypto.subtle.importKey(
|
|
3643
|
+
"jwk",
|
|
3644
|
+
{
|
|
3645
|
+
kty: "EC",
|
|
3646
|
+
crv: "P-256",
|
|
3647
|
+
x: base64UrlEncode(point.subarray(1, 33)),
|
|
3648
|
+
y: base64UrlEncode(point.subarray(33, 65)),
|
|
3649
|
+
d: base64UrlEncode(secret),
|
|
3650
|
+
ext: false
|
|
3651
|
+
},
|
|
3652
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
3653
|
+
false,
|
|
3654
|
+
["sign"]
|
|
3655
|
+
);
|
|
3656
|
+
return base64UrlEncode(new Uint8Array(await globalThis.crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, key3, data)));
|
|
3657
|
+
}
|
|
3658
|
+
async function verifyWithPoint(publicKey, data, signature) {
|
|
3659
|
+
try {
|
|
3660
|
+
const point = p256.Point.fromBytes(base64UrlDecode(publicKey)).toBytes(false);
|
|
3661
|
+
const key3 = await globalThis.crypto.subtle.importKey("raw", point, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
|
|
3662
|
+
return await globalThis.crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, key3, base64UrlDecode(signature), data);
|
|
3663
|
+
} catch {
|
|
3664
|
+
return false;
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3631
3667
|
function contactPublicKey(secret) {
|
|
3632
3668
|
return base64UrlEncode(p256.getPublicKey(secret, true));
|
|
3633
3669
|
}
|
|
@@ -3714,7 +3750,7 @@ async function openSealed(privateKey, sealed, context) {
|
|
|
3714
3750
|
}
|
|
3715
3751
|
}
|
|
3716
3752
|
|
|
3717
|
-
// ../src/utils/hash.ts
|
|
3753
|
+
// ../core/src/utils/hash.ts
|
|
3718
3754
|
async function sha2562(data) {
|
|
3719
3755
|
const buffer = await globalThis.crypto.subtle.digest("SHA-256", data);
|
|
3720
3756
|
return new Uint8Array(buffer);
|
|
@@ -3765,7 +3801,7 @@ async function cidFromBytes(data) {
|
|
|
3765
3801
|
return "b" + base32Encode(hashBytes);
|
|
3766
3802
|
}
|
|
3767
3803
|
|
|
3768
|
-
// ../src/records/links.ts
|
|
3804
|
+
// ../core/src/records/links.ts
|
|
3769
3805
|
var LINK_REL_PATTERN = /^[a-z][a-zA-Z0-9]{0,63}$/;
|
|
3770
3806
|
var MAX_LINKS = 32;
|
|
3771
3807
|
function checkLinks(links2) {
|
|
@@ -3783,7 +3819,7 @@ function checkLinks(links2) {
|
|
|
3783
3819
|
return null;
|
|
3784
3820
|
}
|
|
3785
3821
|
|
|
3786
|
-
// ../src/records/version.ts
|
|
3822
|
+
// ../core/src/records/version.ts
|
|
3787
3823
|
var MAX_SEEN = 64;
|
|
3788
3824
|
var RECORD_KEY_PATTERN = /^[a-z0-9:._-]{1,128}$/;
|
|
3789
3825
|
function newRecordKey() {
|
|
@@ -3824,7 +3860,7 @@ function checkVersionShape(expression) {
|
|
|
3824
3860
|
return null;
|
|
3825
3861
|
}
|
|
3826
3862
|
|
|
3827
|
-
// ../src/schema/expression.ts
|
|
3863
|
+
// ../core/src/schema/expression.ts
|
|
3828
3864
|
function canonicalize(obj) {
|
|
3829
3865
|
if (obj === null || typeof obj !== "object") {
|
|
3830
3866
|
return JSON.stringify(obj) ?? "null";
|
|
@@ -3869,7 +3905,7 @@ async function getExpressionId(expr) {
|
|
|
3869
3905
|
return await cidFromBytes(bytes);
|
|
3870
3906
|
}
|
|
3871
3907
|
|
|
3872
|
-
// ../src/identity/ucan.ts
|
|
3908
|
+
// ../core/src/identity/ucan.ts
|
|
3873
3909
|
var UCAN_CLOCK_SKEW_SECONDS = 300;
|
|
3874
3910
|
async function issueUCAN(options, provider) {
|
|
3875
3911
|
const header = { alg: "ES256", typ: "JWT", ucv: "0.10.0" };
|
|
@@ -4060,7 +4096,7 @@ async function resolveDelegationRoot(encoded, resolveProof, provider, options =
|
|
|
4060
4096
|
return { valid: false, rootDid: null, capabilities: [], audience: null, reason: `Delegation chain deeper than ${MAX_CHAIN_DEPTH} links` };
|
|
4061
4097
|
}
|
|
4062
4098
|
|
|
4063
|
-
// ../src/identity/root-signer.ts
|
|
4099
|
+
// ../core/src/identity/root-signer.ts
|
|
4064
4100
|
function createLocalRootSigner(identity, provider) {
|
|
4065
4101
|
return Object.freeze({
|
|
4066
4102
|
did: identity.did,
|
|
@@ -4080,7 +4116,7 @@ function createLocalRootSigner(identity, provider) {
|
|
|
4080
4116
|
});
|
|
4081
4117
|
}
|
|
4082
4118
|
|
|
4083
|
-
// ../src/identity/pairing.ts
|
|
4119
|
+
// ../core/src/identity/pairing.ts
|
|
4084
4120
|
var ROOM_PREFIX = utf8Encode("weave-pairing-room-v1");
|
|
4085
4121
|
var PAIRING_KEY_INFO = utf8Encode("weave-pairing-key-v1");
|
|
4086
4122
|
var IV_BYTES3 = 12;
|
|
@@ -4112,7 +4148,7 @@ async function openPairingPayload(sealed, key3) {
|
|
|
4112
4148
|
}
|
|
4113
4149
|
}
|
|
4114
4150
|
|
|
4115
|
-
// ../src/identity/agent-note.ts
|
|
4151
|
+
// ../core/src/identity/agent-note.ts
|
|
4116
4152
|
var AGENT_FACT = Object.freeze({ weave: "agent" });
|
|
4117
4153
|
function isAgentNote(encoded) {
|
|
4118
4154
|
if (!encoded) return false;
|
|
@@ -4124,7 +4160,7 @@ function isAgentNote(encoded) {
|
|
|
4124
4160
|
}
|
|
4125
4161
|
}
|
|
4126
4162
|
|
|
4127
|
-
// ../src/schema/schema-engine.ts
|
|
4163
|
+
// ../core/src/schema/schema-engine.ts
|
|
4128
4164
|
function createSchemaEngine() {
|
|
4129
4165
|
const collections = /* @__PURE__ */ new Map();
|
|
4130
4166
|
return Object.freeze({
|
|
@@ -4157,7 +4193,7 @@ function createSchemaEngine() {
|
|
|
4157
4193
|
});
|
|
4158
4194
|
}
|
|
4159
4195
|
|
|
4160
|
-
// ../src/schema/signer.ts
|
|
4196
|
+
// ../core/src/schema/signer.ts
|
|
4161
4197
|
function createSigner(provider) {
|
|
4162
4198
|
return Object.freeze({
|
|
4163
4199
|
async sign(payload, privateKey) {
|
|
@@ -4183,7 +4219,7 @@ function createSigner(provider) {
|
|
|
4183
4219
|
});
|
|
4184
4220
|
}
|
|
4185
4221
|
|
|
4186
|
-
// ../src/sync/negentropy.ts
|
|
4222
|
+
// ../core/src/sync/negentropy.ts
|
|
4187
4223
|
var NEGENTROPY_VERSION = 97;
|
|
4188
4224
|
var ID_SIZE = 32;
|
|
4189
4225
|
var FINGERPRINT_SIZE = 16;
|
|
@@ -4473,7 +4509,7 @@ function createReconciler(set, options) {
|
|
|
4473
4509
|
}
|
|
4474
4510
|
var key = (id) => String.fromCharCode(...id);
|
|
4475
4511
|
|
|
4476
|
-
// ../src/storage/storage-provider.ts
|
|
4512
|
+
// ../core/src/storage/storage-provider.ts
|
|
4477
4513
|
var CURRENT_PREFIX = "r/";
|
|
4478
4514
|
var GENESIS_PREFIX = "g/";
|
|
4479
4515
|
var HISTORY_PREFIX = "h/";
|
|
@@ -4698,7 +4734,7 @@ function createStorageProvider(adapter) {
|
|
|
4698
4734
|
});
|
|
4699
4735
|
}
|
|
4700
4736
|
|
|
4701
|
-
// ../src/storage/encrypted-adapter.ts
|
|
4737
|
+
// ../core/src/storage/encrypted-adapter.ts
|
|
4702
4738
|
var MAGIC = new Uint8Array([119, 101, 97, 118, 101, 101, 2, 0]);
|
|
4703
4739
|
var IV_BYTES4 = 12;
|
|
4704
4740
|
var DEFAULT_ENCRYPTED_PREFIXES = ["space:", "spacekey:", "spaceinvite:", "spacerole:"];
|
|
@@ -4762,7 +4798,7 @@ function createEncryptedAdapter(inner, key3, options) {
|
|
|
4762
4798
|
});
|
|
4763
4799
|
}
|
|
4764
4800
|
|
|
4765
|
-
// ../src/storage/folder-reconcile.ts
|
|
4801
|
+
// ../core/src/storage/folder-reconcile.ts
|
|
4766
4802
|
async function reconcileFolder(storage, adapter, accept = async () => true) {
|
|
4767
4803
|
const disk = await adapter.reload();
|
|
4768
4804
|
if (disk.added.length > 0 || disk.removed.length > 0) storage.invalidate();
|
|
@@ -4791,7 +4827,7 @@ async function reconcileFolder(storage, adapter, accept = async () => true) {
|
|
|
4791
4827
|
};
|
|
4792
4828
|
}
|
|
4793
4829
|
|
|
4794
|
-
// ../src/privacy/space-encryption.ts
|
|
4830
|
+
// ../core/src/privacy/space-encryption.ts
|
|
4795
4831
|
async function generateSpaceKey() {
|
|
4796
4832
|
const key3 = await globalThis.crypto.subtle.generateKey(
|
|
4797
4833
|
{ name: "AES-GCM", length: 256 },
|
|
@@ -4877,7 +4913,7 @@ async function openWith(spaceKey, sealed, context) {
|
|
|
4877
4913
|
}
|
|
4878
4914
|
}
|
|
4879
4915
|
|
|
4880
|
-
// ../src/space/roles.ts
|
|
4916
|
+
// ../core/src/space/roles.ts
|
|
4881
4917
|
var MANAGE = "manage";
|
|
4882
4918
|
var INVITE = "invite";
|
|
4883
4919
|
var DEFINE = "define";
|
|
@@ -5296,7 +5332,7 @@ function replayAccess(genesis, events) {
|
|
|
5296
5332
|
}
|
|
5297
5333
|
}
|
|
5298
5334
|
|
|
5299
|
-
// ../src/space/space-access.ts
|
|
5335
|
+
// ../core/src/space/space-access.ts
|
|
5300
5336
|
var READ_INFO = "weave/space-read/v1";
|
|
5301
5337
|
var INVITE_INFO = "weave/space-invite/v1";
|
|
5302
5338
|
function generateInviteSecret() {
|
|
@@ -5400,7 +5436,7 @@ async function verifyInvite(spaceId, did, inviteDid, signature, provider) {
|
|
|
5400
5436
|
}
|
|
5401
5437
|
}
|
|
5402
5438
|
|
|
5403
|
-
// ../src/space/presets.ts
|
|
5439
|
+
// ../core/src/space/presets.ts
|
|
5404
5440
|
var solo = Object.freeze({
|
|
5405
5441
|
roles: Object.freeze([{ name: "owner", title: "Owner", rank: 100, permissions: ["*"] }]),
|
|
5406
5442
|
creatorRole: "owner"
|
|
@@ -5422,7 +5458,7 @@ var community = Object.freeze({
|
|
|
5422
5458
|
});
|
|
5423
5459
|
var rolePresets = Object.freeze({ solo, team, community });
|
|
5424
5460
|
|
|
5425
|
-
// ../src/space/space-manager.ts
|
|
5461
|
+
// ../core/src/space/space-manager.ts
|
|
5426
5462
|
var SPACE_PREFIX = "space:";
|
|
5427
5463
|
var KEY_PREFIX = "spacekey:";
|
|
5428
5464
|
var INVITE_PREFIX = "spaceinvite:";
|
|
@@ -5600,7 +5636,7 @@ function parseSpaceInvite(invite) {
|
|
|
5600
5636
|
}
|
|
5601
5637
|
}
|
|
5602
5638
|
|
|
5603
|
-
// ../src/space/account-registry.ts
|
|
5639
|
+
// ../core/src/space/account-registry.ts
|
|
5604
5640
|
var MEMBERSHIP_COLLECTION = "sys.joined";
|
|
5605
5641
|
var PROFILE_COLLECTION = "sys.profile";
|
|
5606
5642
|
var PROFILE_KEY = "profile";
|
|
@@ -5652,7 +5688,7 @@ async function deriveAccountSpace(accountKey, owner, label, name, provider) {
|
|
|
5652
5688
|
return { space: space2, key: key3, invite: null, role: solo.creatorRole };
|
|
5653
5689
|
}
|
|
5654
5690
|
|
|
5655
|
-
//
|
|
5691
|
+
// ../../node_modules/@cfworker/json-schema/dist/esm/deep-compare-strict.js
|
|
5656
5692
|
function deepCompareStrict(a, b) {
|
|
5657
5693
|
const typeofa = typeof a;
|
|
5658
5694
|
if (typeofa !== typeof b) {
|
|
@@ -5693,7 +5729,7 @@ function deepCompareStrict(a, b) {
|
|
|
5693
5729
|
return a === b;
|
|
5694
5730
|
}
|
|
5695
5731
|
|
|
5696
|
-
//
|
|
5732
|
+
// ../../node_modules/@cfworker/json-schema/dist/esm/pointer.js
|
|
5697
5733
|
function encodePointer(p) {
|
|
5698
5734
|
return encodeURI(escapePointer(p));
|
|
5699
5735
|
}
|
|
@@ -5701,7 +5737,7 @@ function escapePointer(p) {
|
|
|
5701
5737
|
return p.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
5702
5738
|
}
|
|
5703
5739
|
|
|
5704
|
-
//
|
|
5740
|
+
// ../../node_modules/@cfworker/json-schema/dist/esm/dereference.js
|
|
5705
5741
|
var schemaArrayKeyword = {
|
|
5706
5742
|
prefixItems: true,
|
|
5707
5743
|
items: true,
|
|
@@ -5822,7 +5858,7 @@ function dereference(schema, lookup = /* @__PURE__ */ Object.create(null), baseU
|
|
|
5822
5858
|
return lookup;
|
|
5823
5859
|
}
|
|
5824
5860
|
|
|
5825
|
-
//
|
|
5861
|
+
// ../../node_modules/@cfworker/json-schema/dist/esm/format.js
|
|
5826
5862
|
var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
5827
5863
|
var DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
5828
5864
|
var TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i;
|
|
@@ -5915,7 +5951,7 @@ function regex(str2) {
|
|
|
5915
5951
|
}
|
|
5916
5952
|
}
|
|
5917
5953
|
|
|
5918
|
-
//
|
|
5954
|
+
// ../../node_modules/@cfworker/json-schema/dist/esm/types.js
|
|
5919
5955
|
var OutputFormat;
|
|
5920
5956
|
(function(OutputFormat2) {
|
|
5921
5957
|
OutputFormat2[OutputFormat2["Flag"] = 1] = "Flag";
|
|
@@ -5923,7 +5959,7 @@ var OutputFormat;
|
|
|
5923
5959
|
OutputFormat2[OutputFormat2["Detailed"] = 4] = "Detailed";
|
|
5924
5960
|
})(OutputFormat || (OutputFormat = {}));
|
|
5925
5961
|
|
|
5926
|
-
//
|
|
5962
|
+
// ../../node_modules/@cfworker/json-schema/dist/esm/ucs2-length.js
|
|
5927
5963
|
function ucs2length(s) {
|
|
5928
5964
|
let result = 0;
|
|
5929
5965
|
let length = s.length;
|
|
@@ -5942,7 +5978,7 @@ function ucs2length(s) {
|
|
|
5942
5978
|
return result;
|
|
5943
5979
|
}
|
|
5944
5980
|
|
|
5945
|
-
//
|
|
5981
|
+
// ../../node_modules/@cfworker/json-schema/dist/esm/validate.js
|
|
5946
5982
|
function validate(instance, schema, draft = "2019-09", lookup = dereference(schema), shortCircuit = true, recursiveAnchor = null, instanceLocation = "#", schemaLocation = "#", evaluated = /* @__PURE__ */ Object.create(null)) {
|
|
5947
5983
|
if (schema === true) {
|
|
5948
5984
|
return { valid: true, errors: [] };
|
|
@@ -6700,7 +6736,7 @@ Known schemas:
|
|
|
6700
6736
|
return { valid: errors.length === 0, errors };
|
|
6701
6737
|
}
|
|
6702
6738
|
|
|
6703
|
-
//
|
|
6739
|
+
// ../../node_modules/@cfworker/json-schema/dist/esm/validator.js
|
|
6704
6740
|
var Validator = class {
|
|
6705
6741
|
schema;
|
|
6706
6742
|
draft;
|
|
@@ -6723,7 +6759,7 @@ var Validator = class {
|
|
|
6723
6759
|
}
|
|
6724
6760
|
};
|
|
6725
6761
|
|
|
6726
|
-
// ../src/records/rules.ts
|
|
6762
|
+
// ../core/src/records/rules.ts
|
|
6727
6763
|
var PERMISSION_PATTERN = /^[a-z][a-zA-Z0-9]{0,39}$/;
|
|
6728
6764
|
var permissionName = (collection2, permission) => `${collection2}/${permission}`;
|
|
6729
6765
|
var isWho = (w) => w === "member" || w === "creator" || typeof w === "string" && w.startsWith("can:") && PERMISSION_PATTERN.test(w.slice(4));
|
|
@@ -6793,7 +6829,7 @@ function changedFixedField(fixed, first, next) {
|
|
|
6793
6829
|
return null;
|
|
6794
6830
|
}
|
|
6795
6831
|
|
|
6796
|
-
// ../src/records/topics.ts
|
|
6832
|
+
// ../core/src/records/topics.ts
|
|
6797
6833
|
var MAX_TOPICS = 8;
|
|
6798
6834
|
var MAX_TAGS = 64;
|
|
6799
6835
|
var TAG_BYTES = 16;
|
|
@@ -6845,7 +6881,7 @@ function sameTags(a, b) {
|
|
|
6845
6881
|
return left.length === right.length && left.every((tag, i) => tag === right[i]);
|
|
6846
6882
|
}
|
|
6847
6883
|
|
|
6848
|
-
// ../src/schema/collection-def.ts
|
|
6884
|
+
// ../core/src/schema/collection-def.ts
|
|
6849
6885
|
function toJsonSchema(schema) {
|
|
6850
6886
|
const standard = schema["~standard"];
|
|
6851
6887
|
if (!standard) return schema;
|
|
@@ -7006,7 +7042,7 @@ function validateJsonSchema(schema, value) {
|
|
|
7006
7042
|
}));
|
|
7007
7043
|
}
|
|
7008
7044
|
|
|
7009
|
-
// ../src/utils/events.ts
|
|
7045
|
+
// ../core/src/utils/events.ts
|
|
7010
7046
|
function createEmitter() {
|
|
7011
7047
|
const listeners = /* @__PURE__ */ new Map();
|
|
7012
7048
|
return {
|
|
@@ -7028,7 +7064,7 @@ function createEmitter() {
|
|
|
7028
7064
|
};
|
|
7029
7065
|
}
|
|
7030
7066
|
|
|
7031
|
-
// ../src/network/signaling.ts
|
|
7067
|
+
// ../core/src/network/signaling.ts
|
|
7032
7068
|
var SIGNAL_KINDS = /* @__PURE__ */ new Set(["offer", "answer", "candidate"]);
|
|
7033
7069
|
function iceFrom(payload) {
|
|
7034
7070
|
const { servers, expiresAt } = payload ?? {};
|
|
@@ -7145,7 +7181,7 @@ function createSignalingClient(url, did) {
|
|
|
7145
7181
|
});
|
|
7146
7182
|
}
|
|
7147
7183
|
|
|
7148
|
-
// ../src/network/multi-signaling.ts
|
|
7184
|
+
// ../core/src/network/multi-signaling.ts
|
|
7149
7185
|
function createMultiSignalingClient(urls, did) {
|
|
7150
7186
|
if (urls.length === 0) {
|
|
7151
7187
|
throw new Error("At least one relay is needed to introduce peers.");
|
|
@@ -7279,7 +7315,7 @@ function createMultiSignalingClient(urls, did) {
|
|
|
7279
7315
|
});
|
|
7280
7316
|
}
|
|
7281
7317
|
|
|
7282
|
-
// ../src/network/introductions.ts
|
|
7318
|
+
// ../core/src/network/introductions.ts
|
|
7283
7319
|
var PEERS_MESSAGE = "__peers";
|
|
7284
7320
|
var SIGNAL_MESSAGE = "__signal";
|
|
7285
7321
|
var AUTH_HELLO_MESSAGE = "__auth-hello";
|
|
@@ -7318,7 +7354,7 @@ function signalId() {
|
|
|
7318
7354
|
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
7319
7355
|
}
|
|
7320
7356
|
|
|
7321
|
-
// ../src/network/rtc-transport.ts
|
|
7357
|
+
// ../core/src/network/rtc-transport.ts
|
|
7322
7358
|
function fingerprintOf2(description) {
|
|
7323
7359
|
const match = description?.sdp?.match(/^a=fingerprint:\s*(\S+)\s+(\S+)/im);
|
|
7324
7360
|
return match ? `${match[1].toLowerCase()} ${match[2].toUpperCase()}` : null;
|
|
@@ -7454,7 +7490,7 @@ function createRTCTransport(config) {
|
|
|
7454
7490
|
});
|
|
7455
7491
|
}
|
|
7456
7492
|
|
|
7457
|
-
// ../src/network/peer-auth.ts
|
|
7493
|
+
// ../core/src/network/peer-auth.ts
|
|
7458
7494
|
function readAccessOf(read) {
|
|
7459
7495
|
if (!read) return null;
|
|
7460
7496
|
if ("current" in read) return read;
|
|
@@ -7540,7 +7576,7 @@ function createMeshAuth(spaceId, session, read, provider) {
|
|
|
7540
7576
|
});
|
|
7541
7577
|
}
|
|
7542
7578
|
|
|
7543
|
-
// ../src/network/ws-transport.ts
|
|
7579
|
+
// ../core/src/network/ws-transport.ts
|
|
7544
7580
|
function parseFrame(data, type) {
|
|
7545
7581
|
if (typeof data !== "string") return null;
|
|
7546
7582
|
try {
|
|
@@ -7686,7 +7722,7 @@ function createWebSocketTransport(config) {
|
|
|
7686
7722
|
});
|
|
7687
7723
|
}
|
|
7688
7724
|
|
|
7689
|
-
// ../src/network/mesh.ts
|
|
7725
|
+
// ../core/src/network/mesh.ts
|
|
7690
7726
|
var ICE_REFRESH_MS = 10 * 6e4;
|
|
7691
7727
|
var ICE_WAIT_MS = 1500;
|
|
7692
7728
|
function createMesh(config) {
|
|
@@ -7967,7 +8003,7 @@ function createMesh(config) {
|
|
|
7967
8003
|
};
|
|
7968
8004
|
}
|
|
7969
8005
|
|
|
7970
|
-
// ../src/network/network-manager.ts
|
|
8006
|
+
// ../core/src/network/network-manager.ts
|
|
7971
8007
|
function createNetworkManager(config) {
|
|
7972
8008
|
const transport = config.createTransport();
|
|
7973
8009
|
const peers = /* @__PURE__ */ new Map();
|
|
@@ -8013,7 +8049,7 @@ function createNetworkManager(config) {
|
|
|
8013
8049
|
});
|
|
8014
8050
|
}
|
|
8015
8051
|
|
|
8016
|
-
// ../src/sync/sync-messages.ts
|
|
8052
|
+
// ../core/src/sync/sync-messages.ts
|
|
8017
8053
|
var SYNC_PROTOCOL_VERSION = 4;
|
|
8018
8054
|
function parseSyncMessage(value) {
|
|
8019
8055
|
const message = value;
|
|
@@ -8021,7 +8057,7 @@ function parseSyncMessage(value) {
|
|
|
8021
8057
|
return message;
|
|
8022
8058
|
}
|
|
8023
8059
|
|
|
8024
|
-
// ../src/sync/sync-engine.ts
|
|
8060
|
+
// ../core/src/sync/sync-engine.ts
|
|
8025
8061
|
var MAX_WAITING = 1e3;
|
|
8026
8062
|
var MAX_REFUSED = 1e4;
|
|
8027
8063
|
var holdsCollection = (holds2, collection2) => collection2.startsWith("sys.") || holds2 === "all" || holds2.has(collection2);
|
|
@@ -8309,7 +8345,7 @@ ${id}`;
|
|
|
8309
8345
|
};
|
|
8310
8346
|
}
|
|
8311
8347
|
|
|
8312
|
-
// ../src/validation/crypto-gate.ts
|
|
8348
|
+
// ../core/src/validation/crypto-gate.ts
|
|
8313
8349
|
function createCryptoGate(provider) {
|
|
8314
8350
|
return {
|
|
8315
8351
|
async validate(expression, resolvePublicKey) {
|
|
@@ -8336,7 +8372,7 @@ function createCryptoGate(provider) {
|
|
|
8336
8372
|
};
|
|
8337
8373
|
}
|
|
8338
8374
|
|
|
8339
|
-
// ../src/validation/structural-gate.ts
|
|
8375
|
+
// ../core/src/validation/structural-gate.ts
|
|
8340
8376
|
function isEncryptedBody(body) {
|
|
8341
8377
|
if (!body || typeof body !== "object") return false;
|
|
8342
8378
|
const envelope = body;
|
|
@@ -8370,7 +8406,7 @@ function createStructuralGate(schemaEngine, options = {}) {
|
|
|
8370
8406
|
};
|
|
8371
8407
|
}
|
|
8372
8408
|
|
|
8373
|
-
// ../src/validation/stateful-gate.ts
|
|
8409
|
+
// ../core/src/validation/stateful-gate.ts
|
|
8374
8410
|
function createStatefulGate() {
|
|
8375
8411
|
const rules = /* @__PURE__ */ new Map();
|
|
8376
8412
|
return {
|
|
@@ -8416,7 +8452,7 @@ function createStatefulGate() {
|
|
|
8416
8452
|
};
|
|
8417
8453
|
}
|
|
8418
8454
|
|
|
8419
|
-
// ../src/validation/capability-gate.ts
|
|
8455
|
+
// ../core/src/validation/capability-gate.ts
|
|
8420
8456
|
var fail = (reason) => ({ passed: false, gate: "capability", reason });
|
|
8421
8457
|
var MAX_CLOCK_SKEW_SECONDS = 300;
|
|
8422
8458
|
function createCapabilityGate(config) {
|
|
@@ -8459,7 +8495,7 @@ function createCapabilityGate(config) {
|
|
|
8459
8495
|
};
|
|
8460
8496
|
}
|
|
8461
8497
|
|
|
8462
|
-
// ../src/validation/validation-engine.ts
|
|
8498
|
+
// ../core/src/validation/validation-engine.ts
|
|
8463
8499
|
function createValidationEngine(config) {
|
|
8464
8500
|
const { cryptoGate, structuralGate, statefulGate, capabilityGate, resolvePublicKey, getExpression } = config;
|
|
8465
8501
|
return {
|
|
@@ -8485,7 +8521,7 @@ function createValidationEngine(config) {
|
|
|
8485
8521
|
};
|
|
8486
8522
|
}
|
|
8487
8523
|
|
|
8488
|
-
// ../src/query/filter.ts
|
|
8524
|
+
// ../core/src/query/filter.ts
|
|
8489
8525
|
var MAX_INCLUDE_DEPTH = 3;
|
|
8490
8526
|
var META = {
|
|
8491
8527
|
"@key": (r) => r.key,
|
|
@@ -8649,7 +8685,7 @@ function checkQuery(query) {
|
|
|
8649
8685
|
return null;
|
|
8650
8686
|
}
|
|
8651
8687
|
|
|
8652
|
-
// ../src/query/types.ts
|
|
8688
|
+
// ../core/src/query/types.ts
|
|
8653
8689
|
var nameOf = (ref) => typeof ref === "object" && ref !== null && typeof ref.name === "string" ? ref.name : ref;
|
|
8654
8690
|
function plainQuery(query) {
|
|
8655
8691
|
const isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -8663,7 +8699,7 @@ function plainQuery(query) {
|
|
|
8663
8699
|
return { ...query, collection: nameOf(query.collection), ...query.include !== void 0 ? { include: includes(query.include) } : {} };
|
|
8664
8700
|
}
|
|
8665
8701
|
|
|
8666
|
-
// ../src/query/engine.ts
|
|
8702
|
+
// ../core/src/query/engine.ts
|
|
8667
8703
|
function compareValues(a, b) {
|
|
8668
8704
|
if (a === b) return 0;
|
|
8669
8705
|
if (a === void 0 || a === null) return -1;
|
|
@@ -8723,7 +8759,7 @@ async function runQuery(source, given) {
|
|
|
8723
8759
|
return { records, cursor: more ? page[page.length - 1].key : null, complete: true };
|
|
8724
8760
|
}
|
|
8725
8761
|
|
|
8726
|
-
// ../src/storage/segment.ts
|
|
8762
|
+
// ../core/src/storage/segment.ts
|
|
8727
8763
|
var SEGMENT_SUFFIX = ".seg";
|
|
8728
8764
|
function packSegment(versions) {
|
|
8729
8765
|
return utf8Encode(JSON.stringify({ v: 1, versions }));
|
|
@@ -8743,7 +8779,7 @@ async function segmentName(counter, bytes) {
|
|
|
8743
8779
|
}
|
|
8744
8780
|
var versionSize = (version) => utf8Encode(JSON.stringify(version)).length;
|
|
8745
8781
|
|
|
8746
|
-
// ../src/storage/mirror.ts
|
|
8782
|
+
// ../core/src/storage/mirror.ts
|
|
8747
8783
|
var WRITER_KEY = "mirror:writer";
|
|
8748
8784
|
var COUNTER_KEY = "mirror:counter";
|
|
8749
8785
|
var readKey = (name) => `mirror:read:${name}`;
|
|
@@ -8889,7 +8925,7 @@ async function createMirror(config) {
|
|
|
8889
8925
|
});
|
|
8890
8926
|
}
|
|
8891
8927
|
|
|
8892
|
-
// ../src/space/pass.ts
|
|
8928
|
+
// ../core/src/space/pass.ts
|
|
8893
8929
|
var PASS_COLLECTION = "sys.pass";
|
|
8894
8930
|
var CARRY_CLOSED_KEY = "carry:closed";
|
|
8895
8931
|
async function passKey(spaceId) {
|
|
@@ -8920,7 +8956,7 @@ function carriedRecord(pass) {
|
|
|
8920
8956
|
return { space: pass.space, key: null, invite: null, role: null, read: pass.read };
|
|
8921
8957
|
}
|
|
8922
8958
|
|
|
8923
|
-
// ../src/node/space-runtime.ts
|
|
8959
|
+
// ../core/src/node/space-runtime.ts
|
|
8924
8960
|
var MANAGED = /* @__PURE__ */ new Set([MEMBERSHIP_COLLECTION, PROFILE_COLLECTION, CARRIER_COLLECTION, PASS_COLLECTION, BOX_COLLECTION, MEMBER_KEY_COLLECTION, ...ACCESS_COLLECTIONS]);
|
|
8925
8961
|
var IN_THE_CLEAR = /* @__PURE__ */ new Set([ROLE_COLLECTION, MEMBER_COLLECTION, INVITE_COLLECTION, REVOKE_COLLECTION, KEY_COLLECTION, BOX_COLLECTION, MEMBER_KEY_COLLECTION, RELAYS_COLLECTION, KEEPERS_COLLECTION]);
|
|
8926
8962
|
var ACCESS_KEYS = [
|
|
@@ -9863,7 +9899,7 @@ async function openSpaceRuntime(deps) {
|
|
|
9863
9899
|
channel.unref?.();
|
|
9864
9900
|
}
|
|
9865
9901
|
const HOUR = 36e5;
|
|
9866
|
-
const
|
|
9902
|
+
const DAY3 = 24 * HOUR;
|
|
9867
9903
|
const declared = new Set((cache?.collections ?? []).filter((c) => !c.startsWith("sys.")));
|
|
9868
9904
|
let cacheTimer = null;
|
|
9869
9905
|
const saveCache = () => {
|
|
@@ -9951,7 +9987,7 @@ async function openSpaceRuntime(deps) {
|
|
|
9951
9987
|
});
|
|
9952
9988
|
async function dropUnused() {
|
|
9953
9989
|
if (!cache || !partial || closed) return;
|
|
9954
|
-
const cutoff = Date.now() - (cache.unusedAfterDays ?? 30) *
|
|
9990
|
+
const cutoff = Date.now() - (cache.unusedAfterDays ?? 30) * DAY3;
|
|
9955
9991
|
const waiting = /* @__PURE__ */ new Set();
|
|
9956
9992
|
for (const name of await adapter.list(PENDING_PREFIX)) {
|
|
9957
9993
|
const bytes = await adapter.get(name);
|
|
@@ -10379,7 +10415,7 @@ async function openSpaceRuntime(deps) {
|
|
|
10379
10415
|
});
|
|
10380
10416
|
}
|
|
10381
10417
|
|
|
10382
|
-
// ../src/schemas/contacts.ts
|
|
10418
|
+
// ../core/src/schemas/contacts.ts
|
|
10383
10419
|
var typed = () => (definition) => definition;
|
|
10384
10420
|
var contact = typed()({
|
|
10385
10421
|
name: "std.contact",
|
|
@@ -10392,7 +10428,8 @@ var contact = typed()({
|
|
|
10392
10428
|
name: { type: "string", maxLength: 200, description: "What you call them" },
|
|
10393
10429
|
space: { type: "string", maxLength: 256, description: "The id of your space for two" },
|
|
10394
10430
|
note: { type: "string", maxLength: 2e3 },
|
|
10395
|
-
blocked: { type: "boolean", description: "Their contact requests are hidden, in every space" }
|
|
10431
|
+
blocked: { type: "boolean", description: "Their contact requests are hidden, in every space" },
|
|
10432
|
+
door: { type: "string", maxLength: 64, description: "The door they knocked on, while your answer is still to be written in your space for two" }
|
|
10396
10433
|
},
|
|
10397
10434
|
required: ["did", "name"]
|
|
10398
10435
|
},
|
|
@@ -10412,8 +10449,275 @@ var contactRequest = typed()({
|
|
|
10412
10449
|
},
|
|
10413
10450
|
rules: { edit: "creator", delete: "creator" }
|
|
10414
10451
|
});
|
|
10452
|
+
var door = typed()({
|
|
10453
|
+
name: "std.door",
|
|
10454
|
+
title: "Door",
|
|
10455
|
+
description: "A way for people you share no space with to ask to become your contact.",
|
|
10456
|
+
schema: {
|
|
10457
|
+
type: "object",
|
|
10458
|
+
properties: {
|
|
10459
|
+
id: { type: "string", minLength: 16, maxLength: 64, description: "Random; the door key is derived from it" },
|
|
10460
|
+
label: { type: "string", maxLength: 64, description: "What you call this door \u2014 only you see it" },
|
|
10461
|
+
name: { type: "string", maxLength: 64, description: "The name its code gives, shown to whoever knocks" },
|
|
10462
|
+
relays: { type: "array", items: { type: "string", maxLength: 200 }, minItems: 1, maxItems: 3, description: "Whose mailboxes hold its knocks" }
|
|
10463
|
+
},
|
|
10464
|
+
required: ["id", "relays"]
|
|
10465
|
+
},
|
|
10466
|
+
rules: { onePer: ["id"] }
|
|
10467
|
+
});
|
|
10468
|
+
var knock = typed()({
|
|
10469
|
+
name: "std.knock",
|
|
10470
|
+
title: "Knock",
|
|
10471
|
+
description: "A knock left on a door, waiting for an answer.",
|
|
10472
|
+
schema: {
|
|
10473
|
+
type: "object",
|
|
10474
|
+
properties: {
|
|
10475
|
+
space: { type: "string", maxLength: 256, description: "The space for two it invites them to" },
|
|
10476
|
+
name: { type: "string", maxLength: 64, description: "The name their door code gave" },
|
|
10477
|
+
door: { type: "string", maxLength: 64, description: "The door key knocked on" },
|
|
10478
|
+
sign: { type: "string", maxLength: 64, description: "The door's signing key, which their answer must be signed with" },
|
|
10479
|
+
invite: { type: "string", maxLength: 8e3, description: "The invite sent, to close once they answer" }
|
|
10480
|
+
},
|
|
10481
|
+
required: ["space", "name", "door", "sign", "invite"]
|
|
10482
|
+
},
|
|
10483
|
+
rules: { onePer: ["space"] }
|
|
10484
|
+
});
|
|
10485
|
+
var knockAnswer = typed()({
|
|
10486
|
+
name: "std.knock-answer",
|
|
10487
|
+
title: "Knock answer",
|
|
10488
|
+
description: "Proof that the account which joined is the one behind the door knocked on.",
|
|
10489
|
+
schema: {
|
|
10490
|
+
type: "object",
|
|
10491
|
+
properties: {
|
|
10492
|
+
sig: { type: "string", maxLength: 200, description: 'The door signing key over "weave/knock-answer/v1|<space>|<account>"' }
|
|
10493
|
+
},
|
|
10494
|
+
required: ["sig"]
|
|
10495
|
+
},
|
|
10496
|
+
rules: { edit: "creator", delete: "creator" }
|
|
10497
|
+
});
|
|
10498
|
+
|
|
10499
|
+
// ../core/src/doors/doors.ts
|
|
10500
|
+
var MAX_DOOR_RELAYS = 3;
|
|
10501
|
+
var KNOCK_TTL_SECONDS = 14 * 24 * 3600;
|
|
10502
|
+
var KNOCK_DROP_WINDOW_SECONDS = 600;
|
|
10503
|
+
var MAX_NAME = 64;
|
|
10504
|
+
var MAX_NOTE = 2e3;
|
|
10505
|
+
var MAX_INVITE = 6e3;
|
|
10506
|
+
var MAX_PROOF = 4096;
|
|
10507
|
+
function clip(text, max) {
|
|
10508
|
+
const chars = Array.from(text);
|
|
10509
|
+
return chars.length <= max ? text : chars.slice(0, max).join("");
|
|
10510
|
+
}
|
|
10511
|
+
function encodeDoorCode(code) {
|
|
10512
|
+
const problem = checkDoorCode({ v: 1, ...code });
|
|
10513
|
+
if (problem) throw new Error(problem);
|
|
10514
|
+
return base64UrlEncode(
|
|
10515
|
+
utf8Encode(canonicalize({ v: 1, key: code.key, sign: code.sign, relays: [...code.relays], ...code.name ? { name: code.name } : {} }))
|
|
10516
|
+
);
|
|
10517
|
+
}
|
|
10518
|
+
function parseDoorCode(text) {
|
|
10519
|
+
const trimmed = text.trim();
|
|
10520
|
+
const found = /(?:^|[#?&])door=([A-Za-z0-9_-]+)/.exec(trimmed);
|
|
10521
|
+
const raw = found ? found[1] : trimmed;
|
|
10522
|
+
let parsed;
|
|
10523
|
+
try {
|
|
10524
|
+
parsed = JSON.parse(utf8Decode(base64UrlDecode(raw)));
|
|
10525
|
+
} catch {
|
|
10526
|
+
throw new Error("That is not a door code \u2014 it may be cut short.");
|
|
10527
|
+
}
|
|
10528
|
+
const problem = checkDoorCode(parsed);
|
|
10529
|
+
if (problem) throw new Error(`That door code doesn't work: ${problem}`);
|
|
10530
|
+
const code = parsed;
|
|
10531
|
+
return Object.freeze({ v: 1, key: code.key, sign: code.sign, relays: Object.freeze([...code.relays]), ...code.name ? { name: code.name } : {} });
|
|
10532
|
+
}
|
|
10533
|
+
function checkDoorCode(value) {
|
|
10534
|
+
const code = value;
|
|
10535
|
+
if (!code || typeof code !== "object" || code.v !== 1) return "it is not a version 1 door";
|
|
10536
|
+
if (!isContactPublicKey(code.key)) return "its key is not a P-256 public key";
|
|
10537
|
+
if (!isContactPublicKey(code.sign) || code.sign === code.key) return "its signing key is not a P-256 public key of its own";
|
|
10538
|
+
if (!Array.isArray(code.relays) || code.relays.length === 0 || code.relays.length > MAX_DOOR_RELAYS) {
|
|
10539
|
+
return `it names 1\u2013${MAX_DOOR_RELAYS} relays`;
|
|
10540
|
+
}
|
|
10541
|
+
const relays = checkRelays(code.relays);
|
|
10542
|
+
if (relays) return relays;
|
|
10543
|
+
if (code.name !== void 0 && (typeof code.name !== "string" || Array.from(code.name).length > MAX_NAME)) return `its name is text of at most ${MAX_NAME} characters`;
|
|
10544
|
+
return null;
|
|
10545
|
+
}
|
|
10546
|
+
async function doorTopic(sign) {
|
|
10547
|
+
return base64UrlEncode(await sha2562(utf8Encode(`weave/door-topic/v1|${sign}`)));
|
|
10548
|
+
}
|
|
10549
|
+
var purgeMessage = (topic, nonce, ids) => utf8Encode(`weave/door-purge/v1|${topic}|${nonce}|${ids ? [...ids].sort().join(",") : "*"}`);
|
|
10550
|
+
function signPurge(signKey, topic, nonce, ids) {
|
|
10551
|
+
return signWithScalar(signKey, purgeMessage(topic, nonce, ids));
|
|
10552
|
+
}
|
|
10553
|
+
var answerMessage = (pairSpace, did) => utf8Encode(`weave/knock-answer/v1|${pairSpace}|${did}`);
|
|
10554
|
+
function signAnswer(signKey, pairSpace, did) {
|
|
10555
|
+
return signWithScalar(signKey, answerMessage(pairSpace, did));
|
|
10556
|
+
}
|
|
10557
|
+
function checkAnswer(sign, pairSpace, did, signature) {
|
|
10558
|
+
return verifyWithPoint(sign, answerMessage(pairSpace, did), signature);
|
|
10559
|
+
}
|
|
10560
|
+
async function knockId(blob) {
|
|
10561
|
+
return base64UrlEncode(await sha2562(utf8Encode(blob)));
|
|
10562
|
+
}
|
|
10563
|
+
var knockContext = (door2) => `weave/knock/v1|${door2}`;
|
|
10564
|
+
async function sealKnock(door2, knock2, session, provider) {
|
|
10565
|
+
const note = knock2.note ? clip(knock2.note.trim(), MAX_NOTE) : void 0;
|
|
10566
|
+
const body = {
|
|
10567
|
+
v: 1,
|
|
10568
|
+
door: door2,
|
|
10569
|
+
from: knock2.from,
|
|
10570
|
+
name: clip(knock2.name.trim(), MAX_NAME) || "Someone",
|
|
10571
|
+
invite: knock2.invite,
|
|
10572
|
+
...note ? { note } : {},
|
|
10573
|
+
at: Math.floor(Date.now() / 1e3),
|
|
10574
|
+
session: session.did,
|
|
10575
|
+
proof: session.proof
|
|
10576
|
+
};
|
|
10577
|
+
const sig = base64UrlEncode(await provider.sign(session.key, utf8Encode(canonicalize(body))));
|
|
10578
|
+
return sealFor(door2, { body, sig }, knockContext(door2));
|
|
10579
|
+
}
|
|
10580
|
+
async function openKnock(doorKey, blob, receivedAt, provider) {
|
|
10581
|
+
const door2 = contactPublicKey(doorKey);
|
|
10582
|
+
const opened = await openSealed((await contactKeyPair(doorKey)).privateKey, blob, knockContext(door2));
|
|
10583
|
+
const body = opened?.body;
|
|
10584
|
+
if (!body || typeof opened.sig !== "string") return null;
|
|
10585
|
+
if (body.v !== 1 || body.door !== door2) return null;
|
|
10586
|
+
if (typeof body.from !== "string" || typeof body.session !== "string" || typeof body.name !== "string") return null;
|
|
10587
|
+
if (Array.from(body.name).length > MAX_NAME) return null;
|
|
10588
|
+
if (typeof body.invite !== "string" || body.invite.length > MAX_INVITE) return null;
|
|
10589
|
+
if (typeof body.proof !== "string" || body.proof.length > MAX_PROOF) return null;
|
|
10590
|
+
if (body.note !== void 0 && (typeof body.note !== "string" || Array.from(body.note).length > MAX_NOTE)) return null;
|
|
10591
|
+
if (!Number.isSafeInteger(body.at) || !Number.isFinite(receivedAt)) return null;
|
|
10592
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
10593
|
+
if (body.at > now + UCAN_CLOCK_SKEW_SECONDS || body.at < now - KNOCK_TTL_SECONDS) return null;
|
|
10594
|
+
const dropped = Math.floor(receivedAt / 1e3);
|
|
10595
|
+
if (body.at > dropped + UCAN_CLOCK_SKEW_SECONDS || body.at < dropped - KNOCK_DROP_WINDOW_SECONDS) return null;
|
|
10596
|
+
let signed = false;
|
|
10597
|
+
try {
|
|
10598
|
+
const key3 = await provider.importPublicKey(didToPublicKey(body.session).publicKeyBytes);
|
|
10599
|
+
signed = await provider.verify(key3, base64UrlDecode(opened.sig), utf8Encode(canonicalize(body)));
|
|
10600
|
+
} catch {
|
|
10601
|
+
return null;
|
|
10602
|
+
}
|
|
10603
|
+
if (!signed) return null;
|
|
10604
|
+
if (isAgentNote(body.proof)) return null;
|
|
10605
|
+
const chain = await resolveDelegationRoot(body.proof, () => null, provider, { at: body.at }).catch(() => null);
|
|
10606
|
+
if (!chain?.valid || chain.audience !== body.session || chain.rootDid !== body.from) return null;
|
|
10607
|
+
if (!chain.capabilities.some((capability) => capability.with === "*" && (capability.can === "expression/*" || capability.can === "*"))) return null;
|
|
10608
|
+
let invited;
|
|
10609
|
+
try {
|
|
10610
|
+
invited = parseSpaceInvite(body.invite);
|
|
10611
|
+
} catch {
|
|
10612
|
+
return null;
|
|
10613
|
+
}
|
|
10614
|
+
if (invited.space.creator !== body.from || invited.space.visibility !== "private" || !invited.key) return null;
|
|
10615
|
+
if (await checkSpace(invited.space) !== null) return null;
|
|
10616
|
+
return Object.freeze({
|
|
10617
|
+
from: body.from,
|
|
10618
|
+
name: clip(body.name, MAX_NAME) || "Someone",
|
|
10619
|
+
...body.note ? { note: body.note } : {},
|
|
10620
|
+
invite: body.invite,
|
|
10621
|
+
pairSpace: invited.space.id,
|
|
10622
|
+
at: body.at * 1e3
|
|
10623
|
+
});
|
|
10624
|
+
}
|
|
10415
10625
|
|
|
10416
|
-
// ../src/
|
|
10626
|
+
// ../core/src/network/mailbox.ts
|
|
10627
|
+
var MAX_PAGES = 8;
|
|
10628
|
+
function createMailboxClient(options = {}) {
|
|
10629
|
+
const timeoutMs = options.timeoutMs ?? 8e3;
|
|
10630
|
+
function ask(relay, message, pick) {
|
|
10631
|
+
return talk(relay, (send2) => send2(message), async (answer) => pick(answer));
|
|
10632
|
+
}
|
|
10633
|
+
function talk(relay, start, hear) {
|
|
10634
|
+
const Socket = options.WebSocket ?? globalThis.WebSocket;
|
|
10635
|
+
if (!Socket) return Promise.reject(new Error("No WebSocket here to reach a relay with"));
|
|
10636
|
+
return new Promise((resolve, reject) => {
|
|
10637
|
+
let settled = false;
|
|
10638
|
+
const ws = new Socket(relay);
|
|
10639
|
+
const finish = (error, value) => {
|
|
10640
|
+
if (settled) return;
|
|
10641
|
+
settled = true;
|
|
10642
|
+
clearTimeout(timer);
|
|
10643
|
+
try {
|
|
10644
|
+
ws.close();
|
|
10645
|
+
} catch {
|
|
10646
|
+
}
|
|
10647
|
+
if (error) reject(error);
|
|
10648
|
+
else resolve(value);
|
|
10649
|
+
};
|
|
10650
|
+
const timer = setTimeout(() => finish(new Error(`${relay} did not answer`)), timeoutMs);
|
|
10651
|
+
const say = (message) => ws.send(JSON.stringify(message));
|
|
10652
|
+
ws.onopen = () => start(say);
|
|
10653
|
+
ws.onerror = () => finish(new Error(`Could not reach ${relay}`));
|
|
10654
|
+
ws.onclose = () => finish(new Error(`${relay} closed the connection`));
|
|
10655
|
+
ws.onmessage = (event) => {
|
|
10656
|
+
let answer;
|
|
10657
|
+
try {
|
|
10658
|
+
answer = JSON.parse(String(event.data));
|
|
10659
|
+
} catch {
|
|
10660
|
+
return;
|
|
10661
|
+
}
|
|
10662
|
+
if (!answer || typeof answer !== "object") return;
|
|
10663
|
+
hear(answer, say).then(
|
|
10664
|
+
(value) => {
|
|
10665
|
+
if (value !== void 0) finish(null, value);
|
|
10666
|
+
},
|
|
10667
|
+
(error) => finish(error instanceof Error ? error : new Error(String(error)))
|
|
10668
|
+
);
|
|
10669
|
+
};
|
|
10670
|
+
});
|
|
10671
|
+
}
|
|
10672
|
+
const client = {
|
|
10673
|
+
drop(relay, topic, blob, ttlSeconds) {
|
|
10674
|
+
return ask(relay, { type: "drop", topic, blob, ...ttlSeconds ? { ttl: ttlSeconds } : {} }, (answer) => {
|
|
10675
|
+
if (answer.topic !== topic) return void 0;
|
|
10676
|
+
if (answer.type === "dropped" && typeof answer.id === "string") return answer.id;
|
|
10677
|
+
if (answer.type === "refused") throw new Error(`${relay} refused the knock: ${String(answer.reason ?? "no reason given")}`);
|
|
10678
|
+
return void 0;
|
|
10679
|
+
});
|
|
10680
|
+
},
|
|
10681
|
+
async fetch(relay, topic, after = 0) {
|
|
10682
|
+
const found = [];
|
|
10683
|
+
let from = after;
|
|
10684
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
10685
|
+
const { items, more } = await ask(
|
|
10686
|
+
relay,
|
|
10687
|
+
{ type: "fetch", topic, after: from },
|
|
10688
|
+
(answer) => answer.type === "mail" && answer.topic === topic && Array.isArray(answer.items) ? { items: answer.items.filter(isMailItem), more: answer.more === true } : void 0
|
|
10689
|
+
);
|
|
10690
|
+
found.push(...items);
|
|
10691
|
+
if (!more || items.length === 0) break;
|
|
10692
|
+
from = Math.max(...items.map((item) => item.seq));
|
|
10693
|
+
}
|
|
10694
|
+
return found;
|
|
10695
|
+
},
|
|
10696
|
+
purge(relay, topic, sign, ids, signChallenge) {
|
|
10697
|
+
return talk(
|
|
10698
|
+
relay,
|
|
10699
|
+
(send2) => send2({ type: "challenge" }),
|
|
10700
|
+
async (answer, send2) => {
|
|
10701
|
+
if (answer.type === "challenge" && typeof answer.nonce === "string") {
|
|
10702
|
+
send2({ type: "purge", topic, sign, ...ids ? { ids: [...ids] } : {}, sig: await signChallenge(answer.nonce) });
|
|
10703
|
+
return void 0;
|
|
10704
|
+
}
|
|
10705
|
+
if (answer.topic !== topic) return void 0;
|
|
10706
|
+
if (answer.type === "purged" && typeof answer.count === "number") return answer.count;
|
|
10707
|
+
if (answer.type === "refused") throw new Error(`${relay} refused to clear the door: ${String(answer.reason ?? "no reason given")}`);
|
|
10708
|
+
return void 0;
|
|
10709
|
+
}
|
|
10710
|
+
);
|
|
10711
|
+
}
|
|
10712
|
+
};
|
|
10713
|
+
return Object.freeze(client);
|
|
10714
|
+
}
|
|
10715
|
+
function isMailItem(value) {
|
|
10716
|
+
const item = value;
|
|
10717
|
+
return !!item && Number.isSafeInteger(item.seq) && typeof item.id === "string" && typeof item.at === "number" && typeof item.blob === "string";
|
|
10718
|
+
}
|
|
10719
|
+
|
|
10720
|
+
// ../core/src/space/notify.ts
|
|
10417
10721
|
var NOTIFY_COLLECTION = "sys.notify";
|
|
10418
10722
|
var SUBSCRIPTION_COLLECTION = "sys.subscription";
|
|
10419
10723
|
var COLLECTION = /^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/;
|
|
@@ -10500,7 +10804,7 @@ function matchesSubscription(sub, spaceId, version, account, now = Date.now()) {
|
|
|
10500
10804
|
return true;
|
|
10501
10805
|
}
|
|
10502
10806
|
|
|
10503
|
-
// ../src/session/hosting.ts
|
|
10807
|
+
// ../core/src/session/hosting.ts
|
|
10504
10808
|
var HOSTING_COLLECTION = "sys.hosting";
|
|
10505
10809
|
var REQUEST_WINDOW_SECONDS = 300;
|
|
10506
10810
|
function newSubscriptionSeed() {
|
|
@@ -10616,7 +10920,7 @@ function createHostClient(url, host, key3, provider = createP256Provider()) {
|
|
|
10616
10920
|
});
|
|
10617
10921
|
}
|
|
10618
10922
|
|
|
10619
|
-
// ../src/node/node.ts
|
|
10923
|
+
// ../core/src/node/node.ts
|
|
10620
10924
|
var SESSION_CAPABILITY = { with: "*", can: "expression/*" };
|
|
10621
10925
|
var DEFAULT_TTL_SECONDS = 3600;
|
|
10622
10926
|
var FORGET_CARRIER_AFTER_MS = 30 * 24 * 3600 * 1e3;
|
|
@@ -11425,11 +11729,13 @@ async function createNode(config) {
|
|
|
11425
11729
|
if (!contact2.did.startsWith("did:")) throw new Error("A contact needs their account DID");
|
|
11426
11730
|
const name = contact2.name.trim().slice(0, 200);
|
|
11427
11731
|
if (!name) throw new Error("A contact needs a name");
|
|
11732
|
+
const pending = (await contactRecords()).find((record) => record.body.did === contact2.did)?.body?.door;
|
|
11428
11733
|
return writeContact({
|
|
11429
11734
|
did: contact2.did,
|
|
11430
11735
|
name,
|
|
11431
11736
|
...contact2.space ? { space: contact2.space } : {},
|
|
11432
|
-
...contact2.note ? { note: contact2.note.slice(0, 2e3) } : {}
|
|
11737
|
+
...contact2.note ? { note: contact2.note.slice(0, 2e3) } : {},
|
|
11738
|
+
...pending ? { door: pending } : {}
|
|
11433
11739
|
});
|
|
11434
11740
|
},
|
|
11435
11741
|
async remove(did) {
|
|
@@ -11505,6 +11811,253 @@ async function createNode(config) {
|
|
|
11505
11811
|
return [...seen].filter((account2) => account2 !== config.signer.did && account2 !== did).sort();
|
|
11506
11812
|
}
|
|
11507
11813
|
});
|
|
11814
|
+
const mailbox = config.mailbox ?? createMailboxClient();
|
|
11815
|
+
const knockCache = /* @__PURE__ */ new Map();
|
|
11816
|
+
const DAY_MS = 24 * 3600 * 1e3;
|
|
11817
|
+
function requireContactKey(what) {
|
|
11818
|
+
if (agentSession) throw new Error(`An agent can't use doors. ${what} is the person's to do.`);
|
|
11819
|
+
if (!config.contactKey) throw new Error(`${what} needs your contact key. Connect to your account home again, and allow contacts.`);
|
|
11820
|
+
return config.contactKey;
|
|
11821
|
+
}
|
|
11822
|
+
async function doorKeys(id) {
|
|
11823
|
+
const contactKey = requireContactKey("A door");
|
|
11824
|
+
const key3 = await deriveDoorKeyBytes(contactKey, id);
|
|
11825
|
+
const sign = await deriveDoorSignKeyBytes(contactKey, id);
|
|
11826
|
+
const signPublic = contactPublicKey(sign);
|
|
11827
|
+
return { key: key3, sign, publicKey: contactPublicKey(key3), signPublic, topic: await doorTopic(signPublic) };
|
|
11828
|
+
}
|
|
11829
|
+
async function doorRecords() {
|
|
11830
|
+
if (!contactsSpaceId) return [];
|
|
11831
|
+
return (await (await contactsRuntime()).list({ collection: door.name })).filter(
|
|
11832
|
+
(record) => record.verified && record.root === config.signer.did && typeof record.body?.id === "string" && Array.isArray(record.body.relays)
|
|
11833
|
+
);
|
|
11834
|
+
}
|
|
11835
|
+
async function doorView(record) {
|
|
11836
|
+
const body = record.body;
|
|
11837
|
+
const keys = await doorKeys(body.id);
|
|
11838
|
+
return Object.freeze({
|
|
11839
|
+
id: body.id,
|
|
11840
|
+
...body.label ? { label: body.label } : {},
|
|
11841
|
+
...body.name ? { name: body.name } : {},
|
|
11842
|
+
key: keys.publicKey,
|
|
11843
|
+
sign: keys.signPublic,
|
|
11844
|
+
relays: Object.freeze([...body.relays]),
|
|
11845
|
+
code: encodeDoorCode({ key: keys.publicKey, sign: keys.signPublic, relays: body.relays, ...body.name ? { name: body.name } : {} }),
|
|
11846
|
+
createdAt: record.createdAt
|
|
11847
|
+
});
|
|
11848
|
+
}
|
|
11849
|
+
async function purgeDoor(door2, ids) {
|
|
11850
|
+
const keys = await doorKeys(door2.id);
|
|
11851
|
+
const done = await Promise.allSettled(
|
|
11852
|
+
door2.relays.map((relay) => mailbox.purge(relay, keys.topic, keys.signPublic, ids, (nonce) => signPurge(keys.sign, keys.topic, nonce, ids)))
|
|
11853
|
+
);
|
|
11854
|
+
return done.filter((result) => result.status === "fulfilled").length;
|
|
11855
|
+
}
|
|
11856
|
+
async function sentRecords() {
|
|
11857
|
+
if (!contactsSpaceId) return [];
|
|
11858
|
+
return (await (await contactsRuntime()).list({ collection: knock.name })).filter(
|
|
11859
|
+
(record) => record.verified && record.root === config.signer.did && typeof record.body?.space === "string" && typeof record.body.sign === "string" && typeof record.body.invite === "string"
|
|
11860
|
+
);
|
|
11861
|
+
}
|
|
11862
|
+
async function settleSent() {
|
|
11863
|
+
for (const record of await sentRecords()) {
|
|
11864
|
+
const { space: space2, name, sign, invite } = record.body;
|
|
11865
|
+
if (!await registry.get(space2)) {
|
|
11866
|
+
await (await contactsRuntime()).remove(record.key);
|
|
11867
|
+
continue;
|
|
11868
|
+
}
|
|
11869
|
+
const open2 = await runtime(space2);
|
|
11870
|
+
const { members } = await open2.access();
|
|
11871
|
+
let answered = null;
|
|
11872
|
+
for (const answer of await open2.list({ collection: knockAnswer.name })) {
|
|
11873
|
+
const did = answer.root;
|
|
11874
|
+
if (!answer.verified || !did || did === config.signer.did || answer.createdBy !== did || typeof answer.body?.sig !== "string") continue;
|
|
11875
|
+
if (!members.some((member) => member.did === did)) continue;
|
|
11876
|
+
if (await checkAnswer(sign, space2, did, answer.body.sig)) {
|
|
11877
|
+
answered = did;
|
|
11878
|
+
break;
|
|
11879
|
+
}
|
|
11880
|
+
}
|
|
11881
|
+
if (answered) {
|
|
11882
|
+
await spaces.closeInvite(space2, invite).catch(() => {
|
|
11883
|
+
});
|
|
11884
|
+
const given = (await open2.profiles()).find((profile) => profile.did === answered)?.name;
|
|
11885
|
+
await writeContact({ did: answered, name: given ?? name, space: space2 });
|
|
11886
|
+
await (await contactsRuntime()).remove(record.key);
|
|
11887
|
+
} else if (Date.now() - Date.parse(record.createdAt) > KNOCK_TTL_SECONDS * 1e3 + DAY_MS) {
|
|
11888
|
+
await spaces.leave(space2).catch(() => {
|
|
11889
|
+
});
|
|
11890
|
+
await (await contactsRuntime()).remove(record.key);
|
|
11891
|
+
}
|
|
11892
|
+
}
|
|
11893
|
+
}
|
|
11894
|
+
async function answerAccepted() {
|
|
11895
|
+
for (const record of await contactRecords()) {
|
|
11896
|
+
const { did, space: space2, door: door2 } = record.body;
|
|
11897
|
+
if (typeof door2 !== "string" || typeof space2 !== "string") continue;
|
|
11898
|
+
if (!await registry.get(space2)) continue;
|
|
11899
|
+
const open2 = await runtime(space2);
|
|
11900
|
+
if ((await open2.access()).role === null) continue;
|
|
11901
|
+
const { sign } = await doorKeys(door2);
|
|
11902
|
+
try {
|
|
11903
|
+
await open2.put(knockAnswer.name, { sig: await signAnswer(sign, space2, config.signer.did) });
|
|
11904
|
+
} catch {
|
|
11905
|
+
continue;
|
|
11906
|
+
}
|
|
11907
|
+
const { door: _answered, ...rest } = record.body;
|
|
11908
|
+
await writeContact({ ...rest, did });
|
|
11909
|
+
}
|
|
11910
|
+
}
|
|
11911
|
+
async function readDoors() {
|
|
11912
|
+
const now = Date.now();
|
|
11913
|
+
for (const [id, cached] of knockCache) if (cached.until <= now) knockCache.delete(id);
|
|
11914
|
+
const found = [];
|
|
11915
|
+
for (const record of await doorRecords()) {
|
|
11916
|
+
const { id: doorId, relays } = record.body;
|
|
11917
|
+
const keys = await doorKeys(doorId);
|
|
11918
|
+
const held = /* @__PURE__ */ new Map();
|
|
11919
|
+
for (const result of await Promise.allSettled(relays.map((relay) => mailbox.fetch(relay, keys.topic)))) {
|
|
11920
|
+
if (result.status !== "fulfilled") continue;
|
|
11921
|
+
for (const item of result.value) {
|
|
11922
|
+
const id = await knockId(item.blob);
|
|
11923
|
+
const seen = held.get(id);
|
|
11924
|
+
if (!seen || item.at < seen.at) held.set(id, { blob: item.blob, at: item.at });
|
|
11925
|
+
}
|
|
11926
|
+
}
|
|
11927
|
+
for (const [id, item] of held) {
|
|
11928
|
+
let cached = knockCache.get(id);
|
|
11929
|
+
if (!cached) {
|
|
11930
|
+
cached = { opened: await openKnock(keys.key, item.blob, item.at, provider), door: doorId, until: item.at + KNOCK_TTL_SECONDS * 1e3 };
|
|
11931
|
+
knockCache.set(id, cached);
|
|
11932
|
+
}
|
|
11933
|
+
if (cached.opened) found.push({ id, door: doorId, opened: cached.opened });
|
|
11934
|
+
}
|
|
11935
|
+
}
|
|
11936
|
+
return found;
|
|
11937
|
+
}
|
|
11938
|
+
const doors = Object.freeze({
|
|
11939
|
+
async list() {
|
|
11940
|
+
if (!config.contactKey || agentSession) return [];
|
|
11941
|
+
return Promise.all((await doorRecords()).map(doorView));
|
|
11942
|
+
},
|
|
11943
|
+
async open(options = {}) {
|
|
11944
|
+
requireContactKey("Opening a door");
|
|
11945
|
+
if (!contactsSpaceId) await contactsRuntime();
|
|
11946
|
+
const relays = [...options.relays ?? (config.network?.relays ?? []).filter((url) => checkRelays([url]) === null)].slice(0, MAX_DOOR_RELAYS);
|
|
11947
|
+
if (relays.length === 0) throw new Error("A door needs a relay to hold its knocks, and this node has none.");
|
|
11948
|
+
const name = clip((options.name ?? await ownName() ?? "").trim(), 64);
|
|
11949
|
+
const label = options.label ? clip(options.label.trim(), 64) : "";
|
|
11950
|
+
const id = base64UrlEncode(globalThis.crypto.getRandomValues(new Uint8Array(16)));
|
|
11951
|
+
const keys = await doorKeys(id);
|
|
11952
|
+
encodeDoorCode({ key: keys.publicKey, sign: keys.signPublic, relays, ...name ? { name } : {} });
|
|
11953
|
+
const open2 = await contactsRuntime();
|
|
11954
|
+
await ensureDefined(open2, door);
|
|
11955
|
+
return doorView(await open2.put(door.name, { id, relays, ...name ? { name } : {}, ...label ? { label } : {} }));
|
|
11956
|
+
},
|
|
11957
|
+
async close(id) {
|
|
11958
|
+
const found = (await doorRecords()).find((record) => record.body.id === id);
|
|
11959
|
+
if (found) await (await contactsRuntime()).remove(found.key);
|
|
11960
|
+
for (const [knock2, cached] of knockCache) if (cached.door === id) knockCache.delete(knock2);
|
|
11961
|
+
},
|
|
11962
|
+
async clear(id) {
|
|
11963
|
+
requireContactKey("Clearing a door");
|
|
11964
|
+
const found = (await doorRecords()).find((record) => record.body.id === id);
|
|
11965
|
+
if (!found) throw new Error("There is no such door");
|
|
11966
|
+
if (await purgeDoor(found.body, null) === 0) throw new Error("None of the door's relays could be reached to clear it.");
|
|
11967
|
+
for (const [knock2, cached] of knockCache) if (cached.door === id) knockCache.delete(knock2);
|
|
11968
|
+
},
|
|
11969
|
+
async knock(code, options = {}) {
|
|
11970
|
+
requireContactKey("Knocking on a door");
|
|
11971
|
+
requireEverywhere("Knocking on a door");
|
|
11972
|
+
const door2 = parseDoorCode(code);
|
|
11973
|
+
for (const mine2 of await doors.list()) if (mine2.key === door2.key) throw new Error("That is one of your own doors");
|
|
11974
|
+
if (!contactsSpaceId) await contactsRuntime();
|
|
11975
|
+
const mine = await ownName() ?? "Someone";
|
|
11976
|
+
const theirs = door2.name ?? "Contact";
|
|
11977
|
+
const pair = await spaces.create({ name: `${clip(mine, 64)} & ${theirs}`, visibility: "private", ...team });
|
|
11978
|
+
let invite;
|
|
11979
|
+
try {
|
|
11980
|
+
await ensureDefined(await runtime(pair.id), knockAnswer);
|
|
11981
|
+
invite = await spaces.invite(pair.id, { role: "editor" });
|
|
11982
|
+
const blob = await sealKnock(
|
|
11983
|
+
door2.key,
|
|
11984
|
+
{ from: config.signer.did, name: mine, invite, ...options.note ? { note: options.note } : {} },
|
|
11985
|
+
{ did: session.did, key: session.key, proof: session.proof() },
|
|
11986
|
+
provider
|
|
11987
|
+
);
|
|
11988
|
+
const topic = await doorTopic(door2.sign);
|
|
11989
|
+
const left = await Promise.allSettled(door2.relays.map((relay) => mailbox.drop(relay, topic, blob, KNOCK_TTL_SECONDS)));
|
|
11990
|
+
if (!left.some((result) => result.status === "fulfilled")) {
|
|
11991
|
+
const reasons = left.map((result) => result.status === "rejected" ? String(result.reason?.message ?? result.reason) : "").filter(Boolean);
|
|
11992
|
+
throw new Error(`None of their door's relays took the knock. ${reasons.join("; ")}`);
|
|
11993
|
+
}
|
|
11994
|
+
} catch (error) {
|
|
11995
|
+
await spaces.leave(pair.id).catch(() => {
|
|
11996
|
+
});
|
|
11997
|
+
throw error;
|
|
11998
|
+
}
|
|
11999
|
+
const open2 = await contactsRuntime();
|
|
12000
|
+
await ensureDefined(open2, knock);
|
|
12001
|
+
await open2.put(knock.name, { space: pair.id, name: theirs, door: door2.key, sign: door2.sign, invite });
|
|
12002
|
+
return { space: pair.id };
|
|
12003
|
+
},
|
|
12004
|
+
async knocks() {
|
|
12005
|
+
requireContactKey("Reading knocks");
|
|
12006
|
+
await settleSent().catch(() => {
|
|
12007
|
+
});
|
|
12008
|
+
await answerAccepted().catch(() => {
|
|
12009
|
+
});
|
|
12010
|
+
const blocked = new Set((await contactRecords()).filter((record) => record.body.blocked === true).map((record) => record.body.did));
|
|
12011
|
+
const found = [];
|
|
12012
|
+
for (const { id, door: door2, opened } of await readDoors()) {
|
|
12013
|
+
if (opened.from === config.signer.did || blocked.has(opened.from)) continue;
|
|
12014
|
+
if (await registry.get(opened.pairSpace)) continue;
|
|
12015
|
+
found.push(
|
|
12016
|
+
Object.freeze({
|
|
12017
|
+
id,
|
|
12018
|
+
door: door2,
|
|
12019
|
+
from: opened.from,
|
|
12020
|
+
name: opened.name,
|
|
12021
|
+
...opened.note ? { note: opened.note } : {},
|
|
12022
|
+
pairSpace: opened.pairSpace,
|
|
12023
|
+
at: new Date(opened.at).toISOString()
|
|
12024
|
+
})
|
|
12025
|
+
);
|
|
12026
|
+
}
|
|
12027
|
+
return found.sort((a, b) => b.at.localeCompare(a.at));
|
|
12028
|
+
},
|
|
12029
|
+
async sent() {
|
|
12030
|
+
await settleSent().catch(() => {
|
|
12031
|
+
});
|
|
12032
|
+
return (await sentRecords()).map((record) => Object.freeze({ space: record.body.space, name: record.body.name, at: record.createdAt }));
|
|
12033
|
+
},
|
|
12034
|
+
async accept(id) {
|
|
12035
|
+
requireContactKey("Opening the door to someone");
|
|
12036
|
+
requireEverywhere("Opening the door to someone");
|
|
12037
|
+
if (!knockCache.get(id)?.opened) await readDoors();
|
|
12038
|
+
const cached = knockCache.get(id);
|
|
12039
|
+
const knocked = cached?.opened;
|
|
12040
|
+
if (!cached || !knocked) throw new Error("That knock is gone: it expired, or the door was closed or cleared.");
|
|
12041
|
+
await spaces.join(knocked.invite);
|
|
12042
|
+
const known = await contacts.get(knocked.from);
|
|
12043
|
+
const contact2 = await writeContact({ did: knocked.from, name: known?.name ?? knocked.name, space: knocked.pairSpace, door: cached.door });
|
|
12044
|
+
await answerAccepted().catch(() => {
|
|
12045
|
+
});
|
|
12046
|
+
const door2 = (await doorRecords()).find((record) => record.body.id === cached.door);
|
|
12047
|
+
if (door2) await purgeDoor(door2.body, [id]).catch(() => 0);
|
|
12048
|
+
knockCache.delete(id);
|
|
12049
|
+
return contact2;
|
|
12050
|
+
},
|
|
12051
|
+
async dismiss(id) {
|
|
12052
|
+
requireContactKey("Dismissing a knock");
|
|
12053
|
+
if (!knockCache.has(id)) await readDoors();
|
|
12054
|
+
const cached = knockCache.get(id);
|
|
12055
|
+
if (!cached) return;
|
|
12056
|
+
const door2 = (await doorRecords()).find((record) => record.body.id === cached.door);
|
|
12057
|
+
if (door2) await purgeDoor(door2.body, [id]);
|
|
12058
|
+
knockCache.delete(id);
|
|
12059
|
+
}
|
|
12060
|
+
});
|
|
11508
12061
|
const collections = Object.freeze({
|
|
11509
12062
|
async list(spaceId) {
|
|
11510
12063
|
return (await runtime(spaceId)).collections();
|
|
@@ -11721,6 +12274,18 @@ async function createNode(config) {
|
|
|
11721
12274
|
accept: person("accept contact requests"),
|
|
11722
12275
|
others: person("look inside a contact's space")
|
|
11723
12276
|
}),
|
|
12277
|
+
// Doors are the person's: an agent has none and knocks on none.
|
|
12278
|
+
doors: Object.freeze({
|
|
12279
|
+
list: async () => [],
|
|
12280
|
+
open: person("open a door"),
|
|
12281
|
+
close: person("close a door"),
|
|
12282
|
+
knock: person("knock on anyone's door"),
|
|
12283
|
+
clear: person("clear a door"),
|
|
12284
|
+
knocks: person("read knocks"),
|
|
12285
|
+
sent: person("look at knocks"),
|
|
12286
|
+
accept: person("open the door to anyone"),
|
|
12287
|
+
dismiss: person("dismiss a knock")
|
|
12288
|
+
}),
|
|
11724
12289
|
delegation: () => note,
|
|
11725
12290
|
iceServers: node.iceServers,
|
|
11726
12291
|
delegate: person("pass its access on"),
|
|
@@ -11744,6 +12309,7 @@ async function createNode(config) {
|
|
|
11744
12309
|
hosting,
|
|
11745
12310
|
notifications,
|
|
11746
12311
|
contacts,
|
|
12312
|
+
doors,
|
|
11747
12313
|
asAgent,
|
|
11748
12314
|
delegation: () => current,
|
|
11749
12315
|
iceServers: async () => mesh ? mesh.iceServers() : config.network?.iceServers ?? DEFAULT_ICE_SERVERS,
|
|
@@ -11779,7 +12345,7 @@ async function createNode(config) {
|
|
|
11779
12345
|
return node;
|
|
11780
12346
|
}
|
|
11781
12347
|
|
|
11782
|
-
// ../src/node/stores.ts
|
|
12348
|
+
// ../core/src/node/stores.ts
|
|
11783
12349
|
function folderStores(directory2, options = {}) {
|
|
11784
12350
|
return async (path7, storeOptions) => {
|
|
11785
12351
|
const full = options.basePath ? `${options.basePath}/${path7}` : path7;
|
|
@@ -11788,7 +12354,7 @@ function folderStores(directory2, options = {}) {
|
|
|
11788
12354
|
};
|
|
11789
12355
|
}
|
|
11790
12356
|
|
|
11791
|
-
// ../src/network/local-transport.ts
|
|
12357
|
+
// ../core/src/network/local-transport.ts
|
|
11792
12358
|
var later = (fn) => void setTimeout(fn, 0);
|
|
11793
12359
|
function createLocalHub() {
|
|
11794
12360
|
const connected = /* @__PURE__ */ new Map();
|
|
@@ -11834,7 +12400,7 @@ function createLocalHub() {
|
|
|
11834
12400
|
};
|
|
11835
12401
|
}
|
|
11836
12402
|
|
|
11837
|
-
// ../src/node/carrier.ts
|
|
12403
|
+
// ../core/src/node/carrier.ts
|
|
11838
12404
|
async function createCarryCore(config) {
|
|
11839
12405
|
const provider = config.provider ?? createP256Provider();
|
|
11840
12406
|
const signer = createSigner(provider);
|
|
@@ -12065,7 +12631,7 @@ async function createCarryCore(config) {
|
|
|
12065
12631
|
};
|
|
12066
12632
|
}
|
|
12067
12633
|
|
|
12068
|
-
// ../src/node/host.ts
|
|
12634
|
+
// ../core/src/node/host.ts
|
|
12069
12635
|
var SUBSCRIPTION_PREFIX = "subscription:";
|
|
12070
12636
|
var NotAllowedError = class extends Error {
|
|
12071
12637
|
constructor() {
|
|
@@ -12227,7 +12793,7 @@ async function createHostNode(config) {
|
|
|
12227
12793
|
return Object.freeze(host);
|
|
12228
12794
|
}
|
|
12229
12795
|
|
|
12230
|
-
// ../src/records/describe.ts
|
|
12796
|
+
// ../core/src/records/describe.ts
|
|
12231
12797
|
var KNOWN_RULES = ["create", "edit", "delete", "onePer", "fixed"];
|
|
12232
12798
|
var words = (name) => name.replace(/^.*\./, "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase();
|
|
12233
12799
|
var article = (noun) => /^[aeiou]/.test(noun) ? `an ${noun}` : `a ${noun}`;
|
|
@@ -12305,7 +12871,7 @@ function describeCollection(definition) {
|
|
|
12305
12871
|
return sentences;
|
|
12306
12872
|
}
|
|
12307
12873
|
|
|
12308
|
-
// ../src/schemas/apps.ts
|
|
12874
|
+
// ../core/src/schemas/apps.ts
|
|
12309
12875
|
var MAX_APP_COLLECTIONS = 10;
|
|
12310
12876
|
var app = {
|
|
12311
12877
|
name: "std.app",
|
|
@@ -12406,7 +12972,7 @@ async function proposeApp(node, spaceId, body) {
|
|
|
12406
12972
|
return node.records.put(spaceId, app.name, body);
|
|
12407
12973
|
}
|
|
12408
12974
|
|
|
12409
|
-
// ../src/schemas/screens.ts
|
|
12975
|
+
// ../core/src/schemas/screens.ts
|
|
12410
12976
|
var SCREEN_GUIDE = `A screen is one HTML document (scripts and styles inline, at most 48 KB) put on a collection definition as "screen".
|
|
12411
12977
|
When the app is added, apps in the space can show it instead of plain lists. It runs sealed: no network (no fetch, no
|
|
12412
12978
|
images from URLs, no fonts from URLs), no storage, no popups, no forms that submit. Keep state in records, never in the page.
|
|
@@ -12432,7 +12998,7 @@ Draw everything from weave.list(...) and redraw on weave.onChange \u2014 other p
|
|
|
12432
12998
|
Two people can write at once: design collections so that clashes resolve by the rules (onePer, creator-only edits), not by the screen.
|
|
12433
12999
|
Use plain DOM; no libraries can be loaded. Prefer system fonts and simple, readable styling that works in light and dark.`;
|
|
12434
13000
|
|
|
12435
|
-
// ../src/node/actions.ts
|
|
13001
|
+
// ../core/src/node/actions.ts
|
|
12436
13002
|
var space = { type: "string", description: "Space id, from spaces_list" };
|
|
12437
13003
|
var key2 = { type: "string", description: "Record key \u2014 stays the same when the record is edited" };
|
|
12438
13004
|
var str = (input, key3) => input[key3];
|
|
@@ -12873,7 +13439,7 @@ async function runAction(node, name, input = {}) {
|
|
|
12873
13439
|
return action.run(node, input);
|
|
12874
13440
|
}
|
|
12875
13441
|
|
|
12876
|
-
//
|
|
13442
|
+
// ../../node_modules/aws4fetch/dist/aws4fetch.esm.mjs
|
|
12877
13443
|
var encoder2 = new TextEncoder();
|
|
12878
13444
|
var HOST_SERVICES = {
|
|
12879
13445
|
appstream2: "appstream",
|
|
@@ -13152,7 +13718,7 @@ function guessServiceRegion(url, headers) {
|
|
|
13152
13718
|
return [HOST_SERVICES[service] || service, region || ""];
|
|
13153
13719
|
}
|
|
13154
13720
|
|
|
13155
|
-
// ../src/storage/blob/s3.ts
|
|
13721
|
+
// ../core/src/storage/blob/s3.ts
|
|
13156
13722
|
var ATTEMPTS = 5;
|
|
13157
13723
|
function createS3BlobStore(config) {
|
|
13158
13724
|
const client = new AwsClient({
|
|
@@ -13328,7 +13894,7 @@ async function createAccount(home, options) {
|
|
|
13328
13894
|
throw new Error(`This home already has an account for ${did}`);
|
|
13329
13895
|
}
|
|
13330
13896
|
let vault = createVault({ did, label: options.name, wraps: [] });
|
|
13331
|
-
if (options.passphrase) vault = withWrap(vault, await wrapSeedWithPassphrase(seed, options.passphrase,
|
|
13897
|
+
if (options.passphrase) vault = withWrap(vault, await wrapSeedWithPassphrase(seed, options.passphrase, CLI_PASSPHRASE_LABEL));
|
|
13332
13898
|
const id = newAccountId();
|
|
13333
13899
|
const account = {
|
|
13334
13900
|
id,
|
|
@@ -13389,8 +13955,8 @@ async function unlock(home, account, secret) {
|
|
|
13389
13955
|
import { createServer } from "node:http";
|
|
13390
13956
|
import { WebSocketServer } from "ws";
|
|
13391
13957
|
|
|
13392
|
-
// ../
|
|
13393
|
-
import { createHmac, randomBytes as randomBytes4 } from "node:crypto";
|
|
13958
|
+
// ../relay/relay.mjs
|
|
13959
|
+
import { createHash, createHmac, createPublicKey, randomBytes as randomBytes4, verify } from "node:crypto";
|
|
13394
13960
|
var MAX_MESSAGE_BYTES = 64 * 1024;
|
|
13395
13961
|
var MAX_CONNECTIONS = 5e3;
|
|
13396
13962
|
var MAX_CONNECTIONS_PER_IP = 32;
|
|
@@ -13404,6 +13970,32 @@ var RATE_BURST = 100;
|
|
|
13404
13970
|
var HEARTBEAT_MS = 15e3;
|
|
13405
13971
|
var MAX_BUFFERED_BYTES = 1024 * 1024;
|
|
13406
13972
|
var MAX_TURN_ADDRESSES = 2e4;
|
|
13973
|
+
var MAX_BLOB_LENGTH = 12e3;
|
|
13974
|
+
var TOPIC_PATTERN = /^[A-Za-z0-9_-]{43}$/;
|
|
13975
|
+
var MAX_MAIL_PER_TOPIC = 64;
|
|
13976
|
+
var RESERVED_PER_TOPIC = 2;
|
|
13977
|
+
var DROP_WINDOW_MS = 36e5;
|
|
13978
|
+
var MAILBOX_LIMITS = Object.freeze({
|
|
13979
|
+
/** Topics held at once */
|
|
13980
|
+
maxTopics: 5e4,
|
|
13981
|
+
/** Everything held at once, in blob characters */
|
|
13982
|
+
maxChars: 64 * 1024 * 1024,
|
|
13983
|
+
/**
|
|
13984
|
+
* Past `maxChars`, a reserve only topics holding fewer than two knocks may
|
|
13985
|
+
* use: a mailbox someone filled still takes a knock or two on every door.
|
|
13986
|
+
*/
|
|
13987
|
+
reserveChars: 8 * 1024 * 1024,
|
|
13988
|
+
/** How long a knock is kept. A client may ask for less, never more. */
|
|
13989
|
+
ttlSeconds: 14 * 24 * 3600,
|
|
13990
|
+
/** Drops one address may make into one topic per hour: filling a door takes many addresses */
|
|
13991
|
+
dropsPerNetworkPerTopic: 4,
|
|
13992
|
+
/** Drops one address may make in all, per hour: filling the mailbox takes very many */
|
|
13993
|
+
dropsPerNetwork: 30
|
|
13994
|
+
});
|
|
13995
|
+
var PURGE_SPKI_PREFIX = Buffer.from("3039301306072a8648ce3d020106082a8648ce3d030107032200", "hex");
|
|
13996
|
+
var MAX_WATCHES_PER_SOCKET = 32;
|
|
13997
|
+
var MAX_PAGE_ITEMS = 16;
|
|
13998
|
+
var MAX_PAGE_CHARS = 48 * 1024;
|
|
13407
13999
|
var ROUTED = /* @__PURE__ */ new Set(["offer", "answer", "candidate"]);
|
|
13408
14000
|
var DID_PATTERN = /^did:key:z[1-9A-HJ-NP-Za-km-z]+$/;
|
|
13409
14001
|
var CLOSE_DID_TAKEN = 4009;
|
|
@@ -13415,19 +14007,25 @@ function turnFromEnv(env) {
|
|
|
13415
14007
|
}
|
|
13416
14008
|
function createRelay(options = {}) {
|
|
13417
14009
|
const { turn } = options;
|
|
14010
|
+
const limits = { ...MAILBOX_LIMITS, ...options.mailbox };
|
|
13418
14011
|
const log = options.log ?? (() => {
|
|
13419
14012
|
});
|
|
13420
14013
|
const rooms = /* @__PURE__ */ new Map();
|
|
13421
14014
|
const clients = /* @__PURE__ */ new Set();
|
|
13422
14015
|
const perIp = /* @__PURE__ */ new Map();
|
|
13423
14016
|
const turnByNetwork = /* @__PURE__ */ new Map();
|
|
14017
|
+
const mailbox = /* @__PURE__ */ new Map();
|
|
14018
|
+
const watchers = /* @__PURE__ */ new Map();
|
|
14019
|
+
const dropsBy = /* @__PURE__ */ new Map();
|
|
14020
|
+
let mailChars = 0;
|
|
14021
|
+
let mailSeq = 0;
|
|
13424
14022
|
function canEnter(room) {
|
|
13425
14023
|
if (typeof room !== "string" || room.length === 0 || room.length > MAX_ROOM_LENGTH) return false;
|
|
13426
14024
|
const peers = rooms.get(room);
|
|
13427
14025
|
return peers ? peers.size < MAX_PEERS_PER_ROOM : rooms.size < MAX_ROOMS;
|
|
13428
14026
|
}
|
|
13429
14027
|
function accept(ws, legacyRoom, ip) {
|
|
13430
|
-
const client = { ws, legacyRoom, ip, did: null, rooms: /* @__PURE__ */ new Set(), alive: true, tokens: RATE_BURST, refilled: Date.now() };
|
|
14028
|
+
const client = { ws, legacyRoom, ip, did: null, rooms: /* @__PURE__ */ new Set(), watching: /* @__PURE__ */ new Set(), nonce: null, alive: true, tokens: RATE_BURST, refilled: Date.now() };
|
|
13431
14029
|
clients.add(client);
|
|
13432
14030
|
perIp.set(ip, (perIp.get(ip) ?? 0) + 1);
|
|
13433
14031
|
ws.on("pong", () => client.alive = true);
|
|
@@ -13447,6 +14045,7 @@ function createRelay(options = {}) {
|
|
|
13447
14045
|
if (left > 0) perIp.set(ip, left);
|
|
13448
14046
|
else perIp.delete(ip);
|
|
13449
14047
|
for (const room of [...client.rooms]) leave(client, room);
|
|
14048
|
+
for (const topic of [...client.watching]) unwatch(client, topic);
|
|
13450
14049
|
};
|
|
13451
14050
|
ws.on("close", drop);
|
|
13452
14051
|
ws.on("error", () => ws.terminate());
|
|
@@ -13477,6 +14076,10 @@ function createRelay(options = {}) {
|
|
|
13477
14076
|
log(`peer joined room ${short(room)} (${rooms.get(room).size} present)`);
|
|
13478
14077
|
return;
|
|
13479
14078
|
}
|
|
14079
|
+
if (message.type === "drop" || message.type === "fetch" || message.type === "unwatch" || message.type === "challenge" || message.type === "purge") {
|
|
14080
|
+
handleMail(client, message);
|
|
14081
|
+
return;
|
|
14082
|
+
}
|
|
13480
14083
|
if (message.type === "ice") {
|
|
13481
14084
|
if (client.did) offerTurn(client);
|
|
13482
14085
|
return;
|
|
@@ -13494,6 +14097,137 @@ function createRelay(options = {}) {
|
|
|
13494
14097
|
}
|
|
13495
14098
|
}
|
|
13496
14099
|
}
|
|
14100
|
+
function handleMail(client, message) {
|
|
14101
|
+
if (message.type === "challenge") {
|
|
14102
|
+
client.nonce = randomBytes4(18).toString("base64url");
|
|
14103
|
+
send2(client, JSON.stringify({ type: "challenge", nonce: client.nonce }));
|
|
14104
|
+
return;
|
|
14105
|
+
}
|
|
14106
|
+
const { topic } = message;
|
|
14107
|
+
if (typeof topic !== "string" || !TOPIC_PATTERN.test(topic)) return;
|
|
14108
|
+
if (message.type === "unwatch") {
|
|
14109
|
+
unwatch(client, topic);
|
|
14110
|
+
return;
|
|
14111
|
+
}
|
|
14112
|
+
if (message.type === "purge") {
|
|
14113
|
+
purge(client, message);
|
|
14114
|
+
return;
|
|
14115
|
+
}
|
|
14116
|
+
if (message.type === "fetch") {
|
|
14117
|
+
const after = Number.isSafeInteger(message.after) ? message.after : 0;
|
|
14118
|
+
if (message.watch === true && !client.watching.has(topic) && client.watching.size < MAX_WATCHES_PER_SOCKET) {
|
|
14119
|
+
client.watching.add(topic);
|
|
14120
|
+
if (!watchers.has(topic)) watchers.set(topic, /* @__PURE__ */ new Set());
|
|
14121
|
+
watchers.get(topic).add(client);
|
|
14122
|
+
}
|
|
14123
|
+
const now2 = Date.now();
|
|
14124
|
+
const items = [];
|
|
14125
|
+
let chars = 0;
|
|
14126
|
+
let more = false;
|
|
14127
|
+
for (const item2 of mailbox.get(topic) ?? []) {
|
|
14128
|
+
if (item2.seq <= after || item2.expires <= now2) continue;
|
|
14129
|
+
if (items.length >= MAX_PAGE_ITEMS || chars + item2.blob.length > MAX_PAGE_CHARS) {
|
|
14130
|
+
more = true;
|
|
14131
|
+
break;
|
|
14132
|
+
}
|
|
14133
|
+
items.push(mailItem(item2));
|
|
14134
|
+
chars += item2.blob.length;
|
|
14135
|
+
}
|
|
14136
|
+
send2(client, JSON.stringify({ type: "mail", topic, items, more }));
|
|
14137
|
+
return;
|
|
14138
|
+
}
|
|
14139
|
+
const refuse = (reason) => send2(client, JSON.stringify({ type: "refused", topic, reason }));
|
|
14140
|
+
const { blob } = message;
|
|
14141
|
+
if (typeof blob !== "string" || blob.length === 0 || blob.length > MAX_BLOB_LENGTH || !/^[A-Za-z0-9_-]+$/.test(blob)) {
|
|
14142
|
+
refuse("A knock is base64url, at most 12000 characters");
|
|
14143
|
+
return;
|
|
14144
|
+
}
|
|
14145
|
+
const id = createHash("sha256").update(blob).digest("base64url");
|
|
14146
|
+
const held = mailbox.get(topic) ?? [];
|
|
14147
|
+
if (held.some((item2) => item2.id === id)) {
|
|
14148
|
+
send2(client, JSON.stringify({ type: "dropped", topic, id }));
|
|
14149
|
+
return;
|
|
14150
|
+
}
|
|
14151
|
+
const now = Date.now();
|
|
14152
|
+
const network = networkOf(client.ip);
|
|
14153
|
+
const here = countOf(`${network}|${topic}`, now);
|
|
14154
|
+
const everywhere = countOf(network, now);
|
|
14155
|
+
if (here >= limits.dropsPerNetworkPerTopic) return refuse("Too many knocks on this door from here; try later");
|
|
14156
|
+
if (everywhere >= limits.dropsPerNetwork) return refuse("Too many knocks from here; try later");
|
|
14157
|
+
if (held.length >= MAX_MAIL_PER_TOPIC) return refuse("This door is full");
|
|
14158
|
+
if (!mailbox.has(topic) && mailbox.size >= limits.maxTopics) return refuse("The mailbox is full");
|
|
14159
|
+
const room = held.length < RESERVED_PER_TOPIC ? limits.maxChars + limits.reserveChars : limits.maxChars;
|
|
14160
|
+
if (mailChars + blob.length > room) return refuse("The mailbox is full");
|
|
14161
|
+
const ttl = Number.isFinite(message.ttl) && message.ttl > 0 ? Math.min(message.ttl, limits.ttlSeconds) : limits.ttlSeconds;
|
|
14162
|
+
const item = { seq: ++mailSeq, id, at: now, expires: now + ttl * 1e3, blob };
|
|
14163
|
+
held.push(item);
|
|
14164
|
+
mailbox.set(topic, held);
|
|
14165
|
+
mailChars += blob.length;
|
|
14166
|
+
counted(`${network}|${topic}`, now);
|
|
14167
|
+
counted(network, now);
|
|
14168
|
+
send2(client, JSON.stringify({ type: "dropped", topic, id }));
|
|
14169
|
+
const news = JSON.stringify({ type: "mail", topic, items: [mailItem(item)], more: false });
|
|
14170
|
+
for (const watcher of watchers.get(topic) ?? []) {
|
|
14171
|
+
if (watcher !== client) send2(watcher, news);
|
|
14172
|
+
}
|
|
14173
|
+
}
|
|
14174
|
+
const mailItem = ({ seq, id, at, blob }) => ({ seq, id, at, blob });
|
|
14175
|
+
function countOf(key3, now) {
|
|
14176
|
+
const recent = dropsBy.get(key3);
|
|
14177
|
+
return recent && now - recent.since < DROP_WINDOW_MS ? recent.count : 0;
|
|
14178
|
+
}
|
|
14179
|
+
function counted(key3, now) {
|
|
14180
|
+
const recent = dropsBy.get(key3);
|
|
14181
|
+
if (recent && now - recent.since < DROP_WINDOW_MS) recent.count += 1;
|
|
14182
|
+
else dropsBy.set(key3, { count: 1, since: now });
|
|
14183
|
+
}
|
|
14184
|
+
function purge(client, message) {
|
|
14185
|
+
const { topic, sign, sig } = message;
|
|
14186
|
+
const refuse = (reason) => send2(client, JSON.stringify({ type: "refused", topic, reason }));
|
|
14187
|
+
const nonce = client.nonce;
|
|
14188
|
+
client.nonce = null;
|
|
14189
|
+
const ids = message.ids === void 0 ? null : message.ids;
|
|
14190
|
+
if (!nonce) return refuse("Ask for a challenge first");
|
|
14191
|
+
if (typeof sign !== "string" || !/^[A-Za-z0-9_-]{44}$/.test(sign) || typeof sig !== "string" || sig.length > 200) return refuse("Not a purge");
|
|
14192
|
+
if (ids !== null && (!Array.isArray(ids) || ids.length > MAX_MAIL_PER_TOPIC || !ids.every((id) => typeof id === "string" && id.length <= 64))) {
|
|
14193
|
+
return refuse("Not a purge");
|
|
14194
|
+
}
|
|
14195
|
+
if (createHash("sha256").update(`weave/door-topic/v1|${sign}`).digest("base64url") !== topic) return refuse("That key is not this door\u2019s");
|
|
14196
|
+
let good = false;
|
|
14197
|
+
try {
|
|
14198
|
+
const key3 = createPublicKey({ key: Buffer.concat([PURGE_SPKI_PREFIX, Buffer.from(sign, "base64url")]), format: "der", type: "spki" });
|
|
14199
|
+
const signed = `weave/door-purge/v1|${topic}|${nonce}|${ids ? [...ids].sort().join(",") : "*"}`;
|
|
14200
|
+
good = verify("sha256", Buffer.from(signed), { key: key3, dsaEncoding: "ieee-p1363" }, Buffer.from(sig, "base64url"));
|
|
14201
|
+
} catch {
|
|
14202
|
+
good = false;
|
|
14203
|
+
}
|
|
14204
|
+
if (!good) return refuse("The signature does not check out");
|
|
14205
|
+
const held = mailbox.get(topic) ?? [];
|
|
14206
|
+
const gone = ids === null ? held : held.filter((item) => ids.includes(item.id));
|
|
14207
|
+
for (const item of gone) mailChars -= item.blob.length;
|
|
14208
|
+
const kept = ids === null ? [] : held.filter((item) => !ids.includes(item.id));
|
|
14209
|
+
if (kept.length === 0) mailbox.delete(topic);
|
|
14210
|
+
else mailbox.set(topic, kept);
|
|
14211
|
+
send2(client, JSON.stringify({ type: "purged", topic, count: gone.length }));
|
|
14212
|
+
}
|
|
14213
|
+
function unwatch(client, topic) {
|
|
14214
|
+
client.watching.delete(topic);
|
|
14215
|
+
const watching = watchers.get(topic);
|
|
14216
|
+
watching?.delete(client);
|
|
14217
|
+
if (watching?.size === 0) watchers.delete(topic);
|
|
14218
|
+
}
|
|
14219
|
+
function sweepMail() {
|
|
14220
|
+
const now = Date.now();
|
|
14221
|
+
for (const [topic, held] of mailbox) {
|
|
14222
|
+
const kept = held.filter((item) => item.expires > now);
|
|
14223
|
+
for (const item of held) if (item.expires <= now) mailChars -= item.blob.length;
|
|
14224
|
+
if (kept.length === 0) mailbox.delete(topic);
|
|
14225
|
+
else if (kept.length !== held.length) mailbox.set(topic, kept);
|
|
14226
|
+
}
|
|
14227
|
+
for (const [key3, recent] of dropsBy) {
|
|
14228
|
+
if (now - recent.since >= DROP_WINDOW_MS) dropsBy.delete(key3);
|
|
14229
|
+
}
|
|
14230
|
+
}
|
|
13497
14231
|
function offerTurn(client) {
|
|
13498
14232
|
if (!turn) return;
|
|
13499
14233
|
const network = networkOf(client.ip);
|
|
@@ -13551,6 +14285,7 @@ function createRelay(options = {}) {
|
|
|
13551
14285
|
for (const [network, held] of turnByNetwork) {
|
|
13552
14286
|
if (held.expires <= now) turnByNetwork.delete(network);
|
|
13553
14287
|
}
|
|
14288
|
+
sweepMail();
|
|
13554
14289
|
for (const client of clients) {
|
|
13555
14290
|
if (!client.alive) {
|
|
13556
14291
|
client.ws.terminate();
|
|
@@ -14807,7 +15542,11 @@ import { mkdir as mkdir2, readFile as readFile3, rm, writeFile as writeFile2 } f
|
|
|
14807
15542
|
import os3 from "node:os";
|
|
14808
15543
|
import path4 from "node:path";
|
|
14809
15544
|
|
|
14810
|
-
// ../src/session/
|
|
15545
|
+
// ../core/src/session/stay-signed-in.ts
|
|
15546
|
+
var DAY2 = 24 * 60 * 60 * 1e3;
|
|
15547
|
+
var DURATION2 = { never: 0, "1d": DAY2, "7d": 7 * DAY2, "30d": 30 * DAY2 };
|
|
15548
|
+
|
|
15549
|
+
// ../core/src/session/connect.ts
|
|
14811
15550
|
function grantSigner(grant) {
|
|
14812
15551
|
let token = null;
|
|
14813
15552
|
return {
|
|
@@ -14823,7 +15562,7 @@ function grantSigner(grant) {
|
|
|
14823
15562
|
};
|
|
14824
15563
|
}
|
|
14825
15564
|
|
|
14826
|
-
// ../src/session/agent-link.ts
|
|
15565
|
+
// ../core/src/session/agent-link.ts
|
|
14827
15566
|
var SECRET_BYTES = 16;
|
|
14828
15567
|
var ROOM_PREFIX2 = utf8Encode("weave-agent-link-room-v1");
|
|
14829
15568
|
var KEY_INFO = utf8Encode("weave-agent-link-key-v1");
|
|
@@ -14831,7 +15570,7 @@ var ASK = "agent-link:ask";
|
|
|
14831
15570
|
var HEARD = "agent-link:heard";
|
|
14832
15571
|
var ANSWER = "agent-link:answer";
|
|
14833
15572
|
var DONE = "agent-link:done";
|
|
14834
|
-
var
|
|
15573
|
+
var MAX_NAME2 = 80;
|
|
14835
15574
|
function readAgentCode(input) {
|
|
14836
15575
|
const match = /wv_([A-Za-z0-9_-]{22})(?![A-Za-z0-9_-])/.exec(input);
|
|
14837
15576
|
const secret = match ? base64UrlDecode(match[1]) : null;
|
|
@@ -14898,7 +15637,7 @@ async function acceptAgentLink(params) {
|
|
|
14898
15637
|
done();
|
|
14899
15638
|
};
|
|
14900
15639
|
net.on("peer-connected", (peer) => {
|
|
14901
|
-
void seal2({ did: params.did, name: params.name.slice(0,
|
|
15640
|
+
void seal2({ did: params.did, name: params.name.slice(0, MAX_NAME2) }, key3).then((payload) => {
|
|
14902
15641
|
net.send(peer.did, { type: ASK, from: params.did, payload });
|
|
14903
15642
|
});
|
|
14904
15643
|
});
|