@dotrino/vault 0.82.2 → 0.84.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/package.json +2 -2
- package/src/enroll.js +12 -7
- package/src/index.js +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/vault",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.84.0",
|
|
4
4
|
"description": "Usa ESTE dispositivo (navegador) como bóveda/CA del ecosistema Dotrino: atiende enrolamientos por el proxy y firma certificados de delegación a tus máquinas. Incluye el cliente de SERVICIO (Node): un proyecto se enrola una vez y jala sus credenciales del vault en vez del .env (`import '@dotrino/vault/config'`).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"pairing"
|
|
60
60
|
],
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"@dotrino/identity": ">=0.
|
|
62
|
+
"@dotrino/identity": ">=0.111.0",
|
|
63
63
|
"@dotrino/proxy-client": ">=0.23.1"
|
|
64
64
|
},
|
|
65
65
|
"license": "MIT",
|
package/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, canSeal, DEVICE_CAPS } from '@dotrino/identity/acta'
|
|
33
|
+
import { verifyContinuity, canSeal, DEVICE_CAPS, VAULT_CN } 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
|
|
@@ -169,12 +169,16 @@ export function createEnrollDesk ({
|
|
|
169
169
|
* el código, igual que hacía `caps` después.
|
|
170
170
|
*/
|
|
171
171
|
async function startPairing ({ scope = defaultScope, ttlMs = defaultTtlMs, label = '', mode = 'join', account = '', caps = null } = {}) {
|
|
172
|
+
// El nombre de la propia bóveda no es un servicio: su llave firma con el perfil cerrado
|
|
173
|
+
// y no puede abrir ningún cajón. Un servicio que entrara con ese nombre tampoco podría.
|
|
174
|
+
if (scopeToCn(scope) === VAULT_CN) throw Object.assign(new Error(`"${VAULT_CN}" is the vault's own name: a service cannot use it`), { code: 'reserved-cn' })
|
|
172
175
|
if (caps != null) {
|
|
173
176
|
if (!Array.isArray(caps)) throw Object.assign(new Error('caps must be a list'), { code: 'bad-caps' })
|
|
174
177
|
const cn = scopeToCn(scope)
|
|
175
|
-
const bad = caps.filter((c) => !
|
|
178
|
+
const bad = caps.filter((c) => !DEVICE_CAPS.includes(c))
|
|
176
179
|
if (bad.length) throw Object.assign(new Error('unknown permission(s): ' + bad.join(', ')), { code: 'bad-caps' })
|
|
177
|
-
|
|
180
|
+
// Un servicio puede entrar sin ningún permiso: su cajón lo abre el CN.
|
|
181
|
+
if (!caps.length && !cn) throw Object.assign(new Error('the pairing grants no permission'), { code: 'empty-scope' })
|
|
178
182
|
caps = [...new Set(caps)]
|
|
179
183
|
}
|
|
180
184
|
pending.clear() // uno a la vez: una sesión nueva supersede a la anterior
|
|
@@ -378,13 +382,14 @@ export function createEnrollDesk ({
|
|
|
378
382
|
throw Object.assign(new Error('this vault cannot add devices to the account record: no certificate was issued'), { code: 'admit-unavailable' })
|
|
379
383
|
}
|
|
380
384
|
// PERMISOS, no tipos (2026-08-22): las capacidades son las del scope ENTERO. Un
|
|
381
|
-
// cajón (`secrets:<ns>`)
|
|
382
|
-
// con `sign,secrets:eco` firma como
|
|
385
|
+
// cajón (`secrets:<ns>`) fija el CN, que es lo que lo abre —no hay permiso aparte
|
|
386
|
+
// (dueño, 2026-10-13)—, y no borra lo demás: un bot con `sign,secrets:eco` firma como
|
|
387
|
+
// aparato del acta Y lee solo su cajón.
|
|
383
388
|
// Si quien emparejó ELIGIÓ los permisos (`startPairing({ caps })`), entran esos y en
|
|
384
389
|
// esta misma acta; si no, los que da el scope.
|
|
385
390
|
const cn = scopeToCn(pend.scope)
|
|
386
|
-
const caps = [...new Set(
|
|
387
|
-
if (!caps.length) {
|
|
391
|
+
const caps = [...new Set(pend.caps || scopeToCaps(pend.scope))]
|
|
392
|
+
if (!caps.length && !cn) {
|
|
388
393
|
throw Object.assign(new Error('the pairing scope grants no permission: no certificate was issued'), { code: 'empty-scope' })
|
|
389
394
|
}
|
|
390
395
|
|
package/src/index.js
CHANGED
|
@@ -217,7 +217,10 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
|
|
|
217
217
|
deviceId: x.sub ? await deviceIdOf(x.sub) : null, sub: x.sub || null,
|
|
218
218
|
label: x.label || '', scope: x.scope, seq: x.seq, nonce: x.nonce
|
|
219
219
|
})))
|
|
220
|
-
|
|
220
|
+
// La cadena de selladores va con la lista (como en el daemon): es lo que un aparato de
|
|
221
|
+
// Node necesita para firmar a nombre de la cuenta de forma que un extraño lo compruebe.
|
|
222
|
+
const sealerChain = await identity.sealerChain?.().catch(() => null) || null
|
|
223
|
+
send(from, { type: MSG.DEVICES_RESULT, devices, revoked: (revoked || []).map((r) => r.nonce || r), sealerChain })
|
|
221
224
|
// ¿el que consulta es una máquina revocada que reapareció? → re-emite el REVOKED firmado.
|
|
222
225
|
// Se busca en `revokedCerts`: desde que `issued` solo trae lo vigente, los retirados
|
|
223
226
|
// ya no están ahí y este aviso dejaba de dispararse (se caía en silencio).
|