@dotrino/vaultd 0.105.1 → 0.105.3

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.
@@ -302,14 +302,34 @@ export async function sayHello (client, qr) {
302
302
  }
303
303
 
304
304
  /**
305
- * Cómo se llama la cuenta de esa invitación, sin enrolar nada. Una conexión y una pregunta.
305
+ * El hello de esa invitación, sin enrolar nada: devuelve el `qr` ENRIQUECIDO con
306
+ * `iss`, `proxy` y `acct`.
307
+ *
308
+ * Devuelve el qr y no solo el nombre PORQUE LA CITA SE QUEMA AL CANJEARLA. Quien
309
+ * pregunta el nombre de la cuenta y después enrola estaría canjeando dos veces la
310
+ * misma cita, y el segundo canje falla con «código no válido o ya usado» — un error
311
+ * que señala a la invitación cuando el problema es haberla gastado uno mismo.
312
+ * Con el qr enriquecido, `sayHello` corta por su `if (qr.iss) return qr` y el
313
+ * enrolamiento no vuelve a salir a preguntar.
306
314
  */
307
- export async function vaultAccountName (qr) {
315
+ export async function vaultHello (qr) {
308
316
  if (typeof qr === 'string') qr = parseInvite(qr)
309
317
  if (!qr?.sn) throw new Error('that does not look like a vault invitation')
310
- if (qr.acct) return qr.acct
318
+ if (qr.iss) return qr
311
319
  const client = await freshClient(qr.proxy || 'wss://proxy.dotrino.com')
312
- try { return (await sayHello(client, qr)).acct || '' } finally { try { client.close() } catch (_) {} }
320
+ try { return await sayHello(client, qr) } finally { try { client.close() } catch (_) {} }
321
+ }
322
+
323
+ /**
324
+ * Cómo se llama la cuenta de esa invitación, sin enrolar nada.
325
+ *
326
+ * OJO: gasta la cita. Si además vas a enrolar, usá `vaultHello` y pasale al enroll el
327
+ * qr que devuelve; si no, el enrolamiento se encuentra la cita quemada.
328
+ */
329
+ export async function vaultAccountName (qr) {
330
+ if (typeof qr === 'string') qr = parseInvite(qr)
331
+ if (qr?.acct) return qr.acct
332
+ return (await vaultHello(qr)).acct || ''
313
333
  }
314
334
 
315
335
  export async function enrollWithVault ({ qr, label = 'agent', expectedScope = null, onCode, approveTimeoutMs = 180000, withEncKey = true } = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/vaultd",
3
- "version": "0.105.1",
3
+ "version": "0.105.3",
4
4
  "type": "module",
5
5
  "description": "Certificador personal de Dotrino: daemon headless que custodia la clave maestra y delega capacidades a tus dispositivos por el proxy. Tu CA propia.",
6
6
  "bin": {
@@ -14,7 +14,8 @@
14
14
  "pair": "node bin/dotrino-vaultd.js --pair",
15
15
  "tui": "node bin/dotrino-vault-tui.js",
16
16
  "test": "node --test test/*.test.mjs",
17
- "type-check": "tsc --noEmit"
17
+ "type-check": "tsc --noEmit",
18
+ "release:env": "node packaging/release-env.mjs"
18
19
  },
19
20
  "engines": {
20
21
  "node": ">=20"
@@ -342,6 +342,19 @@ export function openSecretsStore (dir, { sealer = null, recipients = null, signe
342
342
  schemaVersion: () => data.schemaVersion,
343
343
  /** ¿La privada de recuperación está sellada solo con la llave de esta máquina? */
344
344
  recoveryPub: () => data.recovery?.pub || null,
345
+ /**
346
+ * CREA LA LLAVE DE RECUPERACIÓN SI NO EXISTE. Se llama al ABRIR la bóveda, que es el
347
+ * único momento en que se puede: sellar su privada necesita la maestra.
348
+ *
349
+ * Nacía perezosamente, al escribir el primer secreto. En un perfil recién hecho eso
350
+ * significaba que la bóveda no tenía a quién envolver, y como crearla exige estar
351
+ * abierta, escribir el PERFIL con la bóveda cerrada era imposible — que es justo lo
352
+ * que el perfil en sobres viene a permitir. Lo encontró el escenario del navegador:
353
+ * «the vault did not say who to seal the profile for».
354
+ *
355
+ * Idempotente: si ya está, no toca nada.
356
+ */
357
+ async ensureRecovery (adminKey = null) { return enFila(async () => (await ensureRecovery(adminKey)).pub) },
345
358
 
346
359
  /**
347
360
  * MUCHAS ESCRITURAS, UN GUARDADO. Cargar la configuración de un servicio son veinte
package/src/vault.js CHANGED
@@ -2552,6 +2552,15 @@ export async function startVault ({ dir = dataDir(), proxyUrl, log = console.log
2552
2552
  // ANTES de reenvolver nada: si la copia de recuperación se quedó bajo la llave de la
2553
2553
  // máquina, se pasa a la frase. Sin esto, todo lo de abajo falla con «wrong password».
2554
2554
  if (adminKey) await migrarRecuperacionALaFrase(adminKey)
2555
+ // Y LA LLAVE DE RECUPERACIÓN EXISTE. Nacía al escribir el primer secreto, así que una
2556
+ // bóveda recién creada no tenía a quién envolver — y como crearla necesita la maestra,
2557
+ // cerrada ya no había manera. El síntoma: escribir el perfil con la bóveda cerrada
2558
+ // fallaba con «the vault did not say who to seal the profile for», en un perfil nuevo
2559
+ // y solo en el primero. Abrir la bóveda es el momento de saldarlo, y es idempotente.
2560
+ // …salvo en un almacén v3, que no tiene ese concepto: ahí lo que toca es `migrate`, y
2561
+ // escribir o leer un sobre ya se rechaza diciéndolo. Crearla antes de migrar sería
2562
+ // dejar una llave suelta en un formato que va a reescribirse entero.
2563
+ if (!secrets.isLegacy()) await secrets.ensureRecovery(adminKey)
2555
2564
  const out = { drawers: 0, wrapped: 0, sealed: 0, dropped: 0, sinLlave: [], failed: [] }
2556
2565
  for (const owner of secrets.owners?.() || []) {
2557
2566
  const before = new Set(secrets.recipientsIn(owner))