@dotrino/identity 0.75.0 → 0.76.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/vault/acta.js +29 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/identity",
3
- "version": "0.75.0",
3
+ "version": "0.76.0",
4
4
  "description": "Identidad y rating de usuarios compartidos entre apps de Dotrino (vault iframe + postMessage)",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/vault/acta.js CHANGED
@@ -436,7 +436,31 @@ export function checkShape (acta) {
436
436
  if (!Array.isArray(acta.members) || acta.members.length === 0) return 'members'
437
437
  for (const m of acta.members) {
438
438
  if (!isPub(m?.pub) || !Array.isArray(m?.caps)) return 'member'
439
- if (m.caps.some((c) => !CAPS.includes(c))) return 'cap-desconocida'
439
+ // UN PERMISO DESCONOCIDO SE IGNORA, NO INVALIDA EL ACTA (dueño, 2026-09-01).
440
+ //
441
+ // Aquí ponía `if (m.caps.some((c) => !CAPS.includes(c))) return 'cap-desconocida'`, y
442
+ // eso convertía CADA permiso nuevo en una rotura de toda la flota: al añadir
443
+ // `unattended`, los aparatos con un pilar anterior empezaron a contestar «the record
444
+ // does not verify» y se quedaron sin configuración. El acta estaba perfectamente
445
+ // firmada; lo que fallaba era el lector, que la tiraba al suelo por ver una palabra
446
+ // que no conocía.
447
+ //
448
+ // Y el acta VIAJA — a otros aparatos, a otras bóvedas, al proxio —, así que un
449
+ // documento firmado tiene que poder leerlo alguien que no está al día. La firma cubre
450
+ // el documento tal cual, de modo que un permiso desconocido no la rompe: rechazarlo era
451
+ // una decisión del validador, y la equivocada.
452
+ //
453
+ // POR QUÉ IGNORARLO ES SEGURO, que es lo único que hace válido este cambio: todas las
454
+ // comprobaciones preguntan por un permiso CONCRETO (`memberCan(acta, pub, 'sign')`), así
455
+ // que una palabra que el lector no conoce no puede satisfacer ninguna — no concede nada.
456
+ // Ignorar cae siempre del lado estricto.
457
+ //
458
+ // ⚠️ La regla que hay que respetar al añadir permisos futuros: **un permiso nuevo
459
+ // CONCEDE, nunca restringe**. Si algún día se inventa uno cuya AUSENCIA sea lo
460
+ // permisivo, un lector viejo lo ignoraría y actuaría de más — y entonces sí habría que
461
+ // subir `ACTA_V` y negarse a leer las versiones que no se entienden. `unattended`
462
+ // cumple la regla: sin él se pide aprobación.
463
+ if (m.caps.some((c) => typeof c !== 'string' || !c)) return 'member'
440
464
  // El CN es la frontera: un SERVICIO solo puede abrir su propio cajón, y un
441
465
  // DISPOSITIVO no tiene cajón que abrir. Que no se pueda escribir un acta que
442
466
  // mezcle las dos cosas es lo que hace que el límite sea real y no una costumbre.
@@ -909,11 +933,14 @@ export async function verifyRenounce (record) {
909
933
  export function effectiveCaps (acta, pub, extraRenounces = []) {
910
934
  const m = (acta?.members || []).find((x) => x.pub === pub)
911
935
  if (!m) return []
936
+ // Lo que este lector no conoce no es una capacidad PARA ÉL: no la puede juzgar, así que
937
+ // no la enseña ni la cuenta. Sigue en el acta —la firma la cubre— y un lector al día sí
938
+ // la ve. Ver `checkShape` para por qué no se rechaza el acta entera.
912
939
  const quitadas = new Set()
913
940
  for (const r of [...(acta.renounced || []), ...extraRenounces]) {
914
941
  if (r?.member === pub) for (const c of (r.caps || [])) quitadas.add(c)
915
942
  }
916
- return m.caps.filter((c) => !quitadas.has(c))
943
+ return m.caps.filter((c) => CAPS.includes(c) && !quitadas.has(c))
917
944
  }
918
945
 
919
946
  /**