@dotrino/vaultd 0.77.0 → 0.78.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/lib/src/admin.js +16 -3
- package/package.json +1 -1
- package/src/transport.js +10 -1
- package/src/vault.js +25 -2
package/lib/src/admin.js
CHANGED
|
@@ -60,7 +60,20 @@ export const ADMIN_OPS = Object.freeze([
|
|
|
60
60
|
])
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* QUÉ SE ATIENDE CON EL CANDADO ECHADO.
|
|
64
|
+
*
|
|
65
|
+
* No es «solo mirar», y confundirlo fue un error: **guardar una variable NO necesita la
|
|
66
|
+
* maestra**. Sellar un valor usa las llaves PÚBLICAS de quien va a leerlo, así que la
|
|
67
|
+
* bóveda puede hacerlo sin ningún secreto suyo (`docs/secretos-sellados.md` §8.1) — es
|
|
68
|
+
* justo lo que hace barata la rotación. Meterlo en el mismo saco que revocar dejaba la
|
|
69
|
+
* consola sin poder configurar nada con el perfil cerrado, que es su trabajo diario.
|
|
70
|
+
*
|
|
71
|
+
* Lo que SÍ se queda fuera es `revoke`: reescribe el acta, y sellar el acta es de la
|
|
72
|
+
* maestra. Cerrada no hay con qué.
|
|
73
|
+
*
|
|
74
|
+
* (Si el cajón tiene su llave sellada con la frase, repartirla a los miembros puede quedar
|
|
75
|
+
* a deber hasta que alguien abra: eso ya lo dice la bóveda y se salda solo. Guardar el
|
|
76
|
+
* valor, que es lo que se pidió, se hace igual.)
|
|
64
77
|
*
|
|
65
78
|
* El candado ES de la consola —los aparatos ya emparejados siguen leyendo, guardando y
|
|
66
79
|
* firmando con SU llave— así que cerrar el perfil tiene que cerrar exactamente esto. No
|
|
@@ -72,7 +85,7 @@ export const ADMIN_OPS = Object.freeze([
|
|
|
72
85
|
* Se dejan las de LEER a propósito: si se cortaran todas, la consola no podría ni
|
|
73
86
|
* enterarse de por qué no funciona y parecería rota. Así puede decir «está cerrada».
|
|
74
87
|
*/
|
|
75
|
-
export const ADMIN_OPS_WHILE_LOCKED = Object.freeze(['audit', 'vars'])
|
|
88
|
+
export const ADMIN_OPS_WHILE_LOCKED = Object.freeze(['audit', 'vars', 'var.set', 'var.setMany'])
|
|
76
89
|
|
|
77
90
|
/** Cuánto se recuerda un nonce ya usado (el doble de la ventana de frescura). */
|
|
78
91
|
export const ADMIN_NONCE_TTL_MS = 10 * 60 * 1000
|
|
@@ -136,7 +149,7 @@ export function createAdminDesk ({
|
|
|
136
149
|
}
|
|
137
150
|
const by = await deviceIdOf(chk.device).catch(() => null)
|
|
138
151
|
|
|
139
|
-
// CERRADA NO SE
|
|
152
|
+
// CERRADA NO SE SELLA EL ACTA. Va después de autorizar (el candado es estado del perfil,
|
|
140
153
|
// y a un desconocido no se le cuenta) y ANTES de quemar el nonce: quien reintente
|
|
141
154
|
// tras abrir la bóveda no tiene por qué generar otro.
|
|
142
155
|
//
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/vaultd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.78.0",
|
|
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": {
|
package/src/transport.js
CHANGED
|
@@ -66,7 +66,16 @@ export async function createTransport ({ identity, dir, url = DEFAULT_PROXY, com
|
|
|
66
66
|
if (comm && esMiembro) {
|
|
67
67
|
const data = { op: 'identify', publickey: comm, token: client.token, ts: Date.now() }
|
|
68
68
|
const signature = await commKey.sign(data)
|
|
69
|
-
if (signature)
|
|
69
|
+
if (signature) {
|
|
70
|
+
await client.identify({ data, signature, acta: record })
|
|
71
|
+
// SE DICE QUE SE IDENTIFICÓ, y con qué llave. Este camino era MUDO: al pasar de la
|
|
72
|
+
// maestra a la llave de comunicación, el único rastro de «estoy alcanzable» que
|
|
73
|
+
// quedaba en el log era el del repliegue. Así que cuando la consola decía «no
|
|
74
|
+
// contesta», no había forma de saber desde aquí si la bóveda estaba atada al proxio
|
|
75
|
+
// o no. Una línea por reconexión es barata; no tenerla cuesta una hora de sondeos.
|
|
76
|
+
log('[vault] identified on the proxy with the communication key · record #%s', record?.seq ?? '?')
|
|
77
|
+
return
|
|
78
|
+
}
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
// REPLIEGUE, y solo para migrar: una bóveda que todavía no ha metido su llave de
|
package/src/vault.js
CHANGED
|
@@ -285,8 +285,26 @@ export async function startVault ({ dir = dataDir(), proxyUrl, log = console.log
|
|
|
285
285
|
return new Set(revoked.map((r) => r.nonce))
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
/**
|
|
289
|
+
* CONTESTAR SIN MATAR LA CONEXIÓN.
|
|
290
|
+
*
|
|
291
|
+
* El proxio corta los frames a 1 MB (`maxPayload`), y `ws` no «descarta» el que se pasa:
|
|
292
|
+
* CIERRA EL SOCKET con un 1009. La bóveda se queda muda, sin un solo error en su log, y
|
|
293
|
+
* desde fuera se ve igual que si estuviera apagada. Eso ya pasó y duró tres días.
|
|
294
|
+
*
|
|
295
|
+
* Así que una respuesta que no quepa no se manda: se sustituye por un error, que sí cabe.
|
|
296
|
+
* Una bóveda que dice «no cabe» se arregla; una muda no se puede ni diagnosticar.
|
|
297
|
+
*/
|
|
288
298
|
const reply = (to, obj) => {
|
|
289
|
-
try {
|
|
299
|
+
try {
|
|
300
|
+
const bytes = Buffer.byteLength(JSON.stringify(obj))
|
|
301
|
+
if (bytes > MAX_REPLY_BYTES) {
|
|
302
|
+
log(`[vault] the reply to ${obj?.type} does not fit (${bytes} bytes > ${MAX_REPLY_BYTES}): sending an error instead of killing the connection`)
|
|
303
|
+
audit('reply-too-big', { type: obj?.type || null, bytes })
|
|
304
|
+
return client.send(to, { type: MSG.ERROR, error: `reply too big (${bytes} bytes): ask for less at a time` })
|
|
305
|
+
}
|
|
306
|
+
client.send(to, obj)
|
|
307
|
+
} catch (e) { log('[vault] could not reply:', e.message) }
|
|
290
308
|
}
|
|
291
309
|
|
|
292
310
|
// FRESCURA anti-replay: toda petición firmada debe traer `data.ts` dentro de una
|
|
@@ -680,7 +698,9 @@ export async function startVault ({ dir = dataDir(), proxyUrl, log = console.log
|
|
|
680
698
|
if (!msg.chain?.length) msg.chain = null
|
|
681
699
|
const left = msg.chain?.length || 0
|
|
682
700
|
log(`[vault] record chain trimmed to fit the transport: ${left}/${full} link(s), ${size(msg)} bytes`)
|
|
683
|
-
|
|
701
|
+
// Si tras podar la cadena SIGUE sin caber, no se avisa y se manda igual —eso mataba la
|
|
702
|
+
// conexión—: se devuelve tal cual y `reply` lo sustituye por un error. El aviso de aquí
|
|
703
|
+
// sobra porque el de `reply` dice lo mismo y además dice qué hizo.
|
|
684
704
|
return msg
|
|
685
705
|
}
|
|
686
706
|
|
|
@@ -2031,6 +2051,9 @@ export async function startVault ({ dir = dataDir(), proxyUrl, log = console.log
|
|
|
2031
2051
|
|
|
2032
2052
|
return {
|
|
2033
2053
|
identity, client, store, threads, secrets, master, fingerprint: fp, dir,
|
|
2054
|
+
// Se expone para poder PROBAR que una respuesta que no cabe no mata la conexión: es el
|
|
2055
|
+
// único punto por el que sale todo, y el fallo que cierra vive justo ahí.
|
|
2056
|
+
reply,
|
|
2034
2057
|
/**
|
|
2035
2058
|
* SOLTAR LA MAESTRA. Cerrar el perfil tiene que sacarla de la memoria, no solo dejar de
|
|
2036
2059
|
* cargarla en el siguiente arranque: si no, el candado seguiría siendo una bandera al
|