@glyphteck/veyl 0.68.0 → 0.70.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.
@@ -48,39 +48,7 @@ var __export = (target, all) => {
48
48
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
49
49
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
50
50
 
51
- // ../../node_modules/.bun/@noble+hashes@2.3.0/node_modules/@noble/hashes/_u64.js
52
- function fromBig(n, le = false) {
53
- if (le)
54
- return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
55
- return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
56
- }
57
- function split(lst, le = false) {
58
- const len = lst.length;
59
- let Ah = new Uint32Array(len);
60
- let Al = new Uint32Array(len);
61
- for (let i = 0;i < len; i++) {
62
- const { h, l } = fromBig(lst[i], le);
63
- [Ah[i], Al[i]] = [h, l];
64
- }
65
- return [Ah, Al];
66
- }
67
- function setU64FromNum(view, byteOffset, n, isLE) {
68
- const h = fromNumH(n);
69
- const l = fromNumL(n);
70
- view.setUint32(byteOffset, isLE ? l : h, isLE);
71
- view.setUint32(byteOffset + 4, isLE ? h : l, isLE);
72
- }
73
- function add(Ah, Al, Bh, Bl) {
74
- const l = (Al >>> 0) + (Bl >>> 0);
75
- return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 };
76
- }
77
- var U32_MASK64, _32n, fromNumH = (n) => n / 2 ** 32 | 0, fromNumL = (n) => n >>> 0, shrSH = (h, _l, s) => h >>> s, shrSL = (h, l, s) => h << 32 - s | l >>> s, rotrSH = (h, l, s) => h >>> s | l << 32 - s, rotrSL = (h, l, s) => h << 32 - s | l >>> s, rotrBH = (h, l, s) => h << 64 - s | l >>> s - 32, rotrBL = (h, l, s) => h >>> s - 32 | l << 64 - s, rotr32H = (_h, l) => l, rotr32L = (h, _l) => h, add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0), add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0, add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0), add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0, add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0), add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0;
78
- var init__u64 = __esm(() => {
79
- U32_MASK64 = /* @__PURE__ */ (() => BigInt(2 ** 32 - 1))();
80
- _32n = /* @__PURE__ */ BigInt(32);
81
- });
82
-
83
- // ../../node_modules/.bun/@noble+hashes@2.3.0/node_modules/@noble/hashes/utils.js
51
+ // ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/utils.js
84
52
  function isBytes(a) {
85
53
  return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
86
54
  }
