@glyphteck/veyl 0.4.1 → 0.5.0

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/index.js CHANGED
@@ -33490,7 +33490,7 @@ var require_src2 = __commonJS((exports) => {
33490
33490
 
33491
33491
  // ../../node_modules/.bun/@grpc+grpc-js@1.14.3/node_modules/@grpc/grpc-js/build/src/channelz.js
33492
33492
  var require_channelz = __commonJS((exports) => {
33493
- var __dirname = "/Users/zaksorel/glyphteck/veyl/node_modules/.bun/@grpc+grpc-js@1.14.3/node_modules/@grpc/grpc-js/build/src";
33493
+ var __dirname = "/home/runner/work/veyl/veyl/node_modules/.bun/@grpc+grpc-js@1.14.3/node_modules/@grpc/grpc-js/build/src";
33494
33494
  Object.defineProperty(exports, "__esModule", { value: true });
33495
33495
  exports.registerChannelzSocket = exports.registerChannelzServer = exports.registerChannelzSubchannel = exports.registerChannelzChannel = exports.ChannelzCallTrackerStub = exports.ChannelzCallTracker = exports.ChannelzChildrenTrackerStub = exports.ChannelzChildrenTracker = exports.ChannelzTrace = exports.ChannelzTraceStub = undefined;
33496
33496
  exports.unregisterChannelzRef = unregisterChannelzRef;
@@ -38893,7 +38893,7 @@ var require_duration = __commonJS((exports) => {
38893
38893
 
38894
38894
  // ../../node_modules/.bun/@grpc+grpc-js@1.14.3/node_modules/@grpc/grpc-js/build/src/orca.js
38895
38895
  var require_orca = __commonJS((exports) => {
38896
- var __dirname = "/Users/zaksorel/glyphteck/veyl/node_modules/.bun/@grpc+grpc-js@1.14.3/node_modules/@grpc/grpc-js/build/src";
38896
+ var __dirname = "/home/runner/work/veyl/veyl/node_modules/.bun/@grpc+grpc-js@1.14.3/node_modules/@grpc/grpc-js/build/src";
38897
38897
  Object.defineProperty(exports, "__esModule", { value: true });
38898
38898
  exports.OrcaOobMetricsSubchannelWrapper = exports.GRPC_METRICS_HEADER = exports.ServerMetricRecorder = exports.PerRequestMetricRecorder = undefined;
38899
38899
  exports.createOrcaClient = createOrcaClient;
@@ -48427,7 +48427,7 @@ var require_src4 = __commonJS((exports) => {
48427
48427
 
48428
48428
  // ../../node_modules/.bun/@grpc+grpc-js@1.9.15/node_modules/@grpc/grpc-js/build/src/channelz.js
48429
48429
  var require_channelz2 = __commonJS((exports) => {
48430
- var __dirname = "/Users/zaksorel/glyphteck/veyl/node_modules/.bun/@grpc+grpc-js@1.9.15/node_modules/@grpc/grpc-js/build/src";
48430
+ var __dirname = "/home/runner/work/veyl/veyl/node_modules/.bun/@grpc+grpc-js@1.9.15/node_modules/@grpc/grpc-js/build/src";
48431
48431
  Object.defineProperty(exports, "__esModule", { value: true });
48432
48432
  exports.setup = exports.getChannelzServiceDefinition = exports.getChannelzHandlers = exports.unregisterChannelzRef = exports.registerChannelzSocket = exports.registerChannelzServer = exports.registerChannelzSubchannel = exports.registerChannelzChannel = exports.ChannelzCallTracker = exports.ChannelzChildrenTracker = exports.ChannelzTrace = undefined;
48433
48433
  var net_1 = __require("net");
@@ -61203,6 +61203,148 @@ async function openAes(key, iv, ct, aad) {
61203
61203
  return new Uint8Array(pt);
61204
61204
  }
61205
61205
 
61206
+ // ../../shared/crypto/kdf.js
61207
+ "use client";
61208
+ var ROOT_SALT = encoder.encode("GLYPHTECK");
61209
+ function listParts(parts = []) {
61210
+ if (Array.isArray(parts)) {
61211
+ return parts;
61212
+ }
61213
+ return [parts];
61214
+ }
61215
+ function encodeScope(label, parts = []) {
61216
+ return encoder.encode(JSON.stringify([label, ...listParts(parts)]));
61217
+ }
61218
+ function deriveKey(input, label, parts = [], size = 32) {
61219
+ return hkdf(sha256, input, ROOT_SALT, encodeScope(label, parts), size);
61220
+ }
61221
+
61222
+ // ../../shared/crypto/vaultsignature.js
61223
+ "use client";
61224
+ var VAULT_SIGNATURE_PROTOCOL = "veyl-vault-signature-v1";
61225
+ var VAULT_SIGNING_KEY_SCOPE = "vault-signing-key-v1";
61226
+ var VAULT_LEASE_PURPOSE = "vault-lease";
61227
+ var VAULT_SIGNATURE_PURPOSES = Object.freeze({
61228
+ DELETE_ACCOUNT: "delete-account",
61229
+ LOGOUT_ALL_DEVICES: "logout-all-devices",
61230
+ SET_CHAT_IDENTITY: "set-chat-identity",
61231
+ SET_WALLET_IDENTITY: "set-wallet-identity"
61232
+ });
61233
+ var BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
61234
+ function signatureError(code, message, cause = null) {
61235
+ const error = new Error(message);
61236
+ error.code = `vault-signature/${code}`;
61237
+ if (cause)
61238
+ error.cause = cause;
61239
+ return error;
61240
+ }
61241
+ function base64url2(bytes) {
61242
+ const source = toBytes(bytes, "base64url bytes");
61243
+ let encoded = "";
61244
+ for (let index = 0;index < source.length; index += 3) {
61245
+ const a = source[index];
61246
+ const hasB = index + 1 < source.length;
61247
+ const hasC = index + 2 < source.length;
61248
+ const b = hasB ? source[index + 1] : 0;
61249
+ const c = hasC ? source[index + 2] : 0;
61250
+ encoded += BASE64URL_ALPHABET[a >>> 2];
61251
+ encoded += BASE64URL_ALPHABET[(a & 3) << 4 | b >>> 4];
61252
+ if (hasB)
61253
+ encoded += BASE64URL_ALPHABET[(b & 15) << 2 | c >>> 6];
61254
+ if (hasC)
61255
+ encoded += BASE64URL_ALPHABET[c & 63];
61256
+ }
61257
+ return encoded;
61258
+ }
61259
+ function cleanPurpose(value) {
61260
+ const purpose = typeof value === "string" ? value.trim() : "";
61261
+ if (!/^[a-z][a-z0-9-]{0,63}$/u.test(purpose)) {
61262
+ throw signatureError("invalid-purpose", "vault signature purpose required");
61263
+ }
61264
+ return purpose;
61265
+ }
61266
+ function cleanGeneration(value) {
61267
+ const generation = Number(value);
61268
+ if (!Number.isSafeInteger(generation) || generation < 1) {
61269
+ throw signatureError("invalid-generation", "active session generation required");
61270
+ }
61271
+ return generation;
61272
+ }
61273
+ function cleanClientSessionId(value) {
61274
+ const clientSessionId = typeof value === "string" ? value.trim() : "";
61275
+ if (!/^[A-Za-z0-9_-]{24,80}$/u.test(clientSessionId)) {
61276
+ throw signatureError("invalid-client-session", "client session required");
61277
+ }
61278
+ return clientSessionId;
61279
+ }
61280
+ function vaultSigningPublicKey(masterSeed) {
61281
+ const secret = deriveKey(toBytes(masterSeed, "master seed"), VAULT_SIGNING_KEY_SCOPE);
61282
+ try {
61283
+ return toHex(ed25519.getPublicKey(secret));
61284
+ } finally {
61285
+ cleanBytes(secret);
61286
+ }
61287
+ }
61288
+ function createVaultSigner(masterSeed) {
61289
+ const secret = deriveKey(toBytes(masterSeed, "master seed"), VAULT_SIGNING_KEY_SCOPE);
61290
+ const publicKey = toHex(ed25519.getPublicKey(secret));
61291
+ let closed = false;
61292
+ return {
61293
+ publicKey,
61294
+ get closed() {
61295
+ return closed;
61296
+ },
61297
+ sign(bytes) {
61298
+ if (closed) {
61299
+ throw signatureError("signer-closed", "vault signer closed");
61300
+ }
61301
+ return toHex(ed25519.sign(toBytes(bytes, "vault signature bytes"), secret));
61302
+ },
61303
+ close() {
61304
+ if (closed)
61305
+ return;
61306
+ closed = true;
61307
+ cleanBytes(secret);
61308
+ }
61309
+ };
61310
+ }
61311
+ function createVaultSignature(signer, claims, purpose, payload = null, now = Date.now()) {
61312
+ if (!signer?.publicKey || typeof signer?.sign !== "function") {
61313
+ throw signatureError("invalid-signer", "vault signer required");
61314
+ }
61315
+ const uid = typeof claims?.uid === "string" ? claims.uid.trim() : "";
61316
+ if (!uid) {
61317
+ throw signatureError("invalid-uid", "vault signature uid required");
61318
+ }
61319
+ let message;
61320
+ try {
61321
+ message = encoder.encode(JSON.stringify([
61322
+ VAULT_SIGNATURE_PROTOCOL,
61323
+ uid,
61324
+ cleanGeneration(claims?.sessionGeneration),
61325
+ cleanClientSessionId(claims?.clientSessionId),
61326
+ cleanPurpose(purpose),
61327
+ now,
61328
+ base64url2(randomBytes3(24)),
61329
+ payload
61330
+ ]));
61331
+ } catch (error) {
61332
+ if (error?.code)
61333
+ throw error;
61334
+ throw signatureError("message-construction", "vault signature message failed", error);
61335
+ }
61336
+ try {
61337
+ return {
61338
+ message: base64url2(message),
61339
+ signature: signer.sign(message)
61340
+ };
61341
+ } catch (error) {
61342
+ if (error?.code)
61343
+ throw error;
61344
+ throw signatureError("signing", "vault signature signing failed", error);
61345
+ }
61346
+ }
61347
+
61206
61348
  // ../../shared/crypto/seed.js
61207
61349
  "use client";
61208
61350
  var VAULT_SALT = sha256(encoder.encode("vault-v1"));
@@ -61437,21 +61579,22 @@ function getChatSeed(registry) {
61437
61579
  async function encryptSeedWithPassword(seed, pwd, options = {}) {
61438
61580
  const salt = randomBytes3(SALT_BYTES);
61439
61581
  const params = normalizeVaultKdf(options.params);
61440
- const deriveKey = getVaultDeriveKey(options);
61582
+ const deriveKey2 = getVaultDeriveKey(options);
61441
61583
  const registry = normalizeSecretRegistry(options.registry || createSecretRegistry());
61584
+ const vaultSigningPK = vaultSigningPublicKey(seed);
61442
61585
  const sealedRegistry = await sealSecretRegistry(seed, registry);
61443
- const key = await deriveKey(pwd, salt, params);
61586
+ const key = await deriveKey2(pwd, salt, params);
61444
61587
  try {
61445
61588
  const { iv, ct } = await sealAes(key, seed);
61446
- return { crypto: options.crypto || VAULT_CRYPTO, kdf: params, ciphertext: ct, salt, iv, registry: sealedRegistry };
61589
+ return { crypto: options.crypto || VAULT_CRYPTO, kdf: params, ciphertext: ct, salt, iv, registry: sealedRegistry, vaultSigningPK };
61447
61590
  } finally {
61448
61591
  key.fill(0);
61449
61592
  }
61450
61593
  }
61451
61594
  async function decryptSeed(ciphertext, salt, iv, pwd, params = VAULT_KDF, options = {}) {
61452
61595
  const kdf = normalizeVaultKdf(params);
61453
- const deriveKey = getVaultDeriveKey(options);
61454
- const key = await deriveKey(pwd, salt, kdf);
61596
+ const deriveKey2 = getVaultDeriveKey(options);
61597
+ const key = await deriveKey2(pwd, salt, kdf);
61455
61598
  try {
61456
61599
  return await openAes(key, iv, ciphertext);
61457
61600
  } finally {
@@ -61542,6 +61685,196 @@ var WALLET_PENDING_TRANSFER_STALE_RETRY_MS = 2 * MINUTE_MS;
61542
61685
  var WALLET_PENDING_TRANSFER_STUCK_RETRY_MS = 10 * MINUTE_MS;
61543
61686
  var WALLET_TRANSFER_CACHE_WRITE_DELAY_MS = 3 * MS_PER_SECOND;
61544
61687
 
61688
+ // ../../shared/links.js
61689
+ var ROOT_DOMAIN = "glyphteck.com";
61690
+ var domains = Object.freeze({
61691
+ root: ROOT_DOMAIN,
61692
+ rootDev: `dev.${ROOT_DOMAIN}`,
61693
+ veyl: `veyl.${ROOT_DOMAIN}`,
61694
+ veylTest: `test.veyl.${ROOT_DOMAIN}`,
61695
+ veylDev: `dev.veyl.${ROOT_DOMAIN}`
61696
+ });
61697
+ var origins = Object.freeze({
61698
+ root: `https://${domains.root}`,
61699
+ rootDev: `https://${domains.rootDev}`,
61700
+ rootDevWeb: `https://${domains.rootDev}:3001`,
61701
+ veyl: `https://${domains.veyl}`,
61702
+ veylTest: `https://${domains.veylTest}`,
61703
+ veylDev: `https://${domains.veylDev}`,
61704
+ veylDevWeb: `https://${domains.veylDev}:3000`
61705
+ });
61706
+ var links = Object.freeze({
61707
+ root: origins.root,
61708
+ rootDev: origins.rootDev,
61709
+ rootDevWeb: origins.rootDevWeb,
61710
+ veyl: origins.veyl,
61711
+ veylTest: origins.veylTest,
61712
+ veylDev: origins.veylDev,
61713
+ veylDevWeb: origins.veylDevWeb,
61714
+ contact: `mailto:contact@${ROOT_DOMAIN}`,
61715
+ regtestFaucet: "https://app.lightspark.com/regtest-faucet"
61716
+ });
61717
+ var localHosts = Object.freeze([
61718
+ domains.rootDev,
61719
+ domains.veylDev
61720
+ ]);
61721
+ var appDomains = Object.freeze([
61722
+ domains.veyl,
61723
+ domains.veylTest
61724
+ ]);
61725
+ var appLinkDomains = Object.freeze([
61726
+ domains.veyl,
61727
+ domains.veylTest,
61728
+ domains.veylDev
61729
+ ]);
61730
+ var allowedPasskeyOrigins = Object.freeze([
61731
+ origins.root,
61732
+ origins.rootDev,
61733
+ origins.rootDevWeb,
61734
+ origins.veylDev,
61735
+ origins.veylDevWeb,
61736
+ origins.veylTest,
61737
+ origins.veyl
61738
+ ]);
61739
+ var storageCorsOrigins = Object.freeze([
61740
+ origins.rootDevWeb,
61741
+ origins.veylDev,
61742
+ origins.veylDevWeb,
61743
+ origins.veyl,
61744
+ origins.veylTest
61745
+ ]);
61746
+ var webApps = Object.freeze({
61747
+ veyl: Object.freeze({
61748
+ domain: domains.veylDev,
61749
+ origin: origins.veylDevWeb,
61750
+ port: "3000"
61751
+ })
61752
+ });
61753
+
61754
+ // ../../shared/network.js
61755
+ var DEFAULT_NETWORK = "REGTEST";
61756
+ var MAINNET_NETWORK = "MAINNET";
61757
+ var REGTEST_NETWORK = "REGTEST";
61758
+ var MAINNET_DOMAIN = domains.veyl;
61759
+ var REGTEST_DOMAIN = domains.veylTest;
61760
+ function resolveNetwork(env = {}) {
61761
+ const raw = env?.EXPO_PUBLIC_NETWORK ?? env?.NEXT_PUBLIC_NETWORK ?? env?.VEYL_NETWORK ?? env?.NETWORK ?? DEFAULT_NETWORK;
61762
+ return String(raw).toUpperCase();
61763
+ }
61764
+ function getAddressNetwork(address) {
61765
+ const a = String(address ?? "").trim();
61766
+ if (!a)
61767
+ return null;
61768
+ if (/^bcrt1[ac-hj-np-z02-9]{39,87}$/i.test(a))
61769
+ return REGTEST_NETWORK;
61770
+ if (/^bc1[ac-hj-np-z02-9]{39,87}$/i.test(a))
61771
+ return MAINNET_NETWORK;
61772
+ if (/^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(a))
61773
+ return MAINNET_NETWORK;
61774
+ return null;
61775
+ }
61776
+ function isAddressOnNetwork(address, network) {
61777
+ const detected = getAddressNetwork(address);
61778
+ return detected !== null && detected === String(network ?? "").toUpperCase();
61779
+ }
61780
+
61781
+ // ../../shared/settings.js
61782
+ var SEND_ON_SCAN_ENABLED = false;
61783
+ var WALLET_NETWORKS = new Set([MAINNET_NETWORK, REGTEST_NETWORK]);
61784
+ var defaultSettings = {
61785
+ glass: true,
61786
+ moneyFormat: "usd",
61787
+ ghostWallet: true,
61788
+ showChatPreviews: true,
61789
+ sendOnScan: false,
61790
+ confirmSend: false,
61791
+ faceID: null,
61792
+ walletNetwork: null,
61793
+ autolock: {
61794
+ timer: "never",
61795
+ onHide: false,
61796
+ onBlur: false,
61797
+ onBackground: false
61798
+ }
61799
+ };
61800
+ function normalizeWalletNetworkSetting(value) {
61801
+ if (value == null)
61802
+ return null;
61803
+ const network = String(value).trim().toUpperCase();
61804
+ if (!WALLET_NETWORKS.has(network)) {
61805
+ throw new Error("bad walletNetwork");
61806
+ }
61807
+ return network;
61808
+ }
61809
+ function normalizeAutolock(autolock, base = defaultSettings.autolock) {
61810
+ if (autolock !== undefined && (!autolock || typeof autolock !== "object" || Array.isArray(autolock))) {
61811
+ throw new Error("bad autolock");
61812
+ }
61813
+ const next = {
61814
+ ...defaultSettings.autolock,
61815
+ ...base || {},
61816
+ ...autolock || {}
61817
+ };
61818
+ if (next.timer !== "never" && (!Number.isInteger(next.timer) || next.timer < AUTOLOCK_MIN_MINUTES || next.timer > AUTOLOCK_MAX_MINUTES)) {
61819
+ throw new Error("bad timer");
61820
+ }
61821
+ if (typeof next.onHide !== "boolean") {
61822
+ throw new Error("bad onHide");
61823
+ }
61824
+ if (typeof next.onBlur !== "boolean") {
61825
+ throw new Error("bad onBlur");
61826
+ }
61827
+ if (typeof next.onBackground !== "boolean") {
61828
+ throw new Error("bad onBackground");
61829
+ }
61830
+ return next;
61831
+ }
61832
+ function normalizeSettings(settings, base = defaultSettings) {
61833
+ if (!settings || typeof settings !== "object" || Array.isArray(settings)) {
61834
+ throw new Error("settings object required");
61835
+ }
61836
+ const current = {
61837
+ ...defaultSettings,
61838
+ ...base || {},
61839
+ autolock: {
61840
+ ...defaultSettings.autolock,
61841
+ ...base?.autolock || {}
61842
+ }
61843
+ };
61844
+ const next = {
61845
+ ...current,
61846
+ ...settings,
61847
+ ghostWallet: defaultSettings.ghostWallet
61848
+ };
61849
+ next.autolock = normalizeAutolock(settings.autolock, current.autolock);
61850
+ next.walletNetwork = normalizeWalletNetworkSetting(next.walletNetwork);
61851
+ if (!["btc", "usd", "sats"].includes(next.moneyFormat)) {
61852
+ throw new Error("bad moneyFormat");
61853
+ }
61854
+ if (typeof next.glass !== "boolean") {
61855
+ throw new Error("glass must be boolean");
61856
+ }
61857
+ if (typeof next.ghostWallet !== "boolean") {
61858
+ throw new Error("ghostWallet must be boolean");
61859
+ }
61860
+ if (typeof next.showChatPreviews !== "boolean") {
61861
+ throw new Error("showChatPreviews must be boolean");
61862
+ }
61863
+ if (typeof next.sendOnScan !== "boolean") {
61864
+ throw new Error("sendOnScan must be boolean");
61865
+ }
61866
+ if (!SEND_ON_SCAN_ENABLED) {
61867
+ next.sendOnScan = false;
61868
+ }
61869
+ if (typeof next.confirmSend !== "boolean") {
61870
+ throw new Error("confirmSend must be boolean");
61871
+ }
61872
+ if (next.faceID !== null && typeof next.faceID !== "boolean") {
61873
+ throw new Error("faceID must be boolean or null");
61874
+ }
61875
+ return next;
61876
+ }
61877
+
61545
61878
  // ../../shared/crypto/canonical.js
61546
61879
  "use client";
61547
61880
  function isPlainObject(value) {
@@ -61587,22 +61920,6 @@ function canonicalBytes(value, label = "value") {
61587
61920
  return encoder.encode(canonicalJson(value, label));
61588
61921
  }
61589
61922
 
61590
- // ../../shared/crypto/kdf.js
61591
- "use client";
61592
- var ROOT_SALT = encoder.encode("GLYPHTECK");
61593
- function listParts(parts = []) {
61594
- if (Array.isArray(parts)) {
61595
- return parts;
61596
- }
61597
- return [parts];
61598
- }
61599
- function encodeScope(label, parts = []) {
61600
- return encoder.encode(JSON.stringify([label, ...listParts(parts)]));
61601
- }
61602
- function deriveKey(input, label, parts = [], size = 32) {
61603
- return hkdf(sha256, input, ROOT_SALT, encodeScope(label, parts), size);
61604
- }
61605
-
61606
61923
  // ../../node_modules/.bun/@noble+ciphers@2.2.0/node_modules/@noble/ciphers/_arx.js
61607
61924
  var encodeStr = (str) => Uint8Array.from(str.split(""), (c) => c.charCodeAt(0));
61608
61925
  var sigma16_32 = /* @__PURE__ */ (() => swap32IfBE2(u322(encodeStr("expand 16-byte k"))))();
@@ -62370,91 +62687,6 @@ var unpackBodyData = (bytes, nonceBytes = BOX_NONCE_BYTES) => {
62370
62687
  return { nonce: packed.nonce, ct: packed.ct };
62371
62688
  };
62372
62689
 
62373
- // ../../shared/settings.js
62374
- var SEND_ON_SCAN_ENABLED = false;
62375
- var defaultSettings = {
62376
- glass: true,
62377
- moneyFormat: "usd",
62378
- ghostWallet: true,
62379
- showChatPreviews: true,
62380
- sendOnScan: false,
62381
- confirmSend: false,
62382
- faceID: null,
62383
- autolock: {
62384
- timer: "never",
62385
- onHide: false,
62386
- onBlur: false,
62387
- onBackground: false
62388
- }
62389
- };
62390
- function normalizeAutolock(autolock, base = defaultSettings.autolock) {
62391
- if (autolock !== undefined && (!autolock || typeof autolock !== "object" || Array.isArray(autolock))) {
62392
- throw new Error("bad autolock");
62393
- }
62394
- const next = {
62395
- ...defaultSettings.autolock,
62396
- ...base || {},
62397
- ...autolock || {}
62398
- };
62399
- if (next.timer !== "never" && (!Number.isInteger(next.timer) || next.timer < AUTOLOCK_MIN_MINUTES || next.timer > AUTOLOCK_MAX_MINUTES)) {
62400
- throw new Error("bad timer");
62401
- }
62402
- if (typeof next.onHide !== "boolean") {
62403
- throw new Error("bad onHide");
62404
- }
62405
- if (typeof next.onBlur !== "boolean") {
62406
- throw new Error("bad onBlur");
62407
- }
62408
- if (typeof next.onBackground !== "boolean") {
62409
- throw new Error("bad onBackground");
62410
- }
62411
- return next;
62412
- }
62413
- function normalizeSettings(settings, base = defaultSettings) {
62414
- if (!settings || typeof settings !== "object" || Array.isArray(settings)) {
62415
- throw new Error("settings object required");
62416
- }
62417
- const current = {
62418
- ...defaultSettings,
62419
- ...base || {},
62420
- autolock: {
62421
- ...defaultSettings.autolock,
62422
- ...base?.autolock || {}
62423
- }
62424
- };
62425
- const next = {
62426
- ...current,
62427
- ...settings,
62428
- ghostWallet: defaultSettings.ghostWallet
62429
- };
62430
- next.autolock = normalizeAutolock(settings.autolock, current.autolock);
62431
- if (!["btc", "usd", "sats"].includes(next.moneyFormat)) {
62432
- throw new Error("bad moneyFormat");
62433
- }
62434
- if (typeof next.glass !== "boolean") {
62435
- throw new Error("glass must be boolean");
62436
- }
62437
- if (typeof next.ghostWallet !== "boolean") {
62438
- throw new Error("ghostWallet must be boolean");
62439
- }
62440
- if (typeof next.showChatPreviews !== "boolean") {
62441
- throw new Error("showChatPreviews must be boolean");
62442
- }
62443
- if (typeof next.sendOnScan !== "boolean") {
62444
- throw new Error("sendOnScan must be boolean");
62445
- }
62446
- if (!SEND_ON_SCAN_ENABLED) {
62447
- next.sendOnScan = false;
62448
- }
62449
- if (typeof next.confirmSend !== "boolean") {
62450
- throw new Error("confirmSend must be boolean");
62451
- }
62452
- if (next.faceID !== null && typeof next.faceID !== "boolean") {
62453
- throw new Error("faceID must be boolean or null");
62454
- }
62455
- return next;
62456
- }
62457
-
62458
62690
  // ../../shared/utils/text.js
62459
62691
  function cleanText(value) {
62460
62692
  return typeof value === "string" ? value.trim() : "";
@@ -63043,7 +63275,13 @@ function walletPubkey(value) {
63043
63275
  }
63044
63276
  return key;
63045
63277
  }
63046
- function closeSessionResources({ wallet, chatPrivateKey, localCache } = {}) {
63278
+ function closeSessionResources({ wallet, chatPrivateKey, localCache, vaultAccess, vaultSigner } = {}) {
63279
+ try {
63280
+ vaultAccess?.close?.();
63281
+ } catch {}
63282
+ try {
63283
+ vaultSigner?.close?.();
63284
+ } catch {}
63047
63285
  try {
63048
63286
  localCache?.close?.();
63049
63287
  } catch {}
@@ -63085,6 +63323,8 @@ function createAccountSession(resources = {}) {
63085
63323
  session.wallet = null;
63086
63324
  session.chatPrivateKey = null;
63087
63325
  session.localCache = null;
63326
+ session.vaultAccess = null;
63327
+ session.vaultSigner = null;
63088
63328
  }
63089
63329
  };
63090
63330
  return session;
@@ -63092,7 +63332,7 @@ function createAccountSession(resources = {}) {
63092
63332
  function closeAccountSession(session) {
63093
63333
  session?.close?.();
63094
63334
  }
63095
- async function openAccountSessionFromSecrets(walletEntropy, chatSeed, { SparkWallet, network, accountNumber } = {}) {
63335
+ async function openAccountSessionFromSecrets(walletEntropy, chatSeed, masterSeed, { SparkWallet, network, accountNumber, cloud, uid } = {}) {
63096
63336
  if (!SparkWallet) {
63097
63337
  throw new Error("SparkWallet missing");
63098
63338
  }
@@ -63103,6 +63343,8 @@ async function openAccountSessionFromSecrets(walletEntropy, chatSeed, { SparkWal
63103
63343
  let wallet = null;
63104
63344
  let chatPrivateKey = null;
63105
63345
  let chatPubKey = null;
63346
+ let vaultSigner = null;
63347
+ let vaultAccess = null;
63106
63348
  try {
63107
63349
  const result = await SparkWallet.initialize({
63108
63350
  mnemonicOrSeed: walletMnemonic,
@@ -63121,15 +63363,24 @@ async function openAccountSessionFromSecrets(walletEntropy, chatSeed, { SparkWal
63121
63363
  const chatKeyPair = getKeyPair(chatSeed);
63122
63364
  chatPrivateKey = chatKeyPair.priv;
63123
63365
  chatPubKey = chatKeyPair.pub;
63366
+ vaultSigner = createVaultSigner(masterSeed);
63367
+ if (cloud) {
63368
+ if (!uid)
63369
+ throw new Error("vault signature uid missing");
63370
+ vaultAccess = await cloud.auth.vaultSignature.open(vaultSigner);
63371
+ }
63124
63372
  return createAccountSession({
63125
63373
  wallet,
63126
63374
  walletPK,
63127
63375
  chatPK: toHex(chatPubKey),
63128
63376
  chatPrivateKey,
63129
- network
63377
+ network,
63378
+ vaultSigner,
63379
+ vaultAccess,
63380
+ vaultSigningPK: vaultSigner.publicKey
63130
63381
  });
63131
63382
  } catch (error) {
63132
- closeSessionResources({ wallet, chatPrivateKey });
63383
+ closeSessionResources({ wallet, chatPrivateKey, vaultAccess, vaultSigner });
63133
63384
  throw error;
63134
63385
  } finally {
63135
63386
  cleanBytes(chatSeed, chatPubKey);
@@ -63140,7 +63391,7 @@ async function openRegistryAccountSession(masterSeed, registryEnvelope, options
63140
63391
  const walletEntropy = getDefaultWalletEntropy(registry);
63141
63392
  const chatSeed = getChatSeed(registry);
63142
63393
  try {
63143
- return await openAccountSessionFromSecrets(walletEntropy, chatSeed, options);
63394
+ return await openAccountSessionFromSecrets(walletEntropy, chatSeed, masterSeed, options);
63144
63395
  } finally {
63145
63396
  cleanBytes(walletEntropy, chatSeed);
63146
63397
  }
@@ -87291,7 +87542,7 @@ var base642 = hasBase64Builtin2 ? {
87291
87542
  }
87292
87543
  } : chain2(radix22(6), alphabet2("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), padding2(6), join2(""));
87293
87544
  var base64nopad2 = chain2(radix22(6), alphabet2("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), join2(""));
87294
- var base64url2 = hasBase64Builtin2 ? {
87545
+ var base64url3 = hasBase64Builtin2 ? {
87295
87546
  encode(b) {
87296
87547
  abytes6(b);
87297
87548
  return b.toBase64({ alphabet: "base64url" });
@@ -115539,7 +115790,7 @@ async function openOwnChatEntry(chatPrivKey, entryId, body) {
115539
115790
  try {
115540
115791
  const { nonce, ct } = unpackBodyData(body);
115541
115792
  const entry = await openJson(key, nonce, ct, entryAad(entryId));
115542
- if (entry?.v !== CHAT_ENTRY_VERSION || !entry?.linkId || !entry?.chatId || !entry?.peerChatPK) {
115793
+ if (entry?.v !== CHAT_ENTRY_VERSION || !entry?.linkId || !entry?.chatId || !entry?.peerChatPK || !cleanText(entry?.peerUid)) {
115543
115794
  throw new Error("invalid chat entry");
115544
115795
  }
115545
115796
  const { actors, ...entryFields } = entry;
@@ -115553,12 +115804,16 @@ async function openOwnChatEntry(chatPrivKey, entryId, body) {
115553
115804
  }
115554
115805
  }
115555
115806
  function makeOwnChatEntry(pair, fields = {}) {
115807
+ const peerUid = cleanText(fields.peerUid);
115808
+ if (!peerUid) {
115809
+ throw new Error("chat peer uid required");
115810
+ }
115556
115811
  return {
115557
115812
  protocol: CHAT_PROTOCOL_VERSION,
115558
115813
  linkId: pair.linkId,
115559
115814
  chatId: pair.chatId,
115560
115815
  peerChatPK: pair.peerChatPK,
115561
- peerUid: cleanText(fields.peerUid),
115816
+ peerUid,
115562
115817
  signingKeysByChatKey: {
115563
115818
  ...fields.signingKeysByChatKey || {},
115564
115819
  [pair.chatPK]: pair.signingKey.publicKey,
@@ -115582,72 +115837,6 @@ var SYSTEM_RETENTION_KIND = "retention";
115582
115837
  var DEFAULT_REACTION_EMOJI = "❤️";
115583
115838
  var HOLD_VISIBLE_KEY = "__holdVisible";
115584
115839
 
115585
- // ../../shared/links.js
115586
- var ROOT_DOMAIN = "glyphteck.com";
115587
- var domains = Object.freeze({
115588
- root: ROOT_DOMAIN,
115589
- rootDev: `dev.${ROOT_DOMAIN}`,
115590
- veyl: `veyl.${ROOT_DOMAIN}`,
115591
- veylTest: `test.veyl.${ROOT_DOMAIN}`,
115592
- veylDev: `dev.veyl.${ROOT_DOMAIN}`
115593
- });
115594
- var origins = Object.freeze({
115595
- root: `https://${domains.root}`,
115596
- rootDev: `https://${domains.rootDev}`,
115597
- rootDevWeb: `https://${domains.rootDev}:3001`,
115598
- veyl: `https://${domains.veyl}`,
115599
- veylTest: `https://${domains.veylTest}`,
115600
- veylDev: `https://${domains.veylDev}`,
115601
- veylDevWeb: `https://${domains.veylDev}:3000`
115602
- });
115603
- var links = Object.freeze({
115604
- root: origins.root,
115605
- rootDev: origins.rootDev,
115606
- rootDevWeb: origins.rootDevWeb,
115607
- veyl: origins.veyl,
115608
- veylTest: origins.veylTest,
115609
- veylDev: origins.veylDev,
115610
- veylDevWeb: origins.veylDevWeb,
115611
- contact: `mailto:contact@${ROOT_DOMAIN}`,
115612
- regtestFaucet: "https://app.lightspark.com/regtest-faucet"
115613
- });
115614
- var localHosts = Object.freeze([
115615
- domains.rootDev,
115616
- domains.veylDev
115617
- ]);
115618
- var appDomains = Object.freeze([
115619
- domains.veyl,
115620
- domains.veylTest
115621
- ]);
115622
- var appLinkDomains = Object.freeze([
115623
- domains.veyl,
115624
- domains.veylTest,
115625
- domains.veylDev
115626
- ]);
115627
- var allowedPasskeyOrigins = Object.freeze([
115628
- origins.root,
115629
- origins.rootDev,
115630
- origins.rootDevWeb,
115631
- origins.veylDev,
115632
- origins.veylDevWeb,
115633
- origins.veylTest,
115634
- origins.veyl
115635
- ]);
115636
- var storageCorsOrigins = Object.freeze([
115637
- origins.rootDevWeb,
115638
- origins.veylDev,
115639
- origins.veylDevWeb,
115640
- origins.veyl,
115641
- origins.veylTest
115642
- ]);
115643
- var webApps = Object.freeze({
115644
- veyl: Object.freeze({
115645
- domain: domains.veylDev,
115646
- origin: origins.veylDevWeb,
115647
- port: "3000"
115648
- })
115649
- });
115650
-
115651
115840
  // ../../shared/chat/messages/text.js
115652
115841
  var DOMAIN_LABEL_PATTERN = "[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?";
115653
115842
  var DOMAIN_TLD_PATTERN = "(?:[a-z]{2,63}|xn--[a-z0-9-]{2,59})";
@@ -116308,6 +116497,15 @@ function getDisplayMessages(messages, selfChatPublicKey, peerChatPublicKey, opti
116308
116497
  }
116309
116498
 
116310
116499
  // ../../shared/chat/messages/compose.js
116500
+ var REQUEST_NETWORKS = new Set([MAINNET_NETWORK, REGTEST_NETWORK]);
116501
+ function requestNetwork(message) {
116502
+ const network = String(message?.network ?? "").trim().toUpperCase();
116503
+ return REQUEST_NETWORKS.has(network) ? network : null;
116504
+ }
116505
+ function isRequestOnNetwork(message, network) {
116506
+ const request = requestNetwork(message);
116507
+ return request !== null && request === String(network ?? "").trim().toUpperCase();
116508
+ }
116311
116509
  function setReply(msg, replyId) {
116312
116510
  const nextReplyId = String(replyId ?? "").trim();
116313
116511
  if (!nextReplyId) {
@@ -116318,12 +116516,16 @@ function setReply(msg, replyId) {
116318
116516
  r: nextReplyId
116319
116517
  };
116320
116518
  }
116321
- function makeReq(amount) {
116519
+ function makeReq(amount, network) {
116322
116520
  const a = String(amount ?? "").trim();
116323
116521
  if (!a) {
116324
116522
  throw new Error("amount required");
116325
116523
  }
116326
- return { t: "req", a };
116524
+ const request = requestNetwork({ network });
116525
+ if (!request) {
116526
+ throw new Error("wallet network required");
116527
+ }
116528
+ return { t: "req", a, network: request };
116327
116529
  }
116328
116530
  function setReqTx(msg, tx) {
116329
116531
  const a = String(msg?.a ?? "").trim();
@@ -116331,6 +116533,10 @@ function setReqTx(msg, tx) {
116331
116533
  if (!a || !nextTx) {
116332
116534
  throw new Error("request and tx required");
116333
116535
  }
116536
+ const network = requestNetwork(msg);
116537
+ if (!network) {
116538
+ throw new Error("request wallet network missing");
116539
+ }
116334
116540
  return {
116335
116541
  ...typeof msg?.s === "string" && msg.s ? { s: msg.s } : {},
116336
116542
  ...typeof msg?.cid === "string" && msg.cid ? { cid: msg.cid } : {},
@@ -116338,6 +116544,7 @@ function setReqTx(msg, tx) {
116338
116544
  ...retentionPatch(msg),
116339
116545
  t: "req",
116340
116546
  a,
116547
+ network,
116341
116548
  tx: nextTx
116342
116549
  };
116343
116550
  }
@@ -116754,7 +116961,7 @@ function hasPositiveTimestamp(value) {
116754
116961
  return timestampMs(value, null, { positive: true }) != null;
116755
116962
  }
116756
116963
  function isCurrentChatCacheEntry(chat) {
116757
- return !!chat && isHex64(chat.id) && isHex64(chat.linkId) && isHex32(chat.entryId) && isHex64(chat.peerChatPK) && hasPositiveTimestamp(chat.ts);
116964
+ return !!chat && isHex64(chat.id) && isHex64(chat.linkId) && isHex32(chat.entryId) && isHex64(chat.peerChatPK) && !!cleanText(chat.peerUid) && hasPositiveTimestamp(chat.ts);
116758
116965
  }
116759
116966
  function isCurrentUserChatEntry(chat) {
116760
116967
  return isCurrentChatCacheEntry(chat);
@@ -117891,6 +118098,7 @@ function simplifyAccountTransfer(tx) {
117891
118098
  }
117892
118099
 
117893
118100
  // ../../shared/account/actions/chat.js
118101
+ var DELETE_ALL_CHATS_BATCH_SIZE = 400;
117894
118102
  function createChatActions({ readCloud, readSession, inbox, peers }) {
117895
118103
  function writeOptions(active, chat, options = {}) {
117896
118104
  return {
@@ -118161,6 +118369,19 @@ function createChatActions({ readCloud, readSession, inbox, peers }) {
118161
118369
  deleted: true
118162
118370
  };
118163
118371
  }
118372
+ async function deleteAll() {
118373
+ const active = await readSession();
118374
+ const chats = await inbox.drain(active);
118375
+ const targets = chats.map((chat) => ({
118376
+ chatId: chat.id,
118377
+ entryId: chat.entryId || ownChatEntryId(active.chatPrivateKey, chat.id),
118378
+ ...chat.linkId ? { linkId: chat.linkId } : {}
118379
+ }));
118380
+ for (let index = 0;index < targets.length; index += DELETE_ALL_CHATS_BATCH_SIZE) {
118381
+ await readCloud().chat.delete(targets.slice(index, index + DELETE_ALL_CHATS_BATCH_SIZE), { cleanup: false });
118382
+ }
118383
+ return targets.length;
118384
+ }
118164
118385
  async function retention(peer, value, options = {}) {
118165
118386
  const active = await readSession();
118166
118387
  const chat = await chatForPeer(peer, options);
@@ -118192,6 +118413,7 @@ function createChatActions({ readCloud, readSession, inbox, peers }) {
118192
118413
  unsave,
118193
118414
  deleteMessage,
118194
118415
  deleteChat,
118416
+ deleteAll,
118195
118417
  retention
118196
118418
  };
118197
118419
  }
@@ -118214,7 +118436,14 @@ function createInboxSync({ readCloud }) {
118214
118436
  }
118215
118437
  return { chats: current, processed };
118216
118438
  }
118217
- return { load, sync };
118439
+ async function drain(active, chats = null) {
118440
+ let current = Array.isArray(chats) ? chats : await load(active, 50);
118441
+ while (await processInbox(readCloud(), active.uid, active.chatPK, active.chatPrivateKey, { currentChats: current })) {
118442
+ current = await load(active, Math.max(current.length, 50));
118443
+ }
118444
+ return current;
118445
+ }
118446
+ return { drain, load, sync };
118218
118447
  }
118219
118448
 
118220
118449
  // ../../shared/navigation/params.js
@@ -118491,14 +118720,9 @@ async function claimIncomingTransfersById(wallet, transfers = [], { onClaimed, e
118491
118720
  markDone(diag, "wallet.pendingTxs.claim.sdk", claimStartedAt, { count: ids.length, mode: "all", claimed: claimedIds2.length });
118492
118721
  } catch (error) {
118493
118722
  markError(diag, "wallet.pendingTxs.claim.sdk", claimStartedAt, error, { count: ids.length, mode: "all" });
118494
- console.debug?.("could not claim pending transfers", error?.message ?? error);
118495
- }
118496
- try {
118497
- const pendingIds2 = await queryPendingTransferIds(wallet, ids, diag);
118498
- claimedIds2.push(...ids.filter((id) => !pendingIds2.has(id)));
118499
- } catch (error) {
118500
- console.debug?.("could not verify pending transfers", error?.message ?? error);
118501
118723
  }
118724
+ const pendingIds2 = await queryPendingTransferIds(wallet, ids, diag).catch(() => new Set);
118725
+ claimedIds2.push(...ids.filter((id) => !pendingIds2.has(id)));
118502
118726
  const uniqueClaimedIds = [...new Set(claimedIds2)];
118503
118727
  if (uniqueClaimedIds.length) {
118504
118728
  onClaimed?.(uniqueClaimedIds);
@@ -118541,7 +118765,6 @@ async function claimIncomingTransfersById(wallet, transfers = [], { onClaimed, e
118541
118765
  }
118542
118766
  } catch (error) {
118543
118767
  markError(diag, "wallet.pendingTxs.claim.sdk", claimStartedAt, error, { count: 1 });
118544
- console.debug?.("could not claim pending transfer", error?.message ?? error);
118545
118768
  }
118546
118769
  await yieldToUi();
118547
118770
  }
@@ -118586,33 +118809,6 @@ async function claimPendingIncomingTransfers(wallet, { limit = WALLET_PENDING_TR
118586
118809
  });
118587
118810
  }
118588
118811
 
118589
- // ../../shared/network.js
118590
- var DEFAULT_NETWORK = "REGTEST";
118591
- var MAINNET_NETWORK = "MAINNET";
118592
- var REGTEST_NETWORK = "REGTEST";
118593
- var MAINNET_DOMAIN = domains.veyl;
118594
- var REGTEST_DOMAIN = domains.veylTest;
118595
- function resolveNetwork(env = {}) {
118596
- const raw = env?.EXPO_PUBLIC_NETWORK ?? env?.NEXT_PUBLIC_NETWORK ?? env?.VEYL_NETWORK ?? env?.NETWORK ?? DEFAULT_NETWORK;
118597
- return String(raw).toUpperCase();
118598
- }
118599
- function getAddressNetwork(address) {
118600
- const a = String(address ?? "").trim();
118601
- if (!a)
118602
- return null;
118603
- if (/^bcrt1[ac-hj-np-z02-9]{39,87}$/i.test(a))
118604
- return REGTEST_NETWORK;
118605
- if (/^bc1[ac-hj-np-z02-9]{39,87}$/i.test(a))
118606
- return MAINNET_NETWORK;
118607
- if (/^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(a))
118608
- return MAINNET_NETWORK;
118609
- return null;
118610
- }
118611
- function isAddressOnNetwork(address, network) {
118612
- const detected = getAddressNetwork(address);
118613
- return detected !== null && detected === String(network ?? "").toUpperCase();
118614
- }
118615
-
118616
118812
  // ../../shared/wallet/deposits.js
118617
118813
  var AUTO_CLAIM_MAX_FEE_SATS = WALLET_AUTO_CLAIM_MAX_FEE_SATS;
118618
118814
  var CLAIM_PAGE_SIZE = WALLET_CLAIM_PAGE_SIZE;
@@ -118707,9 +118903,7 @@ async function claimStaticDeposits(wallet, { getFundingAddress, maxFeeSats = AUT
118707
118903
  if (claim) {
118708
118904
  claimed = true;
118709
118905
  }
118710
- } catch (error) {
118711
- console.debug?.("could not claim deposit", key, error?.message ?? error);
118712
- }
118906
+ } catch {}
118713
118907
  }
118714
118908
  return claimed;
118715
118909
  }
@@ -119439,7 +119633,8 @@ function createMoneyActions({ readCloud, readSession, resolvePeer, sendPayload }
119439
119633
  };
119440
119634
  }
119441
119635
  async function request(peer, sats, options = {}) {
119442
- return sendPayload(peer, makeReq(String(cleanPositiveSats(sats))), options);
119636
+ const active = await readSession();
119637
+ return sendPayload(peer, makeReq(String(cleanPositiveSats(sats)), active.network), options);
119443
119638
  }
119444
119639
  async function pay(requestId, options = {}) {
119445
119640
  const active = await readSession();
@@ -119462,6 +119657,9 @@ function createMoneyActions({ readCloud, readSession, resolvePeer, sendPayload }
119462
119657
  if (!isPeerMsg(message, active.chatPK)) {
119463
119658
  throw new Error("cannot pay your own request");
119464
119659
  }
119660
+ if (!isRequestOnNetwork(message, active.network)) {
119661
+ throw new Error(`switch wallet network to ${requestNetwork(message)?.toLowerCase() || "the request network"} before paying`);
119662
+ }
119465
119663
  const amountSats = cleanPositiveSats(message.a);
119466
119664
  const profile = await resolvePeer(message.from || message.s || chat.peerChatPK);
119467
119665
  if (!profile.walletPK) {
@@ -151933,9 +152131,6 @@ function avatarUrlWithVersion(url, version8) {
151933
152131
  // ../../shared/community.js
151934
152132
  var COMMUNITY_RULES_VERSION = "2026-05-11.1";
151935
152133
  var COMMUNITY_RULES_DATE = "2026-05-11";
151936
- function hasCurrentCommunityRules(user) {
151937
- return user?.communityRulesVersion === COMMUNITY_RULES_VERSION && !!user?.communityRulesAcceptedAt;
151938
- }
151939
152134
  var COMMUNITY_SECTIONS = [
151940
152135
  {
151941
152136
  title: "Use veyl directly and lawfully",
@@ -152030,6 +152225,15 @@ function requireUid(uid) {
152030
152225
  function recordFromDoc(snap) {
152031
152226
  return snap?.exists?.() ? { ...snap.data(), id: snap.id } : null;
152032
152227
  }
152228
+ function authSessionGeneration(value) {
152229
+ const generation = Number(value);
152230
+ return Number.isSafeInteger(generation) && generation > 0 ? generation : null;
152231
+ }
152232
+ function vaultSignatureError(code, message) {
152233
+ const error = new Error(message);
152234
+ error.code = `vault-signature/${code}`;
152235
+ return error;
152236
+ }
152033
152237
  function recordsFromSnapshot(snapshot) {
152034
152238
  return snapshot.docs.map(recordFromDoc).filter(Boolean);
152035
152239
  }
@@ -152106,6 +152310,10 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
152106
152310
  if (!db) {
152107
152311
  throw new Error("createFirebaseCloud requires db");
152108
152312
  }
152313
+ let activeVaultSigner = null;
152314
+ let vaultLeaseExpiresAt = 0;
152315
+ let vaultLeaseRequest = null;
152316
+ let vaultOpenRequest = null;
152109
152317
  function pageTs(value) {
152110
152318
  if (value instanceof Timestamp2) {
152111
152319
  return value;
@@ -152262,34 +152470,179 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
152262
152470
  function watchAuth(onUpdate, onError) {
152263
152471
  return onAuthStateChanged(requireAuth(), onUpdate, onError);
152264
152472
  }
152473
+ function watchAuthSession(uid, onUpdate, onError) {
152474
+ requireUid(uid);
152475
+ const user = requireAuth().currentUser;
152476
+ let active = true;
152477
+ let unsubscribe = () => {};
152478
+ if (!user || user.uid !== uid) {
152479
+ Promise.resolve().then(() => {
152480
+ if (active)
152481
+ onUpdate?.({ active: false, generation: null, currentGeneration: null });
152482
+ });
152483
+ return () => {
152484
+ active = false;
152485
+ };
152486
+ }
152487
+ user.getIdTokenResult().then((result) => {
152488
+ if (!active)
152489
+ return;
152490
+ const generation = authSessionGeneration(result?.claims?.sessionGeneration);
152491
+ if (!generation) {
152492
+ onUpdate?.({ active: false, generation: null, currentGeneration: null });
152493
+ return;
152494
+ }
152495
+ unsubscribe = onSnapshot(doc(db, "auth_sessions", uid), (snap) => {
152496
+ if (!active)
152497
+ return;
152498
+ const currentGeneration = authSessionGeneration(snap.data()?.generation);
152499
+ onUpdate?.({
152500
+ active: currentGeneration === generation,
152501
+ generation,
152502
+ currentGeneration
152503
+ });
152504
+ }, (error) => {
152505
+ if (active)
152506
+ onError?.(error);
152507
+ });
152508
+ }).catch((error) => {
152509
+ if (active)
152510
+ onError?.(error);
152511
+ });
152512
+ return () => {
152513
+ active = false;
152514
+ unsubscribe();
152515
+ };
152516
+ }
152265
152517
  async function logout() {
152266
152518
  await signOut(requireAuth());
152267
152519
  return true;
152268
152520
  }
152269
152521
  async function logoutDevices() {
152270
- await callFunction("logoutDevices");
152522
+ await callVaultFunction("logoutDevices", VAULT_SIGNATURE_PURPOSES.LOGOUT_ALL_DEVICES);
152271
152523
  return true;
152272
152524
  }
152273
- async function readOnboarding(uid) {
152274
- requireUid(uid);
152275
- const [profileDoc, seedDoc, userDoc] = await Promise.all([
152276
- getDoc(doc(db, "profiles", uid)),
152277
- getDoc(doc(db, "seeds", uid)),
152278
- getDoc(doc(db, "users", uid))
152279
- ]);
152280
- const profile = profileDoc.exists() ? profileDoc.data() : null;
152281
- const user = userDoc.exists() ? userDoc.data() : null;
152525
+ async function clientSessionClaims() {
152526
+ const user = requireAuth().currentUser;
152527
+ if (!user?.uid) {
152528
+ throw new Error("signed in user required");
152529
+ }
152530
+ const token = await user.getIdTokenResult();
152531
+ const sessionGeneration = authSessionGeneration(token?.claims?.sessionGeneration);
152532
+ const clientSessionId = token?.claims?.clientSessionId;
152533
+ if (!sessionGeneration || typeof clientSessionId !== "string" || !clientSessionId) {
152534
+ throw new Error("invalid client session");
152535
+ }
152282
152536
  return {
152283
- uid,
152284
- hasUsername: !!profile?.username,
152285
- hasAvatarEntry: !!profile && Object.prototype.hasOwnProperty.call(profile, "avatar"),
152286
- hasVault: seedDoc.exists(),
152287
- communityRulesVersion: user?.communityRulesVersion || null,
152288
- communityRulesDate: user?.communityRulesDate || null,
152289
- communityRulesAcceptedAt: user?.communityRulesAcceptedAt || null,
152290
- hasCurrentCommunityRules: hasCurrentCommunityRules(user)
152537
+ uid: user.uid,
152538
+ sessionGeneration,
152539
+ clientSessionId
152540
+ };
152541
+ }
152542
+ function vaultSignatureClaims() {
152543
+ return clientSessionClaims();
152544
+ }
152545
+ async function signVaultRequest(purpose, payload = null, signer = activeVaultSigner) {
152546
+ if (!signer) {
152547
+ throw vaultSignatureError("locked", "unlocked vault required");
152548
+ }
152549
+ try {
152550
+ return createVaultSignature(signer, await vaultSignatureClaims(), purpose, payload);
152551
+ } catch (error) {
152552
+ if (!error?.code) {
152553
+ error.code = "vault-signature/proof-construction";
152554
+ }
152555
+ throw error;
152556
+ }
152557
+ }
152558
+ async function grantVaultLease(signer = activeVaultSigner, force = false) {
152559
+ if (!signer || signer !== activeVaultSigner) {
152560
+ throw new Error("unlocked vault required");
152561
+ }
152562
+ if (!force && vaultLeaseExpiresAt - Date.now() > 15000) {
152563
+ return { expiresAt: vaultLeaseExpiresAt };
152564
+ }
152565
+ if (vaultLeaseRequest?.signer === signer) {
152566
+ return vaultLeaseRequest.promise;
152567
+ }
152568
+ const promise = (async () => {
152569
+ const result = await callFunction("grantVaultLease", {
152570
+ vaultSignature: await signVaultRequest(VAULT_LEASE_PURPOSE, null, signer)
152571
+ });
152572
+ if (signer !== activeVaultSigner) {
152573
+ throw new Error("vault locked during authorization");
152574
+ }
152575
+ const expiresAt = Number(result?.expiresAt);
152576
+ if (!Number.isFinite(expiresAt) || expiresAt <= Date.now()) {
152577
+ throw new Error("invalid vault lease");
152578
+ }
152579
+ vaultLeaseExpiresAt = expiresAt;
152580
+ return { expiresAt };
152581
+ })().finally(() => {
152582
+ if (vaultLeaseRequest?.promise === promise) {
152583
+ vaultLeaseRequest = null;
152584
+ }
152585
+ });
152586
+ vaultLeaseRequest = { signer, promise };
152587
+ return promise;
152588
+ }
152589
+ async function openVaultSignature(signer) {
152590
+ if (!signer?.publicKey || typeof signer?.sign !== "function") {
152591
+ throw vaultSignatureError("invalid-signer", "vault signer required");
152592
+ }
152593
+ if (activeVaultSigner?.closed) {
152594
+ activeVaultSigner = null;
152595
+ vaultLeaseExpiresAt = 0;
152596
+ vaultLeaseRequest = null;
152597
+ vaultOpenRequest = null;
152598
+ }
152599
+ if (activeVaultSigner && activeVaultSigner !== signer) {
152600
+ throw vaultSignatureError("already-open", "vault signature already open");
152601
+ }
152602
+ activeVaultSigner = signer;
152603
+ const openPromise = grantVaultLease(signer, true);
152604
+ vaultOpenRequest = { signer, promise: openPromise };
152605
+ try {
152606
+ await openPromise;
152607
+ } catch (error) {
152608
+ if (activeVaultSigner === signer) {
152609
+ activeVaultSigner = null;
152610
+ vaultLeaseExpiresAt = 0;
152611
+ }
152612
+ throw error;
152613
+ } finally {
152614
+ if (vaultOpenRequest?.promise === openPromise) {
152615
+ vaultOpenRequest = null;
152616
+ }
152617
+ }
152618
+ let closed = false;
152619
+ return {
152620
+ close() {
152621
+ if (closed)
152622
+ return;
152623
+ closed = true;
152624
+ if (activeVaultSigner !== signer)
152625
+ return;
152626
+ activeVaultSigner = null;
152627
+ vaultLeaseExpiresAt = 0;
152628
+ callFunction("dropVaultLease").catch(() => {});
152629
+ }
152291
152630
  };
152292
152631
  }
152632
+ async function callVaultFunction(name7, purpose, payload = null) {
152633
+ const signer = activeVaultSigner;
152634
+ if (!signer)
152635
+ throw new Error("unlocked vault required");
152636
+ if (vaultOpenRequest?.signer === signer) {
152637
+ await vaultOpenRequest.promise;
152638
+ }
152639
+ if (signer !== activeVaultSigner) {
152640
+ throw vaultSignatureError("locked", "unlocked vault required");
152641
+ }
152642
+ return callFunction(name7, {
152643
+ vaultSignature: await signVaultRequest(purpose, payload, signer)
152644
+ });
152645
+ }
152293
152646
  async function isAdmin(uid) {
152294
152647
  if (!uid)
152295
152648
  return false;
@@ -152312,27 +152665,18 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
152312
152665
  const snap = await getDoc(doc(db, "seeds", uid));
152313
152666
  return snap.exists();
152314
152667
  }
152315
- async function writeVault(uid, vault) {
152668
+ async function writeVault(uid, vault, { vaultSigningPK } = {}) {
152316
152669
  requireUid(uid);
152317
- if (!vault) {
152318
- throw new Error("vault required");
152670
+ if (!vault || !vaultSigningPK) {
152671
+ throw new Error("vault enrollment required");
152319
152672
  }
152320
- await setDoc(doc(db, "seeds", uid), { es: writeCloudBytes(vault, "vault bytes") });
152321
- return true;
152322
- }
152323
- async function replaceVault(uid, { vault, expectedHash, from, to, walletPK, chatPK, network } = {}) {
152324
- requireUid(uid);
152325
- if (!vault || !expectedHash || !from || !to) {
152326
- throw new Error("vault replacement required");
152673
+ const user = requireAuth().currentUser;
152674
+ if (user?.uid !== uid) {
152675
+ throw new Error("vault account mismatch");
152327
152676
  }
152328
- await callFunction("replaceVault", {
152329
- expectedHash,
152330
- from,
152331
- to,
152677
+ await callFunction("createVault", {
152332
152678
  vault: cloudBytesBase64(vault, "vault bytes"),
152333
- walletPK: walletPK || null,
152334
- chatPK: chatPK || null,
152335
- network: network || null
152679
+ vaultSigningPK
152336
152680
  });
152337
152681
  return true;
152338
152682
  }
@@ -152377,13 +152721,13 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
152377
152721
  async function writeProfileWalletPK(walletPK, { network } = {}) {
152378
152722
  if (!walletPK)
152379
152723
  throw new Error("wallet public key required");
152380
- await callFunction("setWalletPK", { walletPK, network });
152724
+ await callVaultFunction("setWalletPK", VAULT_SIGNATURE_PURPOSES.SET_WALLET_IDENTITY, { walletPK, network });
152381
152725
  return true;
152382
152726
  }
152383
152727
  async function writeProfileChatPK(chatPK) {
152384
152728
  if (!chatPK)
152385
152729
  throw new Error("chat public key required");
152386
- await callFunction("setChatPK", { chatPK });
152730
+ await callVaultFunction("setChatPK", VAULT_SIGNATURE_PURPOSES.SET_CHAT_IDENTITY, { chatPK });
152387
152731
  return true;
152388
152732
  }
152389
152733
  async function getUsername(username) {
@@ -152394,7 +152738,7 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
152394
152738
  return true;
152395
152739
  }
152396
152740
  async function deleteUser2() {
152397
- await callFunction("deleteAccount");
152741
+ await callVaultFunction("deleteAccount", VAULT_SIGNATURE_PURPOSES.DELETE_ACCOUNT);
152398
152742
  return true;
152399
152743
  }
152400
152744
  function watchProfile(uid, onUpdate, onError) {
@@ -152434,6 +152778,7 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
152434
152778
  return openSettings(key, uid, readCloudBytes(saved, "settings body"));
152435
152779
  }
152436
152780
  const settings = normalizeSettings(defaultSettings);
152781
+ await grantVaultLease(activeVaultSigner);
152437
152782
  await setDoc(doc(db, "users", uid), {
152438
152783
  settings: writeCloudBytes(await sealSettings(key, uid, settings), "settings body")
152439
152784
  }, { merge: true });
@@ -152447,6 +152792,7 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
152447
152792
  const base = currentSettings || await readSettings(uid, key);
152448
152793
  const nextSettings = normalizeSettings(settings, base);
152449
152794
  const body = await sealSettings(key, uid, nextSettings);
152795
+ await grantVaultLease(activeVaultSigner);
152450
152796
  await setDoc(doc(db, "users", uid), {
152451
152797
  settings: writeCloudBytes(body, "settings body")
152452
152798
  }, { merge: true });
@@ -152491,9 +152837,12 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
152491
152837
  }
152492
152838
  function watchBlocked(uid, onUpdate, onError) {
152493
152839
  requireUid(uid);
152494
- return onSnapshot(collection(db, "users", uid, "blocked"), (snap) => {
152840
+ return onSnapshot(collection(db, "users", uid, "blocked"), { includeMetadataChanges: true }, (snap) => {
152495
152841
  const blocked = snap.docs.map((item) => item.id).filter(Boolean).sort();
152496
- onUpdate?.(blocked);
152842
+ onUpdate?.(blocked, {
152843
+ fromCache: snap.metadata.fromCache,
152844
+ pending: snap.metadata.hasPendingWrites
152845
+ });
152497
152846
  }, onError);
152498
152847
  }
152499
152848
  function watchBitcoin(onUpdate, onError) {
@@ -152765,6 +153114,15 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
152765
153114
  return resolveAuth()?.currentUser ?? null;
152766
153115
  },
152767
153116
  watch: watchAuth,
153117
+ session: {
153118
+ watch: watchAuthSession
153119
+ },
153120
+ vaultSignature: {
153121
+ open: openVaultSignature,
153122
+ ensure: () => grantVaultLease(activeVaultSigner),
153123
+ sign: signVaultRequest,
153124
+ call: callVaultFunction
153125
+ },
152768
153126
  logout,
152769
153127
  logoutDevices,
152770
153128
  login: {
@@ -153274,10 +153632,8 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
153274
153632
  read: readVault,
153275
153633
  exists: vaultExists,
153276
153634
  write: writeVault,
153277
- replace: replaceVault,
153278
153635
  watch: watchVault
153279
153636
  },
153280
- onboarding: readOnboarding,
153281
153637
  community: {
153282
153638
  accept: acceptCommunity
153283
153639
  },
@@ -154879,7 +155235,7 @@ async function runPasskeyBrowserFlow(options2 = {}) {
154879
155235
  }
154880
155236
 
154881
155237
  // src/storage.js
154882
- import { access, chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
155238
+ import { access, chmod, mkdir, readFile, rename, unlink as unlink2, writeFile } from "node:fs/promises";
154883
155239
  import { randomUUID } from "node:crypto";
154884
155240
  import { homedir } from "node:os";
154885
155241
  import { dirname as dirname2, join as join3 } from "node:path";
@@ -154976,6 +155332,22 @@ class ProfileStore {
154976
155332
  await this.writeActiveProfile(profileName);
154977
155333
  return next;
154978
155334
  }
155335
+ async remove(profileName = "") {
155336
+ const profile = cleanProfileName(profileName || this.profile || await this.readActiveProfile(), "");
155337
+ if (!profile)
155338
+ return false;
155339
+ await unlink2(this.profilePath(profile)).catch((error) => {
155340
+ if (error?.code !== "ENOENT")
155341
+ throw error;
155342
+ });
155343
+ if (await this.readActiveProfile() === profile) {
155344
+ await unlink2(this.activePath()).catch((error) => {
155345
+ if (error?.code !== "ENOENT")
155346
+ throw error;
155347
+ });
155348
+ }
155349
+ return true;
155350
+ }
154979
155351
  }
154980
155352
 
154981
155353
  // src/mcp.js
@@ -154984,10 +155356,15 @@ import process4 from "node:process";
154984
155356
  // package.json
154985
155357
  var package_default = {
154986
155358
  name: "@glyphteck/veyl",
154987
- version: "0.4.1",
155359
+ version: "0.5.0",
154988
155360
  license: "Apache-2.0",
154989
155361
  type: "module",
154990
155362
  description: "Programmable Node.js client for Veyl accounts, vaults, chat, and wallet payments.",
155363
+ repository: {
155364
+ type: "git",
155365
+ url: "https://github.com/glyphteck/veyl.git",
155366
+ directory: "clients/headless"
155367
+ },
154991
155368
  exports: {
154992
155369
  ".": "./dist/index.js"
154993
155370
  },
@@ -155098,6 +155475,7 @@ var HEADLESS_COMMANDS = Object.freeze([
155098
155475
  invokeCli: (client2, input, context) => context.options.passkey ? client2.account.loginPasskey(input) : client2.account.login({ username: input.username })
155099
155476
  }),
155100
155477
  command({ name: "account_me", group: "account", apiNames: ["me"], description: "Show the current local account.", paths: [["me"], ["account", "me"]], shortUsage: "me", structuredUsage: "account me", cliInput: optionsOnly, invoke: (client2) => client2.account.me() }),
155478
+ command({ name: "account_delete", group: "account", apiNames: ["delete"], description: "Delete the unlocked account, its known chats, and its local profile.", properties: { confirm: booleanProp }, required: ["confirm"], paths: [["account", "delete"]], structuredUsage: "account delete --confirm", cliInput: optionsOnly, invoke: (client2, input) => client2.account.delete(input) }),
155101
155479
  command({ name: "vault_create", group: "vault", apiNames: ["create"], description: "Create and upload the local encrypted vault.", properties: { secret: stringProp, saveSecret: booleanProp }, paths: [["vault", "create"]], structuredUsage: "vault create", cliInput: vaultInput, invoke: (client2, input) => client2.vault.create(input) }),
155102
155480
  command({ name: "vault_unlock", group: "vault", apiNames: ["unlock"], description: "Unlock the local vault.", properties: { secret: stringProp }, paths: [["unlock"], ["vault", "unlock"]], shortUsage: "unlock", structuredUsage: "vault unlock [--secret value]", cliInput: vaultInput, invoke: (client2, input) => client2.vault.unlock(input) }),
155103
155481
  command({ name: "vault_lock", group: "vault", apiNames: ["lock"], description: "Lock the wallet and chat session.", paths: [["lock"], ["vault", "lock"]], shortUsage: "lock", structuredUsage: "vault lock", cliInput: optionsOnly, invoke: (client2) => client2.vault.lock() }),
@@ -155319,7 +155697,8 @@ class VeylHeadlessClient {
155319
155697
  createPasskey: (payload) => this.createPasskeyAccount(payload),
155320
155698
  login: (payload) => this.loginAccount(payload),
155321
155699
  loginPasskey: (payload) => this.loginPasskeyAccount(payload),
155322
- me: () => this.me()
155700
+ me: () => this.me(),
155701
+ delete: (payload) => this.deleteAccount(payload)
155323
155702
  };
155324
155703
  this.vault = {
155325
155704
  create: (payload) => this.createVault(payload),
@@ -155564,6 +155943,20 @@ class VeylHeadlessClient {
155564
155943
  this.profile = profile;
155565
155944
  return this.accountSummary(profile);
155566
155945
  }
155946
+ async deleteAccount(options2 = {}) {
155947
+ if (options2.confirm !== true) {
155948
+ throw new Error("account deletion requires confirm: true");
155949
+ }
155950
+ await this.ensureUnlocked();
155951
+ const profileName = this.profile?.profile || "";
155952
+ const chats = await this.actions.chat.deleteAll();
155953
+ await this.cloud.user.delete();
155954
+ await this.lockVault();
155955
+ await signOutHeadless(this.auth).catch(() => {});
155956
+ await this.store.remove(profileName);
155957
+ this.profile = null;
155958
+ return { deleted: true, chats };
155959
+ }
155567
155960
  async ensureAuth() {
155568
155961
  const profile = await this.loadProfile();
155569
155962
  if (this.auth?.currentUser?.uid === profile.uid) {
@@ -155579,7 +155972,9 @@ class VeylHeadlessClient {
155579
155972
  async bootSession(masterSeed, registry, profile) {
155580
155973
  const opened = await openRegistryAccountSession(masterSeed, registry, {
155581
155974
  SparkWallet: SparkWalletNodeJS,
155582
- network: profile.network || this.network
155975
+ network: profile.network || this.network,
155976
+ cloud: this.cloud,
155977
+ uid: profile.uid
155583
155978
  });
155584
155979
  this.session = Object.assign(opened, {
155585
155980
  uid: profile.uid,
@@ -155592,7 +155987,7 @@ class VeylHeadlessClient {
155592
155987
  }
155593
155988
  async createVault(options2 = {}) {
155594
155989
  const profile = await this.ensureAuth();
155595
- if (options2.overwrite !== true && await this.cloud.user.vault.exists(profile.uid)) {
155990
+ if (await this.cloud.user.vault.exists(profile.uid)) {
155596
155991
  throw new Error("vault already exists");
155597
155992
  }
155598
155993
  const vaultSecret = String(options2.secret || process5.env.VEYL_VAULT_SECRET || randomSecret()).trim();
@@ -155606,9 +156001,9 @@ class VeylHeadlessClient {
155606
156001
  deriveKey: deriveVaultKey,
155607
156002
  registry: registrySource
155608
156003
  });
155609
- const session = await this.bootSession(masterSeed, encrypted.registry, profile);
155610
156004
  const vault = packSeedData(encrypted);
155611
- await this.cloud.user.vault.write(profile.uid, vault);
156005
+ await this.cloud.user.vault.write(profile.uid, vault, { vaultSigningPK: encrypted.vaultSigningPK });
156006
+ const session = await this.bootSession(masterSeed, encrypted.registry, profile);
155612
156007
  await this.cloud.user.profile.walletpk.write(session.walletPK, { network: profile.network || this.network });
155613
156008
  await this.cloud.user.profile.chatpk.write(session.chatPK);
155614
156009
  await this.cloud.user.community.accept(profile.uid).catch(() => {});