@dotrino/vaultd 0.61.0 → 0.62.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/index.js CHANGED
@@ -26,6 +26,7 @@
26
26
  * No reimplementa nada del ecosistema.
27
27
  */
28
28
  import { verifyChain, verifyDeviceSig } from '@dotrino/identity/capabilities'
29
+ import { memberCanSign } from '@dotrino/identity/acta'
29
30
  import { createEnrollDesk, deviceIdOf, DEVICE_TTL_MS, FRESH_WINDOW_MS } from './enroll.js'
30
31
  // Las constantes del protocolo salen del MISMO módulo que usa el daemon: si la lista
31
32
  // local se queda corta, el dispositivo deja de handle mensajes sin que nadie lo note.
@@ -205,6 +206,14 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
205
206
  async function handleSign (from, p) {
206
207
  const chk = await authorise(from, p, SCOPE.SIGN)
207
208
  if (!chk) return
209
+ // MANDA EL ACTA, NO EL PAPEL. `authorise` mira el certificado, y quitarle `firma` a un
210
+ // aparato NO reemite ni revoca su cert: seguiría diciendo `vault:sign` hasta 30 días.
211
+ // Sin esto, retirarle el permiso lo cambiaba de firmar él a que firmara la bóveda por
212
+ // él — o sea, no se lo retiraba. Mismo arreglo que en el daemon (`src/vault.js`).
213
+ const record = (await identity.profileActa?.().catch(() => null))?.acta || null
214
+ if (record && !memberCanSign(record, chk.device)) {
215
+ return send(from, { type: MSG.ERROR, error: 'unauthorized: acta — this member does not sign' })
216
+ }
208
217
  const toSign = p.data?.payload
209
218
  if (toSign == null) return send(from, { type: MSG.ERROR, error: 'data.payload required' })
210
219
  const { signature, publickey } = await identity.signData(toSign)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/vaultd",
3
- "version": "0.61.0",
3
+ "version": "0.62.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": {
@@ -20,7 +20,7 @@
20
20
  "node": ">=20"
21
21
  },
22
22
  "dependencies": {
23
- "@dotrino/identity": "^0.64.0",
23
+ "@dotrino/identity": "^0.65.0",
24
24
  "@dotrino/passmanager": "^0.9.0",
25
25
  "@dotrino/proxy-client": "^0.13.1",
26
26
  "ws": "^8.18.0"
package/src/vault.js CHANGED
@@ -315,6 +315,32 @@ export async function startVault ({ dir = dataDir(), proxyUrl, log = console.log
315
315
  expectedScope: SCOPE.SIGN, trustedIssuer: master, revoked: await revocationSet()
316
316
  })
317
317
  if (!chk.ok) return denyChain(from, chk, p, 'sign')
318
+
319
+ // MANDA EL ACTA, NO EL PAPEL. Esto miraba solo el certificado, y era el único
320
+ // mostrador que lo hacía —`approve` y los secretos ya preguntaban al acta—. El agujero
321
+ // que dejaba: `caps <ID> -firma` quita `sign` del acta pero NO reemite ni revoca el
322
+ // cert del aparato, que sigue diciendo `vault:sign` hasta 30 días. Entonces su
323
+ // `signData` local veía que ya no puede y se lo PEDÍA a la bóveda… que decía que sí.
324
+ // O sea que quitarle el permiso de firmar no se lo quitaba: lo cambiaba de firmar él
325
+ // a que firmaras tú por él.
326
+ const record = (await identity.profileActa?.().catch(() => null))?.acta || null
327
+ if (record && !Acta.memberCanSign(record, chk.device)) {
328
+ audit('rejected', { what: 'sign', reason: 'acta' })
329
+ return reply(from, { type: MSG.ERROR, error: 'unauthorized: acta — this member does not sign' })
330
+ }
331
+
332
+ // Y CERRADA NO FIRMA NADA (dueño, 2026-08-31). La regla de antes —«el daemon sigue
333
+ // firmando con el perfil bloqueado, para que un reinicio no deje las apps muertas»— es
334
+ // ANTERIOR AL MODELO DE SOBRES y queda derogada: con los sobres la bóveda no necesita
335
+ // firmar nada para servir. Lo que hace abierta es rehacer los sobres.
336
+ //
337
+ // Y las apps no se quedan muertas por esto: un aparato al que el acta le da `sign`
338
+ // firma con SU llave y no pasa por aquí (ver `signData` en el pilar). Leer y guardar
339
+ // tampoco pasan por aquí, y siguen funcionando con el candado echado.
340
+ if (isLocked()) {
341
+ audit('rejected', { what: 'sign', reason: 'locked' })
342
+ return reply(from, { type: MSG.ERROR, error: 'vault locked: the master key does not sign while the vault is closed' })
343
+ }
318
344
  const toSign = p.data?.payload
319
345
  if (toSign == null) return reply(from, { type: MSG.ERROR, error: 'data.payload required' })
320
346
  const { signature, publickey } = await identity.signData(toSign)