@dotrino/vault 0.43.0 → 0.45.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/vault",
3
- "version": "0.43.0",
3
+ "version": "0.45.0",
4
4
  "description": "Usa ESTE dispositivo (navegador) como bóveda/CA del ecosistema Dotrino: atiende enrolamientos por el proxy y firma certificados de delegación a tus máquinas. Incluye el cliente de SERVICIO (Node): un proyecto se enrola una vez y jala sus credenciales del vault en vez del .env (`import '@dotrino/vault/config'`).",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/admin.js CHANGED
@@ -46,7 +46,7 @@
46
46
  export const ADMIN_OPS = Object.freeze([
47
47
  // `pending`, `pair`, `approve` y `reject` NO están, y no es un olvido: agregar aparatos
48
48
  // a distancia se quitó (ver la cabecera). Lo que queda es mirar, quitar y configurar.
49
- 'status', 'revoke', 'audit',
49
+ 'revoke', 'audit',
50
50
  // Variables de entorno: verlas (nombres siempre; valor solo de las públicas) y
51
51
  // ponerles valor (de las dos). Borrar NO está, y es a propósito: un aparato robado
52
52
  // no debe poder dejar sin configuración a los servicios.
@@ -72,7 +72,7 @@ export const ADMIN_OPS = Object.freeze([
72
72
  * Se dejan las de LEER a propósito: si se cortaran todas, la consola no podría ni
73
73
  * enterarse de por qué no funciona y parecería rota. Así puede decir «está cerrada».
74
74
  */
75
- export const ADMIN_OPS_WHILE_LOCKED = Object.freeze(['status', 'audit', 'vars'])
75
+ export const ADMIN_OPS_WHILE_LOCKED = Object.freeze(['audit', 'vars'])
76
76
 
77
77
  /** Cuánto se recuerda un nonce ya usado (el doble de la ventana de frescura). */
78
78
  export const ADMIN_NONCE_TTL_MS = 10 * 60 * 1000
@@ -159,10 +159,6 @@ export function createAdminDesk ({
159
159
  }
160
160
 
161
161
  try {
162
- // Lo mínimo que la consola necesita saber de la bóveda para explicarse: si está
163
- // cerrada, lo que queda (revocar, configurar) no se puede, y hay que decir por qué en
164
- // vez de dejar pulsar un botón para recibir un error.
165
- if (data.op === 'status') return { ok: true, result: { locked: !!isLocked() } }
166
162
 
167
163
  if (data.op === 'audit') {
168
164
  const limit = Math.min(Math.max(Number(data.limit) || 100, 1), AUDIT_MAX)
package/src/service.js CHANGED
@@ -421,7 +421,7 @@ export async function registerEncKey ({ dir, ns, proxyUrl, masterPubkey, device,
421
421
  * Renueva el cert automáticamente si está por vencer (best-effort).
422
422
  * @returns {Promise<Record<string,string>>} secretos KEY→valor
423
423
  */
424
- export async function fetchSecrets ({ dir, ns, proxyUrl, masterPubkey, device, cert, enc, timeoutMs = 30000, onPending, approvalTimeoutMs = APPROVAL_TIMEOUT_MS } = {}) {
424
+ export async function fetchSecrets ({ dir, ns, proxyUrl, masterPubkey, device, cert, enc, timeoutMs = 30000, onPending, onCert, approvalTimeoutMs = APPROVAL_TIMEOUT_MS } = {}) {
425
425
  let saved = null
426
426
  if (dir) saved = readServiceIdentity(dir)
427
427
  // Sin `dir`: la identidad viene entera por parámetros — es el caso de un agente
@@ -465,7 +465,15 @@ export async function fetchSecrets ({ dir, ns, proxyUrl, masterPubkey, device, c
465
465
  recordar({ actaSeq: acta.seq })
466
466
  }
467
467
 
468
- const renovar = typeof saved?.actaSeq === 'number' && typeof cert?.seq === 'number' && saved.actaSeq > cert.seq
468
+ // DOS MOTIVOS PARA PEDIR PAPEL NUEVO:
469
+ // · el nuestro es del MODELO VIEJO (lleva `exp` y no `seq`). Ese hay que cambiarlo sí
470
+ // o sí: se acepta por el repliegue de migración, pero muere en su fecha y con él el
471
+ // servicio. Sin esta rama la migración no terminaba nunca — el disparador comparaba
472
+ // `seq` contra `seq`, y un papel viejo no tiene ninguno, así que no saltaba jamás.
473
+ // · o el acta que hemos visto va por delante del papel: el dueño cambió permisos.
474
+ const legado = !!cert && typeof cert.seq !== 'number'
475
+ const atrasado = typeof saved?.actaSeq === 'number' && typeof cert?.seq === 'number' && saved.actaSeq > cert.seq
476
+ const renovar = legado || atrasado
469
477
 
470
478
  // RENOVAR YA NO ES UNA TAREA DE CALENDARIO. El papel no vence: lo único que obliga a
471
479
  // pedir otro es que la bóveda diga que el nuestro se quedó atrás respecto del acta, y
@@ -481,6 +489,11 @@ export async function fetchSecrets ({ dir, ns, proxyUrl, masterPubkey, device, c
481
489
  if (res.type === MSG.RENEWED && res.cert?.sub === device.publickey) {
482
490
  cert = res.cert
483
491
  recordar({ cert })
492
+ // QUIEN NO PASA `dir` GUARDA ÉL. Un servicio que lleva su identidad en su propio
493
+ // archivo (`link.json`) y nos pasa el enlace a mano no tiene `saved`, así que
494
+ // `recordar` no escribe nada: sin esto renovaba en cada arranque y volvía a
495
+ // empezar, y su papel del modelo viejo no se cambiaba NUNCA.
496
+ try { onCert?.(cert) } catch (_) {}
484
497
  }
485
498
  } catch (_) { /* la renovación no bloquea el fetch */ }
486
499
  }
@@ -978,11 +991,11 @@ function fingerprintOf (secrets) {
978
991
  * servicio no opera hasta que esto resuelva — esa es la regla.
979
992
  * @returns {Promise<Record<string,string>>}
980
993
  */
981
- export async function waitForSecrets ({ dir, ns, proxyUrl, masterPubkey, device, cert, enc, retryMs = 5000, maxRetryMs = 60000, onRetry, onPending } = {}) {
994
+ export async function waitForSecrets ({ dir, ns, proxyUrl, masterPubkey, device, cert, enc, retryMs = 5000, maxRetryMs = 60000, onRetry, onPending, onCert } = {}) {
982
995
  let delay = retryMs
983
996
  for (;;) {
984
997
  try {
985
- return await fetchSecrets({ dir, ns, proxyUrl, masterPubkey, device, cert, enc, onPending })
998
+ return await fetchSecrets({ dir, ns, proxyUrl, masterPubkey, device, cert, enc, onPending, onCert })
986
999
  } catch (e) {
987
1000
  // Lo NO transitorio no se arregla reintentando: falta de enrolamiento,
988
1001
  // cert revocado/vencido o scope equivocado exigen re-emparejar → se corta.