@@ -198,7 +166,12 @@ function hexToBytes(hex) {
198
166
  function utf8ToBytes(str) {
199
167
  if (typeof str !== "string")
200
168
  throw new TypeError("string expected");
201
- return new Uint8Array(new TextEncoder().encode(str));
169
+ const encoded = new TextEncoder().encode(str);
170
+ try {
171
+ return new Uint8Array(encoded);
172
+ } finally {
173
+ clean(encoded);
174
+ }
202
175
  }
203
176
  function kdfInputToBytes(data, errorTitle = "") {
204
177
  if (typeof data === "string")
@@ -221,10 +194,10 @@ function concatBytes(...arrays) {
221
194
  return res;
222
195
  }
223
196
  function checkOpts(defaults, opts, title = "opts") {
224
- aobject(defaults, "defaults");
197
+ aopts(defaults, "defaults");
225
198
  if (opts !== undefined)
226
- aobject(opts, title);
227
- const merged = Object.assign(defaults, opts);
199
+ aopts(opts, title);
200
+ const merged = Object.assign(Object.create(null), defaults, opts);
228
201
  return merged;
229
202
  }
230
203
  function createHasher(hashCons, info = {}) {
@@ -252,6 +225,13 @@ function randomBytes(bytesLength = 32) {
252
225
  var atitle = (title) => title ? `"${title}" ` : "", aobject = (value, label) => {
253
226
  if (value === null || typeof value !== "object" || Array.isArray(value))
254
227
  throw new TypeError((label === "object" ? "" : `"${label}" `) + "expected object, got type=" + typeof value);
228
+ }, aopts = (value, label) => {
229
+ aobject(value, label);
230
+ const proto = Object.getPrototypeOf(value);
231
+ if (proto !== Object.prototype && proto !== null)
232
+ throw new TypeError(`"${label}" expected plain object`);
233
+ if (Object.hasOwn(value, "__proto__"))
234
+ throw new TypeError(`"${label}.__proto__" is not allowed`);
255
235
  }, isLE, swap8IfBE, swap32IfBE, hasHexBuiltin, hexes, oidNist = (suffix) => ({
256
236
  oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
257
237
  });
@@ -263,7 +243,39 @@ var init_utils = __esm(() => {
263
243
  hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
264
244
  });
265
245
 
266
- // ../../node_modules/.bun/@noble+hashes@2.3.0/node_modules/@noble/hashes/_md.js
246
+ // ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/_u64.js
247
+ function fromBig(n, le = false) {
248
+ if (le)
249
+ return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
250
+ return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
251
+ }
252
+ function split(lst, le = false) {
253
+ const len = lst.length;
254
+ let Ah = new Uint32Array(len);
255
+ let Al = new Uint32Array(len);
256
+ for (let i = 0;i < len; i++) {
257
+ const { h, l } = fromBig(lst[i], le);
258
+ [Ah[i], Al[i]] = [h, l];
259
+ }
260
+ return [Ah, Al];
261
+ }
262
+ function setU64FromNum(view, byteOffset, n, isLE2) {
263
+ const h = fromNumH(n);
264
+ const l = fromNumL(n);
265
+ view.setUint32(byteOffset, isLE2 ? l : h, isLE2);
266
+ view.setUint32(byteOffset + 4, isLE2 ? h : l, isLE2);
267
+ }
268
+ function add(Ah, Al, Bh, Bl) {
269
+ const l = (Al >>> 0) + (Bl >>> 0);
270
+ return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 };
271
+ }
272
+ var U32_MASK64, _32n, fromNumH = (n) => n / 2 ** 32 | 0, fromNumL = (n) => n >>> 0, shrSH = (h, _l, s) => h >>> s, shrSL = (h, l, s) => h << 32 - s | l >>> s, rotrSH = (h, l, s) => h >>> s | l << 32 - s, rotrSL = (h, l, s) => h << 32 - s | l >>> s, rotrBH = (h, l, s) => h << 64 - s | l >>> s - 32, rotrBL = (h, l, s) => h >>> s - 32 | l << 64 - s, rotr32H = (_h, l) => l, rotr32L = (h, _l) => h, add3L = (Al, Bl, Cl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0), add3H = (low, Ah, Bh, Ch) => Ah + Bh + Ch + (low / 2 ** 32 | 0) | 0, add4L = (Al, Bl, Cl, Dl) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0), add4H = (low, Ah, Bh, Ch, Dh) => Ah + Bh + Ch + Dh + (low / 2 ** 32 | 0) | 0, add5L = (Al, Bl, Cl, Dl, El) => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0), add5H = (low, Ah, Bh, Ch, Dh, Eh) => Ah + Bh + Ch + Dh + Eh + (low / 2 ** 32 | 0) | 0;
273
+ var init__u64 = __esm(() => {
274
+ U32_MASK64 = /* @__PURE__ */ (() => BigInt(2 ** 32 - 1))();
275
+ _32n = /* @__PURE__ */ BigInt(32);
276
+ });
277
+
278
+ // ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/_md.js
267
279
  function Chi(a, b, c) {
268
280
  return a & b ^ ~a & c;
269
281
  }
@@ -402,10 +414,7 @@ var init__md = __esm(() => {
402
414
  // src/runtime/kdf-worker-thread.js
403
415
  import { parentPort } from "node:worker_threads";
404
416
 
405
- // ../../node_modules/.bun/@noble+hashes@2.3.0/node_modules/@noble/hashes/argon2.js
406
- init__u64();
407
-
408
- // ../../node_modules/.bun/@noble+hashes@2.3.0/node_modules/@noble/hashes/_blake.js
417
+ // ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/_blake.js
409
418
  var BSIGMA = /* @__PURE__ */ Uint8Array.from([
410
419
  0,
411
420
  1,
@@ -665,7 +674,7 @@ var BSIGMA = /* @__PURE__ */ Uint8Array.from([
665
674
  9
666
675
  ]);
667
676
 
668
- // ../../node_modules/.bun/@noble+hashes@2.3.0/node_modules/@noble/hashes/blake2.js
677
+ // ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/blake2.js
669
678
  init__u64();
670
679
  init_utils();
671
680
  var B2B_IV = /* @__PURE__ */ Uint32Array.from([
@@ -1000,7 +1009,7 @@ class _BLAKE2b extends _BLAKE2 {
1000
1009
  }
1001
1010
  var blake2b = /* @__PURE__ */ createHasher((opts) => new _BLAKE2b(opts));
1002
1011
 
1003
- // ../../node_modules/.bun/@noble+hashes@2.3.0/node_modules/@noble/hashes/argon2.js
1012
+ // ../../node_modules/.bun/@noble+hashes@2.4.0/node_modules/@noble/hashes/argon2.js
1004
1013
  init_utils();
1005
1014
  var AT = { Argon2d: 0, Argon2i: 1, Argon2id: 2 };
1006
1015
  var ARGON2_SYNC_POINTS = 4;
@@ -1009,25 +1018,6 @@ var abytesOrZero = (buf, errorTitle = "") => {
1009
1018
  return Uint8Array.of();
1010
1019
  return kdfInputToBytes(buf, errorTitle);
1011
1020
  };
1012
- function mul(a, b) {
1013
- const aL = a & 65535;
1014
- const aH = a >>> 16;
1015
- const bL = b & 65535;
1016
- const bH = b >>> 16;
1017
- const ll = Math.imul(aL, bL);
1018
- const hl = Math.imul(aH, bL);
1019
- const lh = Math.imul(aL, bH);
1020
- const hh = Math.imul(aH, bH);
1021
- const carry = (ll >>> 16) + (hl & 65535) + lh;
1022
- const high = hh + (hl >>> 16) + (carry >>> 16) | 0;
1023
- const low = carry << 16 | ll & 65535;
1024
- return { h: high, l: low };
1025
- }
1026
- function mulHi(a, b) {
1027
- const aL = a & 65535, aH = a >>> 16, bL = b & 65535, bH = b >>> 16;
1028
- const carry = (Math.imul(aL, bL) >>> 16) + (Math.imul(aH, bL) & 65535) + Math.imul(aL, bH);
1029
- return Math.imul(aH, bH) + (Math.imul(aH, bL) >>> 16) + (carry >>> 16) | 0;
1030
- }
1031
1021
  var A2_BUF = new Uint32Array(256);
1032
1022
  function G(a, b, c, d) {
1033
1023
  let Al = A2_BUF[2 * a], Ah = A2_BUF[2 * a + 1];
@@ -1036,41 +1026,41 @@ function G(a, b, c, d) {
1036
1026
  let Dl = A2_BUF[2 * d], Dh = A2_BUF[2 * d + 1];
1037
1027
  let ml = 0, mh = 0, rl = 0, xh = 0, xl = 0;
1038
1028
  ml = Math.imul(Al, Bl);
1039
- mh = mulHi(Al, Bl);
1029
+ mh = ((Al >>> 0) * (Bl >>> 0) - (ml >>> 0)) / 4294967296 + 0.5 | 0;
1040
1030
  rl = (Al >>> 0) + (Bl >>> 0) + (ml << 1 >>> 0);
1041
1031
  Ah = Ah + Bh + (mh << 1 | ml >>> 31) + (rl / 4294967296 | 0) | 0;
1042
1032
  Al = rl | 0;
1043
1033
  xh = Dh ^ Ah;
1044
1034
  xl = Dl ^ Al;
1045
- Dh = rotr32H(xh, xl);
1046
- Dl = rotr32L(xh, xl);
1035
+ Dh = xl;
1036
+ Dl = xh;
1047
1037
  ml = Math.imul(Cl, Dl);
1048
- mh = mulHi(Cl, Dl);
1038
+ mh = ((Cl >>> 0) * (Dl >>> 0) - (ml >>> 0)) / 4294967296 + 0.5 | 0;
1049
1039
  rl = (Cl >>> 0) + (Dl >>> 0) + (ml << 1 >>> 0);
1050
1040
  Ch = Ch + Dh + (mh << 1 | ml >>> 31) + (rl / 4294967296 | 0) | 0;
1051
1041
  Cl = rl | 0;
1052
1042
  xh = Bh ^ Ch;
1053
1043
  xl = Bl ^ Cl;
1054
- Bh = rotrSH(xh, xl, 24);
1055
- Bl = rotrSL(xh, xl, 24);
1044
+ Bh = xh >>> 24 | xl << 8;
1045
+ Bl = xh << 8 | xl >>> 24;
1056
1046
  ml = Math.imul(Al, Bl);
1057
- mh = mulHi(Al, Bl);
1047
+ mh = ((Al >>> 0) * (Bl >>> 0) - (ml >>> 0)) / 4294967296 + 0.5 | 0;
1058
1048
  rl = (Al >>> 0) + (Bl >>> 0) + (ml << 1 >>> 0);
1059
1049
  Ah = Ah + Bh + (mh << 1 | ml >>> 31) + (rl / 4294967296 | 0) | 0;
1060
1050
  Al = rl | 0;
1061
1051
  xh = Dh ^ Ah;
1062
1052
  xl = Dl ^ Al;
1063
- Dh = rotrSH(xh, xl, 16);
1064
- Dl = rotrSL(xh, xl, 16);
1053
+ Dh = xh >>> 16 | xl << 16;
1054
+ Dl = xh << 16 | xl >>> 16;
1065
1055
  ml = Math.imul(Cl, Dl);
1066
- mh = mulHi(Cl, Dl);
1056
+ mh = ((Cl >>> 0) * (Dl >>> 0) - (ml >>> 0)) / 4294967296 + 0.5 | 0;
1067
1057
  rl = (Cl >>> 0) + (Dl >>> 0) + (ml << 1 >>> 0);
1068
1058
  Ch = Ch + Dh + (mh << 1 | ml >>> 31) + (rl / 4294967296 | 0) | 0;
1069
1059
  Cl = rl | 0;
1070
1060
  xh = Bh ^ Ch;
1071
1061
  xl = Bl ^ Cl;
1072
- Bh = rotrBH(xh, xl, 63);
1073
- Bl = rotrBL(xh, xl, 63);
1062
+ Bh = xh << 1 | xl >>> 31;
1063
+ Bl = xh >>> 31 | xl << 1;
1074
1064
  A2_BUF[2 * a] = Al, A2_BUF[2 * a + 1] = Ah;
1075
1065
  A2_BUF[2 * b] = Bl, A2_BUF[2 * b + 1] = Bh;
1076
1066
  A2_BUF[2 * c] = Cl, A2_BUF[2 * c + 1] = Ch;
@@ -1087,20 +1077,27 @@ function P(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13,
1087
1077
  G(v03, v04, v09, v14);
1088
1078
  }
1089
1079
  function block(x, xPos, yPos, outPos, needXor) {
1090
- for (let i = 0;i < 256; i++)
1091
- A2_BUF[i] = x[xPos + i] ^ x[yPos + i];
1080
+ if (needXor) {
1081
+ for (let i = 0;i < 256; i++) {
1082
+ const r = x[xPos + i] ^ x[yPos + i];
1083
+ A2_BUF[i] = r;
1084
+ x[outPos + i] ^= r;
1085
+ }
1086
+ } else {
1087
+ for (let i = 0;i < 256; i++) {
1088
+ const r = x[xPos + i] ^ x[yPos + i];
1089
+ A2_BUF[i] = r;
1090
+ x[outPos + i] = r;
1091
+ }
1092
+ }
1092
1093
  for (let i = 0;i < 128; i += 16) {
1093
1094
  P(i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9, i + 10, i + 11, i + 12, i + 13, i + 14, i + 15);
1094
1095
  }
1095
1096
  for (let i = 0;i < 16; i += 2) {
1096
1097
  P(i, i + 1, i + 16, i + 17, i + 32, i + 33, i + 48, i + 49, i + 64, i + 65, i + 80, i + 81, i + 96, i + 97, i + 112, i + 113);
1097
1098
  }
1098
- if (needXor)
1099
- for (let i = 0;i < 256; i++)
1100
- x[outPos + i] ^= A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
1101
- else
1102
- for (let i = 0;i < 256; i++)
1103
- x[outPos + i] = A2_BUF[i] ^ x[xPos + i] ^ x[yPos + i];
1099
+ for (let i = 0;i < 256; i++)
1100
+ x[outPos + i] ^= A2_BUF[i];
1104
1101
  clean(A2_BUF);
1105
1102
  }
1106
1103
  function Hp(A, dkLen) {
@@ -1139,19 +1136,28 @@ function indexAlpha(r, s, laneLen, segmentLen, index, randL, sameLane = false) {
1139
1136
  else
1140
1137
  area = laneLen - segmentLen + (index == 0 ? -1 : 0);
1141
1138
  const startPos = r !== 0 && s !== ARGON2_SYNC_POINTS - 1 ? (s + 1) * segmentLen : 0;
1142
- const rel = area - 1 - mul(area, mul(randL, randL).h).h;
1139
+ const randLow = Math.imul(randL, randL);
1140
+ const randHigh = ((randL >>> 0) * (randL >>> 0) - (randLow >>> 0)) / 4294967296 + 0.5 | 0;
1141
+ const areaLow = Math.imul(area, randHigh);
1142
+ const areaHigh = ((area >>> 0) * (randHigh >>> 0) - (areaLow >>> 0)) / 4294967296 + 0.5 | 0;
1143
+ const rel = area - 1 - areaHigh;
1143
1144
  return (startPos + rel) % laneLen;
1144
1145
  }
1145
1146
  var maxUint32 = Math.pow(2, 32);
1147
+ var ARGON2_DEFAULT_MEMORY = 1024 ** 2;
1148
+ var ARGON2_DEFAULT_MAXMEM = ARGON2_DEFAULT_MEMORY * 1024;
1146
1149
  function isU32(num) {
1147
1150
  return Number.isSafeInteger(num) && num >= 0 && num < maxUint32;
1148
1151
  }
1149
- function argon2Opts(opts) {
1152
+ function argon2Opts(opts = {}) {
1150
1153
  opts = checkOpts({}, opts);
1151
1154
  const merged = {
1155
+ t: 3,
1156
+ m: ARGON2_DEFAULT_MEMORY,
1157
+ p: 1,
1152
1158
  version: 19,
1153
1159
  dkLen: 32,
1154
- maxmem: maxUint32 - 1,
1160
+ maxmem: ARGON2_DEFAULT_MAXMEM,
1155
1161
  asyncTick: 10
1156
1162
  };
1157
1163
  for (let [k, v] of Object.entries(opts))
@@ -1175,63 +1181,89 @@ function argon2Opts(opts) {
1175
1181
  throw new Error('"version" must be 0x10 or 0x13, got ' + version);
1176
1182
  return merged;
1177
1183
  }
1178
- function argon2Init(password, salt, type, opts) {
1179
- password = kdfInputToBytes(password, "password");
1180
- salt = kdfInputToBytes(salt, "salt");
1181
- if (!isU32(password.length))
1182
- throw new Error('"password" must be less of length 1..4Gb');
1183
- if (!isU32(salt.length) || salt.length < 8)
1184
- throw new Error('"salt" must be of length 8..4Gb');
1185
- if (!Object.values(AT).includes(type))
1186
- throw new Error('"type" was invalid');
1187
- let { p, dkLen, m, t, version, key, personalization, maxmem, onProgress, asyncTick } = argon2Opts(opts);
1188
- key = abytesOrZero(key, "key");
1189
- personalization = abytesOrZero(personalization, "personalization");
1190
- const h = blake2b.create();
1184
+ function argon2InitialHash(password, salt, type, opts) {
1185
+ const ownedInputs = [];
1191
1186
  const BUF = new Uint32Array(1);
1192
1187
  const BUF8 = u8(BUF);
1193
- for (let item of [p, dkLen, m, t, version, type]) {
1194
- BUF[0] = swap8IfBE(item);
1195
- h.update(BUF8);
1196
- }
1197
- for (let i of [password, salt, key, personalization]) {
1198
- BUF[0] = swap8IfBE(i.length);
1199
- h.update(BUF8).update(i);
1200
- }
1201
- const H0 = new Uint32Array(18);
1202
- const H0_8 = u8(H0);
1203
- h.digestInto(H0_8);
1204
- const lanes = p;
1205
- const mP = 4 * p * Math.floor(m / (ARGON2_SYNC_POINTS * p));
1206
- const laneLen = Math.floor(mP / p);
1207
- const segmentLen = Math.floor(laneLen / ARGON2_SYNC_POINTS);
1208
- const memUsed = mP * 1024;
1209
- if (!isU32(maxmem))
1210
- throw new Error('"maxmem" expected <2**32, got ' + maxmem);
1211
- if (memUsed > maxmem)
1212
- throw new Error('"maxmem" limit was hit: memUsed(mP*1024)=' + memUsed + ", maxmem=" + maxmem);
1213
- const B = new Uint32Array(memUsed / 4);
1214
- for (let l = 0;l < p; l++) {
1215
- const i = 256 * laneLen * l;
1216
- H0[17] = swap8IfBE(l);
1217
- H0[16] = swap8IfBE(0);
1218
- B.set(swap32IfBE(u32(Hp(H0, 1024))), i);
1219
- H0[16] = swap8IfBE(1);
1220
- B.set(swap32IfBE(u32(Hp(H0, 1024))), i + 256);
1188
+ let h;
1189
+ let H0;
1190
+ let succeeded = false;
1191
+ const rememberOwned = (input, bytes) => {
1192
+ if (typeof input === "string")
1193
+ ownedInputs.push(bytes);
1194
+ return bytes;
1195
+ };
1196
+ try {
1197
+ const passwordBytes = rememberOwned(password, kdfInputToBytes(password, "password"));
1198
+ const saltBytes = rememberOwned(salt, kdfInputToBytes(salt, "salt"));
1199
+ if (!isU32(passwordBytes.length))
1200
+ throw new Error('"password" must be less of length 1..4Gb');
1201
+ if (!isU32(saltBytes.length) || saltBytes.length < 8)
1202
+ throw new Error('"salt" must be of length 8..4Gb');
1203
+ if (!Object.values(AT).includes(type))
1204
+ throw new Error('"type" was invalid');
1205
+ let { p, dkLen, m, t, version, key, personalization, maxmem, onProgress, asyncTick } = argon2Opts(opts);
1206
+ const keyInput = key;
1207
+ key = rememberOwned(keyInput, abytesOrZero(keyInput, "key"));
1208
+ const personalizationInput = personalization;
1209
+ personalization = rememberOwned(personalizationInput, abytesOrZero(personalizationInput, "personalization"));
1210
+ h = blake2b.create();
1211
+ for (let item of [p, dkLen, m, t, version, type]) {
1212
+ BUF[0] = swap8IfBE(item);
1213
+ h.update(BUF8);
1214
+ }
1215
+ for (let i of [passwordBytes, saltBytes, key, personalization]) {
1216
+ BUF[0] = swap8IfBE(i.length);
1217
+ h.update(BUF8).update(i);
1218
+ }
1219
+ H0 = new Uint32Array(18);
1220
+ h.digestInto(u8(H0));
1221
+ succeeded = true;
1222
+ return { H0, p, dkLen, m, t, version, maxmem, onProgress, asyncTick };
1223
+ } finally {
1224
+ if (h)
1225
+ h.destroy();
1226
+ clean(BUF, ...ownedInputs);
1227
+ if (!succeeded && H0)
1228
+ clean(H0);
1221
1229
  }
1222
- let perBlock = () => {};
1223
- if (onProgress) {
1224
- const totalBlock = t * ARGON2_SYNC_POINTS * p * segmentLen - 2 * p;
1225
- const callbackPer = Math.max(Math.floor(totalBlock / 1e4), 1);
1226
- let blockCnt = 0;
1227
- perBlock = () => {
1228
- blockCnt++;
1229
- if (onProgress && (!(blockCnt % callbackPer) || blockCnt === totalBlock))
1230
- onProgress(blockCnt / totalBlock);
1231
- };
1230
+ }
1231
+ function argon2Init(password, salt, type, opts) {
1232
+ const { H0, p, dkLen, m, t, version, maxmem, onProgress, asyncTick } = argon2InitialHash(password, salt, type, opts);
1233
+ try {
1234
+ const lanes = p;
1235
+ const mP = 4 * p * Math.floor(m / (ARGON2_SYNC_POINTS * p));
1236
+ const laneLen = Math.floor(mP / p);
1237
+ const segmentLen = Math.floor(laneLen / ARGON2_SYNC_POINTS);
1238
+ const memUsed = mP * 1024;
1239
+ if (!isU32(maxmem))
1240
+ throw new Error('"maxmem" expected <2**32, got ' + maxmem);
1241
+ if (memUsed > maxmem)
1242
+ throw new Error('"maxmem" limit was hit: memUsed(mP*1024)=' + memUsed + ", maxmem=" + maxmem);
1243
+ const B = new Uint32Array(memUsed / 4);
1244
+ for (let l = 0;l < p; l++) {
1245
+ const i = 256 * laneLen * l;
1246
+ H0[17] = swap8IfBE(l);
1247
+ H0[16] = swap8IfBE(0);
1248
+ B.set(swap32IfBE(u32(Hp(H0, 1024))), i);
1249
+ H0[16] = swap8IfBE(1);
1250
+ B.set(swap32IfBE(u32(Hp(H0, 1024))), i + 256);
1251
+ }
1252
+ let perBlock = () => {};
1253
+ if (onProgress) {
1254
+ const totalBlock = t * ARGON2_SYNC_POINTS * p * segmentLen - 2 * p;
1255
+ const callbackPer = Math.max(Math.floor(totalBlock / 1e4), 1);
1256
+ let blockCnt = 0;
1257
+ perBlock = () => {
1258
+ blockCnt++;
1259
+ if (onProgress && (!(blockCnt % callbackPer) || blockCnt === totalBlock))
1260
+ onProgress(blockCnt / totalBlock);
1261
+ };
1262
+ }
1263
+ return { type, mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock, asyncTick };
1264
+ } finally {
1265
+ clean(H0);
1232
1266
  }
1233
- clean(BUF, H0);
1234
- return { type, mP, p, t, version, B, laneLen, lanes, segmentLen, dkLen, perBlock, asyncTick };
1235
1267
  }
1236
1268
  function argon2Output(B, p, laneLen, dkLen) {
1237
1269
  const B_final = new Uint32Array(256);
@@ -1242,9 +1274,8 @@ function argon2Output(B, p, laneLen, dkLen) {
1242
1274
  clean(B, B_final);
1243
1275
  return res;
1244
1276
  }
1245
- function* argon2Blocks(ctx) {
1277
+ function* argon2Blocks(ctx, address) {
1246
1278
  const { type, mP, p, t, version, B, laneLen, lanes, segmentLen, perBlock } = ctx;
1247
- const address = new Uint32Array(3 * 256);
1248
1279
  address[256 + 6] = mP;
1249
1280
  address[256 + 8] = t;
1250
1281
  address[256 + 10] = type;
@@ -1298,11 +1329,11 @@ function* argon2Blocks(ctx) {
1298
1329
  }
1299
1330
  function argon2(type, password, salt, opts) {
1300
1331
  const ctx = argon2Init(password, salt, type, opts);
1301
- const blocks = argon2Blocks(ctx);
1332
+ const blocks = argon2Blocks(ctx, new Uint32Array(3 * 256));
1302
1333
  while (!blocks.next().done) {}
1303
1334
  return argon2Output(ctx.B, ctx.p, ctx.laneLen, ctx.dkLen);
1304
1335
  }
1305
- var argon2id = (password, salt, opts) => argon2(AT.Argon2id, password, salt, opts);
1336
+ var argon2id = (password, salt, opts = {}) => argon2(AT.Argon2id, password, salt, opts);
1306
1337
 
1307
1338
  // src/runtime/kdf-worker-thread.js
1308
1339
  if (!parentPort)
package/docs/agents.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Use the JavaScript API for a long-running agent and the canonical CLI for one-off shell work. A local read-only MCP resource server can help an MCP-capable host discover the installed SDK documentation, but it cannot operate Veyl.
4
4
 
5
- The SDK is not a privileged backend. It authenticates, decrypts the vault, signs wallet operations, encrypts/signs chat actions, and maintains encrypted local cache state exactly like web and iOS. Machine credentials and passkeys are authentication capabilities, not human-or-bot classifications. Third-party agents use ordinary public profiles; only namespace-authorized Glyphteck services carry the managed bot marker.
5
+ The SDK is not a privileged backend. It authenticates, decrypts the vault, signs wallet operations, encrypts/signs chat actions, and maintains encrypted local cache state exactly like web and iOS. A public profile is `sdk` while it is machine-only and becomes `app` if a passkey is linked. That describes its available client surface, not whether a person or agent operates it.
6
6
 
7
7
  ## Setup
8
8
 
@@ -101,7 +101,7 @@ Agents should correct normal validation errors from their messages. They should
101
101
  - preserve `operationId`;
102
102
  - do not blindly repeat a Spark send, request payment, external invoice payment, or withdrawal;
103
103
  - reconcile public wallet history or application state first;
104
- - retry Lightning only when `retryable === true`, using the exact same `idempotencyKey`.
104
+ - retry Lightning only when `retryable === true`, using `operationId` as the exact same `transferId`.
105
105
 
106
106
  This is intentionally a small contract for the one class of error where automatic self-correction can lose money. Veyl does not maintain a large error taxonomy for ordinary mistakes.
107
107
 
package/docs/api.md CHANGED
@@ -25,6 +25,16 @@ The auth and account compositions consume explicit runtime ports and have no dir
25
25
 
26
26
  Most product methods ensure login and vault unlock when saved account and vault keys are available. Long-running callers should explicitly login/unlock once and call `close()` when finished.
27
27
 
28
+ ### Local access and connectivity
29
+
30
+ The shared account owner separates `localReady`, `online`, and `connection` in its snapshot. `localReady` means the exact locally signed-in account has enough encrypted bootstrap data for vault unlock; it is not server authorization. Unlock opens encrypted cached chats and wallet history without waiting for cloud services. Cached balance is display-only and must be labeled last known. `online` becomes true after cloud reads and online proof succeed; reconnect attaches services to the same unlocked session. Text sends wait up to 30 seconds for connectivity, then remain encrypted on-device as failed attempts for manual retry; reactions expire without a retry row. Avatar changes wait only for the current session, as described below. These are domain-owned pending operations, not a general cloud mutation queue. Live wallet readiness remains mandatory for payments.
31
+
32
+ Custom platform hosts may provide `bootstrapStorage.read(uid)`, `write(uid, snapshot)`, and `remove(uid)` to `openAccount`, and call `setInternetAvailable(false | true | null)` with platform reachability. Encrypt bootstrap data under the account/environment-bound install key; never put decrypted private content there. Standard web, iOS and Node adapters supply encrypted storage. Logout or observed revocation removes it.
33
+
34
+ Node authentication is currently process-local: a fresh SDK/CLI process still needs the network to authenticate, even with saved credentials. Cached access supports an already-authenticated long-running SDK process; saved username/profile metadata cannot establish an offline session. Web and iOS can restore their platform-persisted signed-in auth slots.
35
+
36
+ `connection.unavailable` is the shared offline-display signal, separate from account readiness in `connection.status`. Ordinary startup, local service initialization, and brief cache refreshes do not mean offline. Explicit device/cloud blocking and network errors report unavailability immediately; silent required server reads use the existing 15-second deadline. The cloud transport's `availability` port reports `false` when explicitly blocked and `null` when unknown, never treating an allowed test phase as proof of a server connection.
37
+
28
38
  ## Graphical auth owner
29
39
 
30
40
  ```js
@@ -158,7 +168,9 @@ await account.close();
158
168
 
159
169
  `openAccount()` owns authenticated user observation, username and avatar publication, vault observation and creation, vault unlock/lock, encrypted settings/network selection, presence, late wallet readiness, public Bitcoin data, support/report commands, chat and peer/profile composition, account switching, and secret-bearing session teardown. Focused-chat presence is separately owned by the chat session's encrypted ephemeral live transport. The account snapshot exposes `user`, `vault`, `vaultReady`, `vaultError`, `session`, `wallet`, `walletError`, `network`, and `lockState`; `vaultReady` becomes true only after the backend authorizes and confirms the current vault snapshot, while a cached snapshot may warm `vault` without authorizing a guarded route. Stable domain owners such as `bitcoin` and `support` live directly on the returned account owner. Graphical password-change flows call `verifyVaultPasswordForChange(currentPassword)` before revealing the new-password step, then call the atomic `changeVaultPassword({ currentPassword, newPassword })` command. Both are account-bound local decryptions; the first immediately clears its temporary seed, while the second preserves and verifies the existing Vault Signature identity before replacing the authoritative ciphertext.
160
170
 
161
- `account.profile` owns the server mutation after a platform has prepared avatar bytes or collected a username. Browser canvas work and native image manipulation remain platform-local; both then call the same `setAvatar`, `clearAvatar`, or `setUsername` command. Successful avatar commands update the shared user owner before returning.
171
+ `account.profile` owns the server mutation after a platform has prepared avatar bytes or collected a username. Browser canvas work and native image manipulation remain platform-local; both then call the same `setAvatar`, `clearAvatar`, or `setUsername` command. Avatar selection/removal previews immediately through the shared user snapshot, waits while offline, and publishes once authenticated profile reads recover. Only the latest selection is retained, in memory for this session; lock/close discards unsent work and rejects its promise with `cancelled`. The promise resolves after server acknowledgment and the confirmed bytes/version enter the ordinary avatar cache. No pending avatar is persisted or compared through extra server reads. Username changes still require connectivity.
172
+
173
+ `account.bitcoin` restores the last observed public USD price from `bitcoinPriceStorage` (`read()` / `write({ price, updatedAt })`). The platform port is shared across accounts within the same installation and realm. The snapshot exposes `priceUpdatedAt` and `priceFromCache`; a null observation time means the configured $80,000 default, never a fetched rate. Cache hydration restores price only, not fee estimates or server/wallet readiness. The existing cloud listener refreshes the saved rate without additional requests. Confirmed self/peer avatar bytes similarly hydrate from their exact cached version independently of cloud readiness.
162
174
 
163
175
  `account.peers` is the shared peer-directory owner used by Node, web, and iOS. `openAccount()` supplies its chat, wallet, blocked-user, and encrypted-cache sources directly, including missing-profile chat cleanup. Graphical adapters only subscribe to the same snapshot and profile selectors already defined by core. `openSearch('profiles')` creates profile-only search; web may also use `openSearch('mainmenu')` for its combined local-action and remote-profile menu. Active searches track peer and blocked-user changes and release those subscriptions when cleared or closed.
164
176
 
@@ -193,7 +205,7 @@ try {
193
205
  }
194
206
  ```
195
207
 
196
- An unknown Spark send, payment-request payment, invoice payment, or withdrawal outcome has `retryable: false`. Reconcile wallet transactions or the named operation before deciding what to do; blindly repeating it can spend twice. An unknown Lightning payment is retryable only when the same caller-supplied `idempotencyKey` will be reused. Direct CLI errors and persistent-session transport preserve these fields instead of reducing them to an error string.
208
+ An unknown Spark send, payment-request payment, invoice payment, or withdrawal outcome has `retryable: false`. Reconcile wallet transactions or the named operation before deciding what to do; blindly repeating it can spend twice. An unknown Lightning payment is retryable only when the same `transferId` will be reused. Veyl creates that UUID before the first attempt and returns it as `operationId` on an uncertain outcome. Direct CLI errors and persistent-session transport preserve these fields instead of reducing them to an error string.
197
209
 
198
210
  ## Account
199
211
 
@@ -214,7 +226,7 @@ await veyl.account.logoutAll();
214
226
  await veyl.account.delete({ confirm: true });
215
227
  ```
216
228
 
217
- - `create` makes an ordinary account authenticated by a local machine credential and returns its account key directly. Authentication does not assign a public bot label.
229
+ - `create` makes an account authenticated by a local machine credential, publishes its account type as `sdk`, and returns its account key directly.
218
230
  - `createPasskey({ username, webUrl, onUrl })` creates a normal passkey account through a browser-assisted WebAuthn flow, installs a local machine credential for future CLI sessions, and returns that account key directly.
219
231
  - `login({ username?, key?, saveKey? })` authenticates with an account key. The key can instead come from `open({ accountKey })` or `VEYL_ACCOUNT_KEY`.
220
232
  - `loginPasskey({ username?, webUrl?, onUrl? })` authenticates through the browser-assisted passkey flow.
@@ -225,7 +237,7 @@ await veyl.account.delete({ confirm: true });
225
237
  - `logoutAll` revokes every product session generation, tears down this runtime locally, and stops a persistent CLI owner after its in-flight work drains.
226
238
  - `delete({ confirm: true, key? })` drains decryptable inbox state, marks all discoverable chats deleted, destroys the complete account/network encrypted cache scope, proves vault possession inside the same destructive operation, deletes identifiable account data, removes the local profile, and stops a persistent CLI owner after its in-flight work drains. `key` is required only when the runtime has no saved vault key.
227
239
 
228
- Account summaries report identity, network, local credential/vault availability, public wallet/chat keys, auth kind, an explicit managed-bot marker when assigned by Glyphteck's owner namespace, and current signed-in/unlocked state. Account and vault keys are never included in summaries.
240
+ Account summaries report identity, network, local credential/vault availability, public wallet/chat keys, auth kind, public `app` or `sdk` account type, and current signed-in/unlocked state. Linking a passkey promotes the type to `app`; account and vault keys are never included in summaries.
229
241
 
230
242
  ## Vault
231
243
 
@@ -242,6 +254,9 @@ Vault creation returns the vault key directly. That key unlocks the vault and ev
242
254
 
243
255
  ```js
244
256
  await veyl.profile.show();
257
+ await veyl.profile.getPresence({ timeoutMs: 15_000 });
258
+ await veyl.profile.setPresenceVisibility('private'); // appear offline on every account device
259
+ await veyl.profile.setPresenceVisibility('public'); // share online status and last active
245
260
  await veyl.profile.uploadAvatar(webpBytes);
246
261
  await veyl.profile.deleteAvatar();
247
262
  await veyl.profile.setChatAdmission({
@@ -257,10 +272,12 @@ await veyl.cache.show();
257
272
  await veyl.cache.clear();
258
273
  ```
259
274
 
260
- `uploadAvatar` accepts prepared WebP bytes and uses the shared profile/avatar backend owner. `setChatAdmission` publishes a public `direct` mode (`open`, `requests`, or `closed`), a `groups` mode (`open` or `closed`), and a fixed-size padded set of private pair-capability commitments for up to eight allowed direct peers. Allowed peers may be usernames or public chat keys; their identities are resolved locally and are not published in the policy. Missing policies remain open for ordinary accounts, while malformed present policies fail closed. Existing chats retain their private routes.
275
+ `uploadAvatar` accepts prepared WebP bytes and uses the shared profile/avatar backend owner. `setChatAdmission` publishes a public `direct` mode (`open`, `requests`, or `closed`), a `groups` mode (`open` or `closed`), and a fixed-size padded set of private pair-capability commitments for up to eight allowed direct peers. Allowed peers may be usernames or public chat keys; their identities are resolved locally and are not published in the policy. Missing policies remain open for ordinary accounts, while malformed present policies fail closed. Existing membership does not bypass a closed policy: disallowed chats are hidden and unavailable for admission-controlled actions, but policy changes never delete a direct or leave a group. `groups: closed` also prevents new group membership.
261
276
 
262
277
  Settings use the shared normalization and encrypted settings document. Changing `walletNetwork` locks the current vault so the next unlock boots the selected network. Cache methods operate on the same vault-encrypted display and media cache as the other clients.
263
278
 
279
+ Presence visibility is an independent account-wide relay policy, not an encrypted settings field. `getPresence` waits for the acknowledged policy and returns `{ visibility, pending, error, availability, status, lastActiveAt }`. `setPresenceVisibility` accepts only `public` or `private` and resolves after the shared presence owner confirms the revisioned change; connection, conflict, and timeout errors reject explicitly. An unloaded policy never implies public visibility. The matching CLI commands are `veyl profile presence` and `veyl profile presence-set <public|private>`.
280
+
264
281
  ## Peers
265
282
 
266
283
  ```js
@@ -272,7 +289,9 @@ await veyl.peers.block('@alice');
272
289
  await veyl.peers.unblock('@alice');
273
290
  ```
274
291
 
275
- Peer resolution accepts `@username`, username, chat public key, or wallet public key when the operation supports it. Blocking self is rejected. Peer/profile results are public projections and do not expose local cache internals. `peers.show` authoritatively refreshes a cached identity; a confirmed missing profile evicts it and deletes loaded chats through the shared missing-peer owner. Normal chat/wallet actions keep the shared cached fast path, while their server-side operations still validate the authoritative route they mutate.
292
+ Peer resolution accepts `@username`, username, chat public key, or wallet public key when the operation supports it. Blocking self is rejected. A block independently submits a narrow user report, retires every private chat route containing that peer, and returns `reported` separately from `blocked`; report failure never prevents the block. Peer/profile results are public projections and do not expose local cache internals. `peers.show` authoritatively refreshes a cached identity; a confirmed missing profile evicts it and deletes loaded chats through the shared missing-peer owner. Normal chat/wallet actions keep the shared cached fast path, while their server-side operations still validate the authoritative route they mutate.
293
+
294
+ Profile and peer results include `presence: { availability, status, lastActiveAt }`; `active` is derived only from `availability === 'online'`, never from a stored profile flag. Availability is `unknown`, `online`, or `offline`; unknown/private/unobserved peers must not be presented as confirmed offline. Last-active timestamps are coarse buckets, not exact interaction times. Presence reflects the current bounded observation set; `@active` / `@online` search filters observed local peers rather than querying a global directory of online users.
276
295
 
277
296
  ## Chat
278
297
 
@@ -326,7 +345,7 @@ await veyl.chat.react('@alice', sent.message.id, '+1');
326
345
  await veyl.chat.unreact('@alice', sent.message.id);
327
346
  await veyl.chat.save('@alice', sent.message.id);
328
347
  await veyl.chat.unsave('@alice', sent.message.id);
329
- await veyl.chat.update('@alice', sent.message.id, 'edited');
348
+ await veyl.chat.update('@alice', sent.message.id, 'edited'); // own text, strictly within 10 minutes
330
349
  await veyl.chat.delete('@alice', sent.message.id);
331
350
  await veyl.chat.retention('@alice', '24h');
332
351
  await veyl.chat.deleteChat('@alice', { cleanup: true });
@@ -419,7 +438,7 @@ await veyl.lightning.quote(invoice.encodedInvoice, { amountSats: 10 });
419
438
  await veyl.lightning.pay(invoice.encodedInvoice, {
420
439
  amountSats: 10,
421
440
  maxFeeSats: 5,
422
- idempotencyKey: 'order-123',
441
+ transferId: '019c0000-0000-7000-8000-000000000001',
423
442
  });
424
443
  await veyl.lightning.receive(receiveId);
425
444
  await veyl.lightning.send(sendId);
@@ -552,7 +571,7 @@ Losing the root loses every derived account. Back it up as carefully as a wallet
552
571
 
553
572
  An existing account cannot retroactively acquire a derived master seed. Adopting one requires a one-time authenticated handoff that installs its root-derived account key and encrypts the existing master seed under its root-derived vault key without changing wallet/chat material.
554
573
 
555
- The Glyphteck namespace seed is separate from a fleet root. `veyl namespace init` creates that owner-only local key once. When its default file exists under the fleet `homeDir`, provisioning automatically signs a short-lived claim bound to the exact derived machine credential so a reserved canonical username can use the normal public account-creation transaction. The seed is never stored in the manifest or an account profile. Other fleet operators do not have this key and cannot claim the reserved namespace.
574
+ The Veyl namespace seed is separate from a fleet root. `veyl namespace init` creates that owner-only local key once. When its default file exists under the fleet `homeDir`, provisioning automatically signs a short-lived claim bound to the exact derived machine credential so a reserved canonical username can use the normal public account-creation transaction. The seed is never stored in the manifest or an account profile. Other fleet operators do not have this key and cannot claim the reserved namespace.
556
575
 
557
576
  The owner opens one ordinary public client per enabled ready profile, runs account boots with bounded concurrency, tags events with their manifest account, preserves per-account policy order, fails visibly on backlog overflow, and closes every client on stop. An owner-only PID lock prevents two local processes from operating the fleet. It reclaims a dead same-host PID after a crash but never signals or replaces a healthy owner. Shared owners still control chat ordering, wallet serialization, encryption, cache, and account revocation.
558
577