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