@dotrino/vaultd 0.63.0 → 0.65.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/enroll.js CHANGED
@@ -30,7 +30,7 @@
30
30
  * `docs/pairing-protocol.md`).
31
31
  */
32
32
  import { verifyDeviceSig, pubkeyId, commitCode } from '@dotrino/identity/capabilities'
33
- import { verifyContinuity } from '@dotrino/identity/acta'
33
+ import { verifyContinuity, canSeal } from '@dotrino/identity/acta'
34
34
 
35
35
  /** Un token de emparejamiento vale 5 min. */
36
36
  export const PAIRING_TTL_MS = 5 * 60 * 1000
@@ -388,9 +388,12 @@ export function createEnrollDesk ({
388
388
  const pend = [...pending.values()].find((x) => x.state === 'AWAITING_ACTA' && (x.from === from || x.dpub))
389
389
  if (!pend) return reply(from, { type: MSG_ERROR, error: 'no adoption awaiting a record' })
390
390
  if (!record || typeof record !== 'object') return reply(from, { type: MSG_ERROR, error: 'record missing or unreadable' })
391
- if (record.sealer !== iss) {
391
+ // Ya no hay campo `sealer`: se le pregunta al PERMISO. Es la misma comprobación —«¿me
392
+ // nombra a mí la que me mandan?»— dicha en el idioma nuevo, y con varios selladores la
393
+ // respuesta puede ser que sí para más de uno, que es lo correcto.
394
+ if (!canSeal(record, iss)) {
392
395
  audit('rejected', { what: 'adopt', reason: 'not-sealer' })
393
- return reply(from, { type: MSG_ERROR, error: 'that record does not name this vault as the sealer' })
396
+ return reply(from, { type: MSG_ERROR, error: 'that record does not let this vault seal it' })
394
397
  }
395
398
  if (record.sealedBy !== pend.dpub) {
396
399
  audit('rejected', { what: 'adopt', reason: 'sealed-by-other' })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/vaultd",
3
- "version": "0.63.0",
3
+ "version": "0.65.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.66.0",
23
+ "@dotrino/identity": "^0.68.0",
24
24
  "@dotrino/passmanager": "^0.9.0",
25
25
  "@dotrino/proxy-client": "^0.13.1",
26
26
  "ws": "^8.18.0"
package/src/ctl.js CHANGED
@@ -548,7 +548,10 @@ async function cmdMembers () {
548
548
  for (const m of record.members) {
549
549
  const who = m.label || m.id
550
550
  const marks = [
551
- m.isMaster ? `${B}Master${Z}` : null,
551
+ // Ya no hay UN master: la marca dice quién puede sellar, y pueden ser varios. El
552
+ // permiso también sale abajo en la lista, pero aquí se ve de un vistazo — que es
553
+ // justo lo que se busca al mirar quién es quién.
554
+ m.isMaster ? `${B}Sella${Z}` : null,
552
555
  m.isMe ? 'este dispositivo' : null,
553
556
  m.cn ? `servicio «${m.cn}»` : null
554
557
  ].filter(Boolean)
package/src/manager.js CHANGED
@@ -24,13 +24,16 @@ import { atRestFor } from './atrest.js'
24
24
  * Pura a propósito (recibe el veredicto ya calculado) para poder probar la regla sin
25
25
  * levantar un vault entero.
26
26
  */
27
- export function assertCanRemove ({ isMaster, memberCount, name = '' }) {
28
- if (!isMaster || memberCount <= 1) return true
27
+ export function assertCanRemove ({ isMaster, memberCount, sealerCount = 1, name = '' }) {
28
+ // `isMaster` ahora es «¿puedo sellar?». Y con varios selladores, borrar esta bóveda ya
29
+ // NO deja la cuenta muerta si queda otro que pueda sellar: eso es exactamente para lo
30
+ // que existe el multivault, y el freno tiene que dejar de estorbar en ese caso.
31
+ if (!isMaster || memberCount <= 1 || sealerCount > 1) return true
29
32
  const others = memberCount - 1
30
33
  const e = new Error(
31
- `this vault is the master of account "${name}" and it has ${others} more device(s): ` +
32
- 'hand the master over to one that is connected first. Deleting it like this leaves them ' +
33
- 'with their key and nobody able to sign the record again.'
34
+ `this vault is the only one that can seal account "${name}", and it has ${others} more device(s): ` +
35
+ 'give another one the sealing permission first (`caps <ID> +sella`). Deleting it like this ' +
36
+ 'leaves them with their key and nobody able to seal the record again.'
34
37
  )
35
38
  e.code = 'MASTER_WITH_MEMBERS'
36
39
  e.members = memberCount
@@ -159,7 +162,12 @@ export async function startVaultManager ({ root = dataDir(), proxyUrl, log = con
159
162
  v.isMaster().catch(() => false),
160
163
  v.profileMembers().catch(() => ({ members: [] }))
161
164
  ])
162
- assertCanRemove({ isMaster, memberCount: (record?.members || []).length, name: profiles.get(id)?.name || id })
165
+ assertCanRemove({
166
+ isMaster,
167
+ memberCount: (record?.members || []).length,
168
+ sealerCount: (record?.sealers || []).length || 1,
169
+ name: profiles.get(id)?.name || id
170
+ })
163
171
  } else {
164
172
  log('[vault] profile %s is not open: deleting without being able to check its record', id)
165
173
  }