@dotrino/vaultd 0.150.0 → 0.151.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/service.js +3 -2
- package/package.json +1 -1
- package/src/transport.js +10 -2
- package/src/vault.js +15 -10
package/lib/src/service.js
CHANGED
|
@@ -522,8 +522,9 @@ export async function registerEncKey ({ dir, ns, proxyUrl, masterPubkey, device,
|
|
|
522
522
|
* privadas al pasar.
|
|
523
523
|
*
|
|
524
524
|
* Lo que decide la bóveda, no este cliente:
|
|
525
|
-
* ·
|
|
526
|
-
* ·
|
|
525
|
+
* · AÑADIR una variable nueva es directo: se guarda en el acto;
|
|
526
|
+
* · EDITAR una que ya existe pide aprobación (la tanda entera queda en espera);
|
|
527
|
+
* · si nadie puede aprobar, una que ya exista falla con `exists`;
|
|
527
528
|
* · BORRAR no existe por aquí.
|
|
528
529
|
*
|
|
529
530
|
* @param {object} o
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/vaultd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.151.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
|
@@ -36,7 +36,7 @@ export async function masterPubkeyOf (identity) {
|
|
|
36
36
|
* @param {string} [opts.url] URL del proxy (default wss://proxy.dotrino.com).
|
|
37
37
|
* @returns {Promise<{ client, token:string, identify():Promise<void> }>}
|
|
38
38
|
*/
|
|
39
|
-
export async function createTransport ({ identity, dir, url = DEFAULT_PROXY, commKey = null, log = () => {}, makeClient = null, announce = null }) {
|
|
39
|
+
export async function createTransport ({ identity, dir, url = DEFAULT_PROXY, commKey = null, log = () => {}, makeClient = null, announce = null, recheckMs = 60_000, reannounceMs = 10 * 60_000 }) {
|
|
40
40
|
installNodeGlobals(dir)
|
|
41
41
|
// Import dinámico DESPUÉS de instalar los globals que el paquete usa.
|
|
42
42
|
// `WebSocketProxyClient` (la clase) y NO el helper `getWebSocketProxyClient`:
|
|
@@ -174,8 +174,10 @@ export async function createTransport ({ identity, dir, url = DEFAULT_PROXY, com
|
|
|
174
174
|
* existe». Si el anuncio falla, la bóveda sigue sirviendo a todo lo demás y se dice en voz
|
|
175
175
|
* alta: lo que se pierde es que la encuentren por su dirección, no que deje de funcionar.
|
|
176
176
|
*/
|
|
177
|
+
let announcedAt = 0
|
|
177
178
|
const announceAccount = async () => {
|
|
178
179
|
if (!announce || !identificado) return
|
|
180
|
+
announcedAt = Date.now()
|
|
179
181
|
try { await client.publish(announce) } catch (e) {
|
|
180
182
|
log(`[vault] could not announce this account on the proxy (${e.message}) — a borrowed machine will not find this vault by its address`)
|
|
181
183
|
}
|
|
@@ -219,8 +221,9 @@ export async function createTransport ({ identity, dir, url = DEFAULT_PROXY, com
|
|
|
219
221
|
* `connect()` es seguro de repetir: si ya está conectado devuelve el token y no abre
|
|
220
222
|
* nada.
|
|
221
223
|
*/
|
|
222
|
-
const RECHECK_MS =
|
|
224
|
+
const RECHECK_MS = recheckMs
|
|
223
225
|
const PATIENCE_MS = 5 * 60_000
|
|
226
|
+
const REANNOUNCE_MS = reannounceMs // la mitad de lo que el proxio guarda una entrada
|
|
224
227
|
let downSince = 0
|
|
225
228
|
let saidDown = false
|
|
226
229
|
client.on('disconnect', ({ code, reason } = {}) => {
|
|
@@ -236,6 +239,11 @@ export async function createTransport ({ identity, dir, url = DEFAULT_PROXY, com
|
|
|
236
239
|
if (identificado && client.token) {
|
|
237
240
|
if (downSince) log(`[vault] back on the proxy after ${Math.round((Date.now() - downSince) / 1000)}s`)
|
|
238
241
|
downSince = 0; saidDown = false
|
|
242
|
+
// EL ANUNCIO CADUCA: el proxio borra una entrada de canal a los 20 minutos de publicada.
|
|
243
|
+
// Sin renovarlo, entrar con usuario y contraseña solo funcionaba los primeros 20 minutos
|
|
244
|
+
// tras identificarse, y desde fuera se veía como «no hay ninguna bóveda en esa dirección»
|
|
245
|
+
// con la bóveda encendida (2026-10-12, la demo de los revisores).
|
|
246
|
+
if (announce && Date.now() - announcedAt >= REANNOUNCE_MS) await announceAccount()
|
|
239
247
|
return
|
|
240
248
|
}
|
|
241
249
|
if (!downSince) downSince = Date.now()
|
package/src/vault.js
CHANGED
|
@@ -1477,9 +1477,9 @@ export async function startVault ({ dir = dataDir(), proxyUrl, log = console.log
|
|
|
1477
1477
|
* firma del autor, las envolturas, la espera de aprobación. Aquí no hay otra puerta.
|
|
1478
1478
|
*
|
|
1479
1479
|
* Los límites (dueño, 2026-09-29):
|
|
1480
|
-
* ·
|
|
1481
|
-
*
|
|
1482
|
-
* · si nadie puede aprobar, no se pide y
|
|
1480
|
+
* · AÑADIR una variable nueva es directo, haya o no quien apruebe (dueño, 2026-10-12);
|
|
1481
|
+
* · EDITAR una que ya existe pide aprobación —`unattended` no exime—;
|
|
1482
|
+
* · si nadie puede aprobar, no se pide y lo que ya existe no se toca (`exists`);
|
|
1483
1483
|
* · BORRAR, nunca: eso es de la consola y del CLI de la bóveda.
|
|
1484
1484
|
*
|
|
1485
1485
|
* Dos operaciones: `store.recipients` (para quién se sella: solo públicas, nada que ocultar)
|
|
@@ -1696,7 +1696,7 @@ export async function startVault ({ dir = dataDir(), proxyUrl, log = console.log
|
|
|
1696
1696
|
log(`[vault] ${ns}: ${deviceId || '????-????'} wants to store ${items.length} variable(s) — waiting for approval (${result.pending})`)
|
|
1697
1697
|
} else {
|
|
1698
1698
|
audit('store', { device: deviceId, ns, keys: result?.keys || [] })
|
|
1699
|
-
log(`[vault] ${ns}: ${deviceId || '????-????'} stored ${(result?.keys || []).length} variable(s)
|
|
1699
|
+
log(`[vault] ${ns}: ${deviceId || '????-????'} stored ${(result?.keys || []).length} new variable(s)`)
|
|
1700
1700
|
await notifyMembers('vars', { by: deviceId, keys: result?.keys || [], ns })
|
|
1701
1701
|
}
|
|
1702
1702
|
return await answer({ op: 'store.result', ns, ts: Date.now(), keys: result?.keys || [], pending: result?.pending || null })
|
|
@@ -2811,13 +2811,18 @@ export async function startVault ({ dir = dataDir(), proxyUrl, log = console.log
|
|
|
2811
2811
|
const owner = ns ? `ns:${ns}` : `dev:${pub}`
|
|
2812
2812
|
const keys = []
|
|
2813
2813
|
// ¿SE PIDE APROBACIÓN? Desde la mesa del admin, lo de siempre (`needsApproval`: quien
|
|
2814
|
-
// tiene `unattended` escribe solo). Desde el propio SERVICIO que guarda en su cajón,
|
|
2815
|
-
//
|
|
2816
|
-
//
|
|
2817
|
-
//
|
|
2818
|
-
//
|
|
2814
|
+
// tiene `unattended` escribe solo). Desde el propio SERVICIO que guarda en su cajón, la
|
|
2815
|
+
// aprobación es SOLO PARA EDITAR (dueño, 2026-10-12: «la aprobación solamente es para
|
|
2816
|
+
// editar una env; para almacenarla es directo»): una variable nueva no le quita nada a
|
|
2817
|
+
// nadie y se guarda en el acto; reemplazar una que ya existe pide el sí de quien
|
|
2818
|
+
// aprueba, tenga o no `unattended` el servicio (dueño, 2026-09-29: «el unattended
|
|
2819
|
+
// también pediría aprobación»). Una tanda con alguna que ya existe se pide ENTERA: va
|
|
2820
|
+
// todo o nada. Sin nadie que apruebe no se pide —la regla de siempre— y lo que ya
|
|
2821
|
+
// existe falla con `exists`.
|
|
2819
2822
|
const record = await refreshActa()
|
|
2820
|
-
const ask = fromService
|
|
2823
|
+
const ask = fromService
|
|
2824
|
+
? hasApprover(record) && items.some((it) => it?.key && secrets.has(owner, it.key))
|
|
2825
|
+
: needsApproval(caller, record)
|
|
2821
2826
|
for (const it of items) {
|
|
2822
2827
|
if (!it?.key || !it?.sealed) throw new Error('var.setMany: each variable needs its key and its sealed envelope')
|
|
2823
2828
|
await checkAuthor(owner, it.key, it.sealed, caller, { allowOverwrite: ask })
|