@dotrino/vaultd 0.106.1 → 0.106.2

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/vault.js +23 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/vaultd",
3
- "version": "0.106.1",
3
+ "version": "0.106.2",
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/vault.js CHANGED
@@ -1923,6 +1923,29 @@ export async function startVault ({ dir = dataDir(), proxyUrl, log = console.log
1923
1923
  audit('rejected', { what: 'renounce', reason: 'signature' })
1924
1924
  return reply(from, { type: MSG.ERROR, error: 'renounce: the signature is not the member own' })
1925
1925
  }
1926
+ // UNA RENUNCIA QUE NO QUITA NADA NO SE HONRA, y esto es lo que corta el replay barato.
1927
+ //
1928
+ // El registro va firmado y lleva `ts`, pero nadie lo miraba: cualquiera que lo viera
1929
+ // pasar por el proxio podía volver a mandarlo cuando quisiera. Como `absorbRenounce`
1930
+ // SELLA UN ACTA NUEVA cada vez, repetirlo hacía subir el `seq` y empujar a todos los
1931
+ // aparatos sin que hubiera cambiado nada — y dejaba en la bitácora una renuncia con el
1932
+ // nombre del aparato, que es lo peor: la bitácora es la prueba, y ahí decía algo que
1933
+ // ese aparato no hizo.
1934
+ //
1935
+ // Mirando lo que el acta dice HOY, un registro repetido no quita nada y se para aquí.
1936
+ // Lo que esto NO cierra, y queda anotado: si el dueño vuelve a conceder ese permiso, el
1937
+ // mismo registro viejo lo quita otra vez. Cerrarlo del todo pide que el acta recuerde
1938
+ // la fecha de la última renuncia por miembro y rechace las que no sean posteriores.
1939
+ const actual = (await identity.profileActa?.().catch(() => null))?.acta || null
1940
+ if (!actual) {
1941
+ audit('rejected', { what: 'renounce', reason: 'sin-acta' })
1942
+ return reply(from, { type: MSG.ERROR, error: 'renounce: this vault has no record to apply it to' })
1943
+ }
1944
+ const tiene = new Set(Acta.effectiveCaps(actual, record.member) || [])
1945
+ if (!(record.caps || []).some((c) => tiene.has(c))) {
1946
+ audit('rejected', { what: 'renounce', reason: 'nada-que-quitar' })
1947
+ return reply(from, { type: MSG.ERROR, error: 'renounce: that member no longer holds any of those capabilities' })
1948
+ }
1926
1949
  try {
1927
1950
  const r = await identity.absorbRenounce(record)
1928
1951
  const device = await deviceIdOf(record.member).catch(() => null)