@dotrino/vault 0.93.0 → 0.93.1

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.
@@ -285,10 +285,10 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
285
285
  *
286
286
  * La cola es de este proceso, que es el único que escribe este archivo.
287
287
  */
288
- let cola = Promise.resolve()
289
- const enFila = (fn) => {
290
- const r = cola.then(fn, fn)
291
- cola = r.then(() => {}, () => {})
288
+ let queue = Promise.resolve()
289
+ const queued = (fn) => {
290
+ const r = queue.then(fn, fn)
291
+ queue = r.then(() => {}, () => {})
292
292
  return r
293
293
  }
294
294
 
@@ -309,13 +309,13 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
309
309
  */
310
310
  const gcKeyring = (bag, k, owner) => {
311
311
  if (!bag[k]) return
312
- const vivos = new Set()
312
+ const aliveOnes = new Set()
313
313
  // TAMBIÉN LAS PÚBLICAS. Se saltaban porque antes no tenían generación —se guardaban en
314
314
  // claro—, así que su llave se recogía por «no la usa nadie» y la variable quedaba
315
315
  // ilegible. Desde 2026-09-02 van en sobre como todas y cuentan como todas.
316
- for (const e of Object.values(bag[k].vars || {})) if (e.gen != null) vivos.add(e.gen)
317
- for (const h of data.history) if (h.owner === owner && h.gen != null) vivos.add(h.gen)
318
- bag[k].keyring = (bag[k].keyring || []).filter((g) => vivos.has(g.gen))
316
+ for (const e of Object.values(bag[k].vars || {})) if (e.gen != null) aliveOnes.add(e.gen)
317
+ for (const h of data.history) if (h.owner === owner && h.gen != null) aliveOnes.add(h.gen)
318
+ bag[k].keyring = (bag[k].keyring || []).filter((g) => aliveOnes.has(g.gen))
319
319
  }
320
320
 
321
321
  /**
@@ -350,9 +350,9 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
350
350
  */
351
351
  const wrapAll = async (cek, owner, isPublic = false) => {
352
352
  const members = (await recipients?.(owner, { public: isPublic })) || []
353
- const { wraps, sinLlave } = await sealer.wrapFor(cek, members)
353
+ const { wraps, sinLlave: noKey } = await sealer.wrapFor(cek, members)
354
354
  wraps[RECOVERY] = await sealer.wrapForKey(cek, data.recovery.pub)
355
- return { wraps, sinLlave }
355
+ return { wraps, sinLlave: noKey }
356
356
  }
357
357
 
358
358
  return {
@@ -375,7 +375,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
375
375
  *
376
376
  * Idempotente: si ya está, no toca nada.
377
377
  */
378
- async ensureRecovery (adminKey = null) { return enFila(async () => (await ensureRecovery(adminKey)).pub) },
378
+ async ensureRecovery (adminKey = null) { return queued(async () => (await ensureRecovery(adminKey)).pub) },
379
379
 
380
380
  /**
381
381
  * MUCHAS ESCRITURAS, UN GUARDADO. Cargar la configuración de un servicio son veinte
@@ -410,13 +410,13 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
410
410
  */
411
411
  bundleFor (ns, devicePub = null, { publicOnly = false } = {}) {
412
412
  assertNs(ns)
413
- const todo = { ...entriesOf(data.ns, ns), ...(devicePub ? entriesOf(data.dev, devicePub) : {}) }
413
+ const everything = { ...entriesOf(data.ns, ns), ...(devicePub ? entriesOf(data.dev, devicePub) : {}) }
414
414
  // EL FILTRO LO HACE LA BÓVEDA, no quien pide. Si mandara todo y el cliente eligiera,
415
415
  // pedir «solo públicas» sería la forma de saltarse la aprobación y llevarse las
416
416
  // privadas igual — o sea justo lo contrario de lo que se quiere.
417
417
  const entries = publicOnly
418
- ? Object.fromEntries(Object.entries(todo).filter(([, e]) => e.pub))
419
- : todo
418
+ ? Object.fromEntries(Object.entries(everything).filter(([, e]) => e.pub))
419
+ : everything
420
420
  if (isLegacy()) return { legacy: true, entries }
421
421
 
422
422
  const misWraps = (bag, k) => (bag[k]?.keyring || [])
@@ -628,7 +628,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
628
628
  return this._put(data.dev, pub, `dev:${pub}`, key, value, isPublic, by)
629
629
  },
630
630
  /** @private */
631
- async _put (...a) { return enFila(() => this._putRaw(...a)) },
631
+ async _put (...a) { return queued(() => this._putRaw(...a)) },
632
632
  /** @private El cuerpo, ya en fila (ver `enFila`). */
633
633
  async _putRaw (bag, k, owner, key, value, isPublic, by) {
634
634
  // El cajón del perfil tiene su propio alfabeto (`nickname`, no `NICKNAME`). Por aquí
@@ -659,7 +659,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
659
659
  // CEK NUEVA, siempre: no se puede reutilizar la de antes sin poder abrirla.
660
660
  const cek = await sealer.newKey()
661
661
  const gen = nextGen(bag, k, owner)
662
- const { wraps, sinLlave } = await wrapAll(cek, owner, pub)
662
+ const { wraps, sinLlave: noKey } = await wrapAll(cek, owner, pub)
663
663
  const e = await sealer.encrypt(cek, value, gen)
664
664
  // La FIRMA dice que este sobre salió de esta bóveda, y con qué acta (§8.8). Si no
665
665
  // hay con qué firmar, el sobre sale sin firma: guardar es más importante.
@@ -670,7 +670,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
670
670
  bag[k].keyring = [...(bag[k].keyring || []), { gen, createdAt: Date.now(), wraps }]
671
671
  gcKeyring(bag, k, owner)
672
672
  save()
673
- return { gen, sinLlave }
673
+ return { gen, sinLlave: noKey }
674
674
  },
675
675
 
676
676
  /**
@@ -693,18 +693,18 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
693
693
  * después ni abriendo la bóveda.
694
694
  *
695
695
  * @param {string} owner `ns:<scope>` o `dev:<pub>`
696
- * @param {{e:{iv:string,ct:string,gen?:number}, wraps:Object}} sobre
696
+ * @param {{e:{iv:string,ct:string,gen?:number}, wraps:Object}} envelope
697
697
  */
698
- async putSealed (owner, key, sobre, { by = null, public: isPublic = false } = {}) {
699
- return enFila(() => this._putSealedRaw(owner, key, sobre, by, !!isPublic))
698
+ async putSealed (owner, key, envelope, { by = null, public: isPublic = false } = {}) {
699
+ return queued(() => this._putSealedRaw(owner, key, envelope, by, !!isPublic))
700
700
  },
701
701
  /** @private */
702
- async _putSealedRaw (owner, key, sobre, by, isPublic) {
702
+ async _putSealedRaw (owner, key, envelope, by, isPublic) {
703
703
  if (isLegacy()) throw new Error('putSealed: this store is still v3 (run `migrate` first)')
704
704
  if (needsMigration()) throw new NeedsMigration()
705
705
  const [kind, k] = splitOwner(owner)
706
706
  const bag = kind === 'ns' ? data.ns : data.dev
707
- const e = sobre?.e; const wraps = sobre?.wraps
707
+ const e = envelope?.e; const wraps = envelope?.wraps
708
708
  if (!e || typeof e.iv !== 'string' || typeof e.ct !== 'string') {
709
709
  throw new Error('putSealed: the envelope must carry `iv` and `ct`')
710
710
  }
@@ -823,7 +823,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
823
823
  return this._setVis(data.dev, pub, `dev:${pub}`, key, isPublic, adminKey)
824
824
  },
825
825
  /** @private */
826
- async _setVis (...a) { return enFila(() => this._setVisRaw(...a)) },
826
+ async _setVis (...a) { return queued(() => this._setVisRaw(...a)) },
827
827
  /** @private El cuerpo, ya en fila. */
828
828
  async _setVisRaw (bag, k, owner, key, isPublic, adminKey) {
829
829
  const vars = varsOf(bag, k)
@@ -930,7 +930,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
930
930
  *
931
931
  * Y por eso ESTO sí pide la frase: heredar lo viejo obliga a abrirlo.
932
932
  */
933
- async rewrap (...a) { return enFila(() => this._rewrap(...a)) },
933
+ async rewrap (...a) { return queued(() => this._rewrap(...a)) },
934
934
  /** Los cajones que existen, para poder recorrerlos todos (`ns:…` y `dev:…`). */
935
935
  owners () {
936
936
  return [...Object.keys(data.ns).map((k) => `ns:${k}`), ...Object.keys(data.dev).map((k) => `dev:${k}`)]
@@ -1004,7 +1004,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1004
1004
  * de los secretos se queda intacta.
1005
1005
  */
1006
1006
  async profilePutPublic (key, value, { by = null } = {}) {
1007
- return enFila(async () => {
1007
+ return queued(async () => {
1008
1008
  if (isLegacy()) throw new Error('profile: this store is still v3 (run `migrate` first)')
1009
1009
  if (needsMigration()) throw new NeedsMigration()
1010
1010
  assertProfileKey(key)
@@ -1039,9 +1039,9 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1039
1039
  const priv = await openRecovery(adminKey)
1040
1040
  let wrapped = 0
1041
1041
  let sealed = 0
1042
- const sinLlave = new Set()
1042
+ const noKey = new Set()
1043
1043
  const vars = varsOf(bag, k)
1044
- const paraVisibilidad = async (esPublica) => (typeof members === 'function' ? members(esPublica) : members)
1044
+ const forVisibility = async (isPublic) => (typeof members === 'function' ? members(isPublic) : members)
1045
1045
 
1046
1046
  // UN VALOR QUE SE QUEDÓ EN CLARO SE SELLA AQUÍ, que es el único sitio donde se puede.
1047
1047
  //
@@ -1060,27 +1060,27 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1060
1060
  if (typeof e.v !== 'string' || e.e) continue
1061
1061
  const cek = await sealer.newKey()
1062
1062
  const gen = nextGen(bag, k, owner)
1063
- const r = await sealer.wrapFor(cek, await paraVisibilidad(!!e.pub))
1063
+ const r = await sealer.wrapFor(cek, await forVisibility(!!e.pub))
1064
1064
  r.wraps[RECOVERY] = await sealer.wrapForKey(cek, data.recovery.pub)
1065
- const sobre = await sealer.encrypt(cek, e.v, gen)
1066
- const seal = signer ? await signer({ owner, key, gen, iv: sobre.iv, ct: sobre.ct }) : null
1065
+ const envelope = await sealer.encrypt(cek, e.v, gen)
1066
+ const seal = signer ? await signer({ owner, key, gen, iv: envelope.iv, ct: envelope.ct }) : null
1067
1067
  // `pub` y `at` se conservan: no es un valor nuevo, es el mismo mejor guardado.
1068
- vars[key] = { pub: !!e.pub, owner, gen, e: sobre, seal, at: e.at || Date.now(), by: e.by || null }
1068
+ vars[key] = { pub: !!e.pub, owner, gen, e: envelope, seal, at: e.at || Date.now(), by: e.by || null }
1069
1069
  bag[k].keyring = [...(bag[k].keyring || []), { gen, createdAt: Date.now(), wraps: r.wraps }]
1070
- for (const x of r.sinLlave) sinLlave.add(x)
1070
+ for (const x of r.sinLlave) noKey.add(x)
1071
1071
  sealed++
1072
1072
  }
1073
1073
 
1074
1074
  // De qué variable es cada generación: es lo que dice si esa CEK es de una pública.
1075
1075
  // Se calcula DESPUÉS de sellar, para que las generaciones recién creadas entren.
1076
- const visibilidad = new Map()
1077
- for (const e of Object.values(vars)) if (e.gen != null) visibilidad.set(e.gen, !!e.pub)
1078
- const paraGen = async (gen) => paraVisibilidad(visibilidad.get(gen) === true)
1076
+ const visibility = new Map()
1077
+ for (const e of Object.values(vars)) if (e.gen != null) visibility.set(e.gen, !!e.pub)
1078
+ const forGen = async (gen) => forVisibility(visibility.get(gen) === true)
1079
1079
  for (const g of bag[k].keyring || []) {
1080
1080
  const w = g.wraps?.[RECOVERY]
1081
1081
  if (!w) continue
1082
1082
  const cek = await sealer.openWrapWith(priv, w)
1083
- const r = await sealer.wrapFor(cek, await paraGen(g.gen))
1083
+ const r = await sealer.wrapFor(cek, await forGen(g.gen))
1084
1084
  // `exact`: el llavero queda con lo que dice el acta y NADA más. Es lo que hace
1085
1085
  // falta para rehacerlo al abrir la bóveda:
1086
1086
  // · una envoltura basura que metió alguien se reemplaza por la buena;
@@ -1092,11 +1092,11 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1092
1092
  // OJO con lo que esto NO es: quitarle la envoltura a alguien no le quita lo que
1093
1093
  // ya leyó ni la llave que se haya guardado. Cortar de verdad es `rotate`.
1094
1094
  g.wraps = exact ? { ...r.wraps, [RECOVERY]: w } : { ...g.wraps, ...r.wraps }
1095
- for (const s of r.sinLlave) sinLlave.add(s)
1095
+ for (const s of r.sinLlave) noKey.add(s)
1096
1096
  wrapped += Object.keys(r.wraps).length
1097
1097
  }
1098
1098
  save()
1099
- return { wrapped, sealed, sinLlave: [...sinLlave] }
1099
+ return { wrapped, sealed, sinLlave: [...noKey] }
1100
1100
  },
1101
1101
 
1102
1102
  /**
@@ -1114,7 +1114,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1114
1114
  * El HISTÓRICO se muda con ella: es la misma variable, y dejarlo bajo el nombre viejo
1115
1115
  * lo volvería inalcanzable — se vería como un hueco, no como una decisión.
1116
1116
  */
1117
- async rename (owner, from, to) { return enFila(() => this._renameRaw(owner, from, to)) },
1117
+ async rename (owner, from, to) { return queued(() => this._renameRaw(owner, from, to)) },
1118
1118
  /** @private */
1119
1119
  async _renameRaw (owner, from, to) {
1120
1120
  if (needsMigration()) throw new NeedsMigration()
@@ -1148,7 +1148,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1148
1148
  *
1149
1149
  * No devuelve lo que el expulsado ya leyó. Eso no se puede deshacer y no se promete.
1150
1150
  */
1151
- async rotate (...a) { return enFila(() => this._rotate(...a)) },
1151
+ async rotate (...a) { return queued(() => this._rotate(...a)) },
1152
1152
  /** @private */
1153
1153
  async _rotate (owner, members, adminKey = null) {
1154
1154
  needSealer('rotate the key of a drawer')
@@ -1159,30 +1159,30 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1159
1159
  const priv = await openRecovery(adminKey)
1160
1160
 
1161
1161
  const vars = bag[k].vars || {}
1162
- const claras = {}
1162
+ const plainOnes = {}
1163
1163
  for (const [key, e] of Object.entries(vars)) {
1164
- if (!e.pub) claras[key] = await sealer.openValue(await this._cekOf(bag, k, e.gen, priv), e.e)
1164
+ if (!e.pub) plainOnes[key] = await sealer.openValue(await this._cekOf(bag, k, e.gen, priv), e.e)
1165
1165
  }
1166
1166
  // Se tira el llavero entero: las generaciones viejas son justamente lo que el que
1167
1167
  // se fue podría abrir. Con ellas se va el histórico de este cajón, que estaba
1168
1168
  // cifrado con ellas — rotar es renunciar a poder revertir lo de antes.
1169
1169
  bag[k].keyring = []
1170
1170
  data.history = data.history.filter((h) => h.owner !== owner)
1171
- const sinLlave = new Set()
1171
+ const noKey = new Set()
1172
1172
  let gen = 0
1173
- for (const [key, value] of Object.entries(claras)) {
1173
+ for (const [key, value] of Object.entries(plainOnes)) {
1174
1174
  const cek = await sealer.newKey()
1175
1175
  gen += 1
1176
- const { wraps, sinLlave: faltan } = await sealer.wrapFor(cek, members)
1176
+ const { wraps, sinLlave: missingOnes } = await sealer.wrapFor(cek, members)
1177
1177
  wraps[RECOVERY] = await sealer.wrapForKey(cek, data.recovery.pub)
1178
- for (const s of faltan) sinLlave.add(s)
1178
+ for (const s of missingOnes) noKey.add(s)
1179
1179
  const e = await sealer.encrypt(cek, value, gen)
1180
1180
  const seal = signer ? await signer({ owner, key, gen, iv: e.iv, ct: e.ct }) : null
1181
1181
  vars[key] = { pub: false, owner, gen, e, seal, at: Date.now(), by: null }
1182
1182
  bag[k].keyring.push({ gen, createdAt: Date.now(), wraps })
1183
1183
  }
1184
1184
  save()
1185
- return { rotated: Object.keys(claras).length, sinLlave: [...sinLlave], gen, cambio: true }
1185
+ return { rotated: Object.keys(plainOnes).length, sinLlave: [...noKey], gen, cambio: true }
1186
1186
  },
1187
1187
 
1188
1188
  /**
@@ -1233,7 +1233,7 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1233
1233
  *
1234
1234
  * `null` en cualquiera de las dos significa «la del perfil sin contraseña».
1235
1235
  */
1236
- async rekeyRecovery (...a) { return enFila(() => this._rekeyRecovery(...a)) },
1236
+ async rekeyRecovery (...a) { return queued(() => this._rekeyRecovery(...a)) },
1237
1237
  /**
1238
1238
  * ¿ESTA llave abre la copia de recuperación? Lanza si no.
1239
1239
  *
@@ -1286,19 +1286,19 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1286
1286
  * el original, y solo entonces se escribe. Si algo no cuadra no se toca nada y se
1287
1287
  * lanza: media migración es peor que ninguna.
1288
1288
  */
1289
- async migrate (...a) { return enFila(() => this._migrate(...a)) },
1289
+ async migrate (...a) { return queued(() => this._migrate(...a)) },
1290
1290
  /** @private */
1291
1291
  async _migrate (membersOf, adminKey = null) {
1292
1292
  if (data.schemaVersion === SCHEMA_VERSION) return { migrated: false, reason: 'already-v5' }
1293
1293
  needSealer('seal the store')
1294
- const desde = data.schemaVersion
1294
+ const since = data.schemaVersion
1295
1295
 
1296
1296
  // De v4: las CEK viejas viven en la copia maestra, y para abrirla hace falta la
1297
1297
  // frase. Es la única vez que se pide.
1298
- const master = desde === SEALED_MASTER_VERSION ? await sealer.openMaster(data.master, keyOr(adminKey)) : null
1299
- const claro = async (owner, e) => {
1298
+ const master = since === SEALED_MASTER_VERSION ? await sealer.openMaster(data.master, keyOr(adminKey)) : null
1299
+ const plain = async (owner, e) => {
1300
1300
  if (e.pub) return e.v
1301
- if (desde === LEGACY_VERSION) return e.v
1301
+ if (since === LEGACY_VERSION) return e.v
1302
1302
  return sealer.decrypt(master, e.e, e.owner || owner)
1303
1303
  }
1304
1304
 
@@ -1312,12 +1312,12 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1312
1312
  history: []
1313
1313
  }
1314
1314
  const antes = {}
1315
- const sinLlave = {}
1315
+ const noKey = {}
1316
1316
 
1317
1317
  for (const [kind, src, dst] of [['ns', data.ns, next.ns], ['dev', data.dev, next.dev]]) {
1318
1318
  for (const [k, bag] of Object.entries(src)) {
1319
1319
  const owner = `${kind}:${k}`
1320
- const entradas = desde === LEGACY_VERSION ? (bag || {}) : (bag?.vars || {})
1320
+ const entries = since === LEGACY_VERSION ? (bag || {}) : (bag?.vars || {})
1321
1321
  const vars = {}
1322
1322
  const keyring = []
1323
1323
  // UNA SOLA generación para todo el cajón, y no es una excepción a «una por
@@ -1328,8 +1328,8 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1328
1328
  // sin apagar a nadie. Lo que se escriba DESPUÉS ya estrena generación.
1329
1329
  const gen = 1
1330
1330
  let cek = null
1331
- for (const [key, e] of Object.entries(entradas)) {
1332
- const value = await claro(owner, e)
1331
+ for (const [key, e] of Object.entries(entries)) {
1332
+ const value = await plain(owner, e)
1333
1333
  antes[`${owner}\u0000${key}`] = value
1334
1334
  // LA PÚBLICA TAMBIÉN SE SELLA (dueño, 2026-09-02). Aquí se dejaba en claro,
1335
1335
  // así que un archivo migrado se quedaba con valores legibles en el disco —
@@ -1337,14 +1337,14 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1337
1337
  // a quién se le despacha sin aprobación, no cómo se guarda.
1338
1338
  if (!cek) {
1339
1339
  cek = await sealer.newKey()
1340
- const { wraps, sinLlave: faltan } = await sealer.wrapFor(cek, (await membersOf(owner)) || [])
1340
+ const { wraps, sinLlave: missingOnes } = await sealer.wrapFor(cek, (await membersOf(owner)) || [])
1341
1341
  wraps[RECOVERY] = await sealer.wrapForKey(cek, next.recovery.pub)
1342
- if (faltan.length) sinLlave[owner] = faltan
1342
+ if (missingOnes.length) noKey[owner] = missingOnes
1343
1343
  keyring.push({ gen, createdAt: Date.now(), wraps })
1344
1344
  }
1345
- const sobre = await sealer.encrypt(cek, value, gen)
1346
- const seal = signer ? await signer({ owner, key, gen, iv: sobre.iv, ct: sobre.ct }) : null
1347
- vars[key] = { pub: !!e.pub, owner, gen, e: sobre, seal, at: Date.now(), by: null }
1345
+ const envelope = await sealer.encrypt(cek, value, gen)
1346
+ const seal = signer ? await signer({ owner, key, gen, iv: envelope.iv, ct: envelope.ct }) : null
1347
+ vars[key] = { pub: !!e.pub, owner, gen, e: envelope, seal, at: Date.now(), by: null }
1348
1348
  }
1349
1349
  dst[k] = { vars, keyring }
1350
1350
  }
@@ -1359,8 +1359,8 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1359
1359
  // Se abre TODO, también las públicas: ahora van selladas igual, así que la
1360
1360
  // comprobación de que la migración no cambió ningún valor vale para las dos.
1361
1361
  const g = bag.keyring.find((x) => x.gen === e.gen)
1362
- const abierto = await sealer.openValue(await sealer.openWrapWith(priv, g.wraps[RECOVERY]), e.e)
1363
- if (abierto !== antes[`${owner}\u0000${key}`]) {
1362
+ const isOpen = await sealer.openValue(await sealer.openWrapWith(priv, g.wraps[RECOVERY]), e.e)
1363
+ if (isOpen !== antes[`${owner}\u0000${key}`]) {
1364
1364
  throw new Error(`secrets: the migration check failed on ${owner}/${key}; nothing was touched`)
1365
1365
  }
1366
1366
  }
@@ -1369,10 +1369,10 @@ export function openSecretsStore (disk, { sealer = null, recipients = null, sign
1369
1369
 
1370
1370
  // Copia de lo anterior antes de pisarlo. Con nodos en producción, deshacer tiene
1371
1371
  // que ser un `mv`, no una restauración. La borra el operador a mano.
1372
- writeJson(disk, `${file}.v${desde}.bak`, data)
1372
+ writeJson(disk, `${file}.v${since}.bak`, data)
1373
1373
  data = next
1374
1374
  flush()
1375
- return { migrated: true, from: desde, sinLlave }
1375
+ return { migrated: true, from: since, sinLlave: noKey }
1376
1376
  }
1377
1377
  }
1378
1378
  }
@@ -59,7 +59,7 @@ export async function isAcceptable (e) {
59
59
  export function openSubacta (disk) {
60
60
  const file = 'subacta.json'
61
61
  let data = readJson(disk, file, null) || { v: 1, entries: [] }
62
- const guardar = () => writeJson(disk, file, data)
62
+ const save = () => writeJson(disk, file, data)
63
63
 
64
64
  return {
65
65
  /** Las entradas vigentes, tal cual se le pasan a `memberCan` y compañía. */
@@ -79,7 +79,7 @@ export function openSubacta (disk) {
79
79
  const id = idOf(e)
80
80
  if (data.entries.some((x) => idOf(x) === id)) return { ok: false, reason: 'already-present' }
81
81
  data.entries = [...data.entries, e]
82
- guardar()
82
+ save()
83
83
  return { ok: true, reason: 'new' }
84
84
  },
85
85
 
@@ -92,10 +92,10 @@ export function openSubacta (disk) {
92
92
 
93
93
  /** Vacía lo ya absorbido. Se le pasa lo que se absorbió, no «todo»: mientras se sella
94
94
  * puede haber entrado una nueva, y tirarla sería perder una renuncia sin aplicar. */
95
- clear (absorbidas) {
96
- const ids = new Set(absorbidas.map(idOf))
95
+ clear (absorbed) {
96
+ const ids = new Set(absorbed.map(idOf))
97
97
  data.entries = data.entries.filter((e) => !ids.has(idOf(e)))
98
- guardar()
98
+ save()
99
99
  }
100
100
  }
101
101
  }
@@ -235,7 +235,7 @@ export function openThreadStore (disk) {
235
235
  // Solo sale lo que ya cumplió su año de respaldo.
236
236
  const antes = data.resealed.length
237
237
  data.resealed = data.resealed.filter((x) => now - (x.at || 0) < RESEALED_KEEP_MS)
238
- const podados = antes - data.resealed.length
238
+ const pruned = antes - data.resealed.length
239
239
  for (const [key, arr] of Object.entries(data.threads)) {
240
240
  for (let i = 0; i < arr.length; i++) {
241
241
  const r = await resealStale(arr[i], { gen, reseal })
@@ -246,7 +246,7 @@ export function openThreadStore (disk) {
246
246
  changed += r.changed; entries++
247
247
  }
248
248
  }
249
- if (entries || podados) save()
249
+ if (entries || pruned) save()
250
250
  return { changed, entries, skipped }
251
251
  }
252
252
 
@@ -67,38 +67,38 @@ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey
67
67
  // La puerta tiene que decidir AL INSTANTE (la señal llega y o se atiende o no), y pedir
68
68
  // el acta es asíncrono. Así que se guarda una copia y se refresca donde ya se refresca
69
69
  // todo: en `identify`, que es lo que corre cada vez que el acta cambia o se reconecta.
70
- let actaVista = null
70
+ let seenActa = null
71
71
  // ¿Está esta bóveda ATADA al proxio ahora mismo? Conectado no es identificado: el socket
72
72
  // puede estar perfectamente abierto y el sobre rechazado, y entonces nadie la alcanza por
73
73
  // su pubkey. Se expone para que `status` lo enseñe en vez de dejarlo a un log del arranque.
74
- let identificado = false
75
- const enElActa = (token) => {
74
+ let identified = false
75
+ const inActa = (token) => {
76
76
  // SIN ACTA NO SE NEGOCIA. No se sabe quién es nadie, y en la duda no se arranca a
77
77
  // parsear red no confiable. Se sigue por el proxio, que es el escalón que siempre está.
78
- if (!actaVista) return false
79
- return (actaVista.members || []).some((m) => m?.pub === token)
78
+ if (!seenActa) return false
79
+ return (seenActa.members || []).some((m) => m?.pub === token)
80
80
  }
81
- const client = makeClient ? makeClient({ url, acceptDirectFrom: enElActa }) : new WebSocketProxyClient({
82
- url, enableWebRTC: true, acceptDirectFrom: enElActa, autoReconnect: true,
81
+ const client = makeClient ? makeClient({ url, acceptDirectFrom: inActa }) : new WebSocketProxyClient({
82
+ url, enableWebRTC: true, acceptDirectFrom: inActa, autoReconnect: true,
83
83
  maxReconnectAttempts: 100000, reconnectDelay: 4000
84
84
  })
85
85
 
86
86
  await client.connect()
87
87
 
88
88
  const identify = async () => {
89
- identificado = false
89
+ identified = false
90
90
  if (!client.token) return
91
91
  const record = (await identity.profileActa?.().catch(() => null))?.acta || null
92
- actaVista = record // la copia que mira la puerta del canal directo
92
+ seenActa = record // la copia que mira la puerta del canal directo
93
93
  const comm = commKey?.pub?.() || null
94
94
  // ¿Nos nombra el acta? Con `cn` o sin él, lo que el proxio mira es la pertenencia.
95
- const esMiembro = comm && (record?.members || []).some((m) => m?.pub === comm)
95
+ const isMember = comm && (record?.members || []).some((m) => m?.pub === comm)
96
96
 
97
- if (comm && esMiembro) {
97
+ if (comm && isMember) {
98
98
  // El sobre lo arma el pilar (`identifyAs`), que le pone el destinatario.
99
99
  try {
100
100
  await client.identifyAs({ publickey: comm, sign: (d) => commKey.sign(d), acta: record })
101
- identificado = true
101
+ identified = true
102
102
  // SE DICE QUE SE IDENTIFICÓ, y con qué llave. Este camino era MUDO: al pasar de la
103
103
  // maestra a la llave de comunicación, el único rastro de «estoy alcanzable» que
104
104
  // quedaba en el log era el del repliegue. Así que cuando la consola decía «no
@@ -133,7 +133,7 @@ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey
133
133
  if (!publickey) return
134
134
  log('[vault] identifying with the master key: this vault is not in its own record yet (open the profile once to fix it)')
135
135
  await client.identifyAs({ publickey, sign: (d) => identity.signData(d), acta: record })
136
- identificado = true
136
+ identified = true
137
137
  } catch (e) {
138
138
  log(`[vault] cannot identify on the proxy yet: ${e.message} — the profile still opens; unlock it once and it gets its own communication key`)
139
139
  }
@@ -175,7 +175,7 @@ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey
175
175
  */
176
176
  let announcedAt = 0
177
177
  const announceAccount = async () => {
178
- if (!announce || !identificado) return
178
+ if (!announce || !identified) return
179
179
  announcedAt = Date.now()
180
180
  try { await client.publish(announce) } catch (e) {
181
181
  log(`[vault] could not announce this account on the proxy (${e.message}) — a borrowed machine will not find this vault by its address`)
@@ -190,38 +190,38 @@ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey
190
190
  * lo que se pierde es que la despierten, y eso no se puede callar.
191
191
  */
192
192
  const registerWake = async () => {
193
- if (!wakeUrl || !identificado) return
193
+ if (!wakeUrl || !identified) return
194
194
  const comm = commKey?.pub?.() || null
195
- if (!comm || !actaVista) { log('[vault] cannot register the wake URL: no communication key in the record yet'); return }
195
+ if (!comm || !seenActa) { log('[vault] cannot register the wake URL: no communication key in the record yet'); return }
196
196
  try {
197
- await client.registerWake({ publicKey: comm, sign: (d) => commKey.sign(d), url: wakeUrl, acta: actaVista, channel: announce || undefined })
197
+ await client.registerWake({ publicKey: comm, sign: (d) => commKey.sign(d), url: wakeUrl, acta: seenActa, channel: announce || undefined })
198
198
  log('[vault] wake URL registered on the proxy')
199
199
  } catch (e) {
200
200
  log(`[vault] could not register the wake URL (${e.code || ''} ${e.message}) — the proxy will not wake this vault`)
201
201
  }
202
202
  }
203
203
 
204
- let reintento = null
205
- let fallos = 0
204
+ let retry = null
205
+ let failures = 0
206
206
  const identifyWithRetry = async () => {
207
- if (reintento) { clearTimeout(reintento); reintento = null }
207
+ if (retry) { clearTimeout(retry); retry = null }
208
208
  try {
209
209
  await identify()
210
210
  await announceAccount()
211
211
  await registerWake()
212
- if (fallos) log(`[vault] identified on the proxy after ${fallos + 1} attempt(s)`)
213
- fallos = 0
212
+ if (failures) log(`[vault] identified on the proxy after ${failures + 1} attempt(s)`)
213
+ failures = 0
214
214
  } catch (e) {
215
- fallos++
216
- const espera = Math.min(60000, 5000 * fallos)
217
- log(`[vault] could not identify on the proxy (${e.message}) — retrying in ${Math.round(espera / 1000)}s; until then this vault is unreachable and cannot ring anyone`)
218
- reintento = setTimeout(() => { reintento = null; identifyWithRetry() }, espera)
219
- reintento.unref?.()
215
+ failures++
216
+ const wait = Math.min(60000, 5000 * failures)
217
+ log(`[vault] could not identify on the proxy (${e.message}) — retrying in ${Math.round(wait / 1000)}s; until then this vault is unreachable and cannot ring anyone`)
218
+ retry = setTimeout(() => { retry = null; identifyWithRetry() }, wait)
219
+ retry.unref?.()
220
220
  }
221
221
  }
222
222
  await identifyWithRetry()
223
223
  // Re-identificar al reconectar (el token cambia).
224
- client.on('token', () => { fallos = 0; identifyWithRetry() })
224
+ client.on('token', () => { failures = 0; identifyWithRetry() })
225
225
 
226
226
  /**
227
227
  * QUE NO SE PUEDA VOLVER A QUEDAR MUDA.
@@ -247,7 +247,7 @@ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey
247
247
  let saidDown = false
248
248
  client.on('disconnect', ({ code, reason } = {}) => {
249
249
  // Sin socket no hay nadie atado al proxio: lo que se identificó era la conexión anterior.
250
- identificado = false
250
+ identified = false
251
251
  if (!downSince) downSince = Date.now()
252
252
  if (saidDown) return
253
253
  saidDown = true
@@ -267,7 +267,7 @@ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey
267
267
  // borra al caerse, así que mirarlo daba la bóveda por vuelta con el socket cerrado: el
268
268
  // 2026-10-15 dijo «back on the proxy after 50s», no volvió a marcar nunca y pasó hora y
269
269
  // media fuera, avisando cada 10 minutos de que no podía anunciarse.
270
- if (identificado && client.isConnected && client.token) {
270
+ if (identified && client.isConnected && client.token) {
271
271
  if (downSince) log(`[vault] back on the proxy after ${Math.round((Date.now() - downSince) / 1000)}s`)
272
272
  downSince = 0; saidDown = false
273
273
  // EL ANUNCIO CADUCA: el proxio borra una entrada de canal a los 20 minutos de publicada.
@@ -286,5 +286,5 @@ export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey
286
286
  }, RECHECK_MS)
287
287
  watchdog.unref?.()
288
288
 
289
- return { client, token: client.token, identify: identifyWithRetry, isIdentified: () => identificado }
289
+ return { client, token: client.token, identify: identifyWithRetry, isIdentified: () => identified }
290
290
  }