@dotrino/identity 0.117.2 → 0.118.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 +1 -1
- package/vault/acta.js +8 -10
- package/vault/session.js +1 -1
- package/vault/vendor/passmanager/VERSION.txt +1 -1
- package/vault/vendor/passmanager/sealed/entry.js +1 -1
- package/vault/vendor/passmanager/sealed/store.js +16 -10
- package/vault/vendor/passmanager/vault/sealed-responder.js +18 -1
- package/vault/vendor/passmanager/vault/sealed.js +5 -1
- package/vault/vendor/roadmap/VERSION.txt +1 -1
- package/vault/vendor/roadmap/manifests.js +56 -26
- package/vault/vendor/vault/VERSION.txt +1 -1
- package/vault/vendor/vault/core/libVersion.js +1 -1
- package/vault/vendor/vault/core/passwords.js +0 -1
- package/vault/vendor/vault/core/vault.js +4 -4
package/package.json
CHANGED
package/vault/acta.js
CHANGED
|
@@ -105,7 +105,7 @@ const conCampoSellador = (acta) => Number(acta?.v) < V_SIN_CAMPO_SELLADOR
|
|
|
105
105
|
* el rol de master, que no se delega. Así un dispositivo con `admin` robado hace daño
|
|
106
106
|
* acotado y **reversible** (se le revoca), en vez de poder dejarte fuera de tu cuenta.
|
|
107
107
|
*/
|
|
108
|
-
export const CAPS = Object.freeze(['sign', 'store', 'read', 'admin', 'approve', 'passwords', '
|
|
108
|
+
export const CAPS = Object.freeze(['sign', 'store', 'read', 'admin', 'approve', 'passwords', 'sealer', 'unattended', 'replica'])
|
|
109
109
|
|
|
110
110
|
/** Capacidades de un DISPOSITIVO (sin CN): acceso a todo lo del usuario. */
|
|
111
111
|
/**
|
|
@@ -119,14 +119,12 @@ export const CAPS = Object.freeze(['sign', 'store', 'read', 'admin', 'approve',
|
|
|
119
119
|
* pedirle algo a la bóveda es exactamente lo que decide el acta — tener dos registros
|
|
120
120
|
* de lo mismo obliga a acordarse de los dos al quitar un aparato.
|
|
121
121
|
*
|
|
122
|
-
* `
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* Sin `passwords` no significa nada: la passkey vive en una entrada de contraseñas. Y una
|
|
129
|
-
* SESIÓN no lo lleva nunca, como `passwords` (`session.js`).
|
|
122
|
+
* Y `passwords` cubre TAMBIÉN LAS PASSKEYS (dueño, 2026-10-16: «realmente deberían ser uno
|
|
123
|
+
* solo»). Hasta la 0.117 había un `passkeys` aparte para abrir la privada de una passkey;
|
|
124
|
+
* se fusionó en este. Un acta vieja que todavía lo nombre sigue valiendo: un permiso
|
|
125
|
+
* desconocido se ignora (ver `checkShape`), y al siguiente cambio del acta desaparece.
|
|
126
|
+
* Lo que distingue una passkey es la POLÍTICA de la bóveda, no un permiso: añadirla es
|
|
127
|
+
* directo, reemplazarla o borrarla pide aprobación (`@dotrino/passmanager`).
|
|
130
128
|
*
|
|
131
129
|
* `replica` es REPARTIR, NO DECIDIR. Un replicador no tiene maestra: guarda el acta y los
|
|
132
130
|
* sobres —que ya vienen sellados a su destinatario, así que tampoco puede abrirlos— y los
|
|
@@ -151,7 +149,7 @@ export const CAPS = Object.freeze(['sign', 'store', 'read', 'admin', 'approve',
|
|
|
151
149
|
* de la cuenta, se ve en la pantalla de permisos como los demás, y se quita quitándolo —
|
|
152
150
|
* sin acordarse de un segundo registro escondido.
|
|
153
151
|
*/
|
|
154
|
-
export const DEVICE_CAPS = Object.freeze(['sign', 'store', 'read', 'admin', 'approve', 'passwords', '
|
|
152
|
+
export const DEVICE_CAPS = Object.freeze(['sign', 'store', 'read', 'admin', 'approve', 'passwords', 'sealer', 'unattended', 'replica'])
|
|
155
153
|
|
|
156
154
|
/**
|
|
157
155
|
* Lo que recibe un dispositivo recién emparejado. `admin` **no está**: no se
|
package/vault/session.js
CHANGED
|
@@ -57,7 +57,7 @@ export const SESSION_SCOPES = Object.freeze(['id:whoami', 'vault:store'])
|
|
|
57
57
|
* blanca a propósito: si mañana alguien añade un alcance a `SESSION_SCOPES` sin pensarlo,
|
|
58
58
|
* esto sigue cortando lo que no puede pasar.
|
|
59
59
|
*/
|
|
60
|
-
export const SESSION_FORBIDDEN = Object.freeze(['secrets', 'admin', 'approve', 'sealer', 'passwords', '
|
|
60
|
+
export const SESSION_FORBIDDEN = Object.freeze(['secrets', 'admin', 'approve', 'sealer', 'passwords', 'unattended', 'replica'])
|
|
61
61
|
|
|
62
62
|
/** Qué capacidad del acta hace falta para conceder cada alcance de sesión. */
|
|
63
63
|
const SCOPE_NEEDS = Object.freeze({ 'id:whoami': null, 'vault:store': 'store' })
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Copia vendorizada de @dotrino/passmanager@0.
|
|
1
|
+
Copia vendorizada de @dotrino/passmanager@0.18.1 (dotrino-passmanager/lib/src/{crypto,fields,generate,import,index,match,model,pubkey,sealed/convert,sealed/device,sealed/entry,sealed/index,sealed/keys,sealed/recovery,sealed/store,session-cache,totp,transport/protocol,transport/proxy,transport/sealed,vault/approval,vault/errors,vault/guard,vault/interface,vault/local,vault/remote,vault/responder,vault/sealed-local,vault/sealed-responder,vault/sealed,webauthn}.js).
|
|
2
2
|
NO se edita a mano: la escribe `node vendor.mjs` y la vigila test/vendor-up-to-date.test.mjs.
|
|
3
3
|
El sobre de las contraseñas, el mismo que usan el binario y la extensión. Importa @dotrino/identity/content.
|
|
@@ -78,7 +78,7 @@ export async function sealEntry ({ plain, main, passkeys = null, keys }) {
|
|
|
78
78
|
if (e.webauthn.privateKey) {
|
|
79
79
|
if (!passkeys?.cek || typeof passkeys.gen !== 'number') {
|
|
80
80
|
throw Object.assign(
|
|
81
|
-
new Error('this entry carries a passkey: it needs its own generation
|
|
81
|
+
new Error('this entry carries a passkey: it needs its own generation'),
|
|
82
82
|
{ code: 'passkeys-generation-missing' })
|
|
83
83
|
}
|
|
84
84
|
fields[PASSKEY_FIELD] = await sealV(passkeys.cek, passkeys.gen, e.webauthn.privateKey)
|
|
@@ -38,8 +38,9 @@ export class SealedStore {
|
|
|
38
38
|
* @param {object} store `{ get(k), set(k, v) }`
|
|
39
39
|
* @param {object} opts
|
|
40
40
|
* `recipients(kind)` → `[{ pub, encPub }]` quién DEBE tener envoltura. `kind` es
|
|
41
|
-
* `'main'`
|
|
42
|
-
*
|
|
41
|
+
* `'main'` o `'passkeys'` (la generación aparte de la privada de una passkey). Desde
|
|
42
|
+
* 2026-10-16 las dos son los aparatos con `passwords`: el permiso `passkeys` se fusionó
|
|
43
|
+
* en ese. Lo pone quien conoce el acta: esta pieza no la lee.
|
|
43
44
|
* `verifyAuthor({ body, author })` → ¿la firmó quien dice, y puede escribir? Lo
|
|
44
45
|
* contesta quien tiene el acta. **No es opcional**: sin ella, cualquiera que llegue
|
|
45
46
|
* al almacén podría dejar una entrada a nombre de otro, y la bóveda no puede mirar
|
|
@@ -97,13 +98,12 @@ export class SealedStore {
|
|
|
97
98
|
'the wraps must include the recovery one (#recovery), or this entry could never be handed out again')
|
|
98
99
|
}
|
|
99
100
|
const deben = (await this.recipients(kind)).filter((m) => m?.encPub).map((m) => m.pub)
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
if (kind === 'passkeys' && !deben.length) {
|
|
101
|
+
// Una passkey no se guarda para NADIE: sin aparatos que puedan abrirla y sin copia de
|
|
102
|
+
// recuperación, se perdería entera. Con la copia basta: quien reciba el permiso después
|
|
103
|
+
// la recibe al re-repartir (`rewrapAll`).
|
|
104
|
+
if (kind === 'passkeys' && !deben.length && !wraps[RECOVERY]) {
|
|
105
105
|
throw new SealedError('no-passkeys-device',
|
|
106
|
-
'
|
|
106
|
+
'nobody could ever open this passkey: no device has `passwords` and the account has no recovery copy')
|
|
107
107
|
}
|
|
108
108
|
const hay = Object.keys(wraps).filter((k) => k !== RECOVERY)
|
|
109
109
|
if (!same(deben, hay)) {
|
|
@@ -127,7 +127,7 @@ export class SealedStore {
|
|
|
127
127
|
// reparte una llave que nadie va a usar.
|
|
128
128
|
if (llevaPasskey && !tienePasskeys) {
|
|
129
129
|
throw new SealedError('passkeys-generation-missing',
|
|
130
|
-
'this entry carries a passkey: it needs its own wraps
|
|
130
|
+
'this entry carries a passkey: it needs its own wraps (its own generation)')
|
|
131
131
|
}
|
|
132
132
|
if (!llevaPasskey && tienePasskeys) {
|
|
133
133
|
throw new SealedError('passkeys-generation-unused', 'there is no passkey in this entry to wrap')
|
|
@@ -225,7 +225,7 @@ export class SealedStore {
|
|
|
225
225
|
if (passkeys) await this.#checkWraps('passkeys', passkeys.wraps)
|
|
226
226
|
if (fields[PASSKEY_FIELD] && !passkeys) {
|
|
227
227
|
throw new SealedError('passkeys-generation-missing',
|
|
228
|
-
'that patch carries a passkey: it needs its own wraps
|
|
228
|
+
'that patch carries a passkey: it needs its own wraps (its own generation)')
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
const s = await this.#state()
|
|
@@ -343,6 +343,12 @@ export class SealedStore {
|
|
|
343
343
|
return s.entries.find((e) => e.id === id)?.priv || []
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
+
/** ¿Esa entrada lleva una passkey? La bóveda lo sabe sin abrir nada: por su huella `wa`. */
|
|
347
|
+
async hasPasskey (id) {
|
|
348
|
+
const s = await this.#state()
|
|
349
|
+
return !!s.entries.find((e) => e.id === id)?.wa
|
|
350
|
+
}
|
|
351
|
+
|
|
346
352
|
async remove (id) {
|
|
347
353
|
const s = await this.#state()
|
|
348
354
|
const antes = s.entries.length
|
|
@@ -119,8 +119,25 @@ export class SealedResponder {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* ¿CAMBIA O QUITA UNA PASSKEY QUE YA ESTÁ? Añadir es directo y no pregunta; reemplazarla o
|
|
124
|
+
* borrarla, sí — como las variables de `dotrino-env` (dueño, 2026-10-16). Una passkey
|
|
125
|
+
* borrada o pisada deja a alguien fuera de un sitio, y no hay forma de deshacerlo.
|
|
126
|
+
*
|
|
127
|
+
* Subir el contador al entrar NO es reemplazar: cambia la vista, no la llave.
|
|
128
|
+
*/
|
|
129
|
+
async replacesPasskey (op, payload) {
|
|
130
|
+
if (op === 'pm2.remove') return this.store.hasPasskey(payload?.id)
|
|
131
|
+
if (op === 'pm2.put') return this.store.hasPasskey(payload?.entry?.id)
|
|
132
|
+
if (op === 'pm2.patch') {
|
|
133
|
+
return !!payload?.fields?.[PASSKEY_FIELD] || (payload?.drop || []).includes(PASSKEY_FIELD)
|
|
134
|
+
}
|
|
135
|
+
return false
|
|
136
|
+
}
|
|
137
|
+
|
|
122
138
|
async #needsApproval (op, payload, pubkey) {
|
|
123
|
-
|
|
139
|
+
if (!await this.needsApproval(pubkey)) return false
|
|
140
|
+
return await this.wantsPrivate(op, payload) || await this.replacesPasskey(op, payload)
|
|
124
141
|
}
|
|
125
142
|
|
|
126
143
|
async #run (op, payload = {}, pubkey) {
|
|
@@ -262,7 +262,11 @@ export class SealedVault {
|
|
|
262
262
|
fields: actual.fieldKeys.some((k) => !FIXED.includes(k)),
|
|
263
263
|
webauthn: actual.hasWebauthn
|
|
264
264
|
},
|
|
265
|
-
|
|
265
|
+
// El contador de la passkey sube en cada firma y vive en la vista: cambiarlo no toca
|
|
266
|
+
// la llave, así que no es reemplazarla (no pide aprobación).
|
|
267
|
+
...(actual.webauthn
|
|
268
|
+
? { webauthn: Number.isInteger(changes.signCount) ? { ...actual.webauthn, signCount: changes.signCount } : actual.webauthn }
|
|
269
|
+
: {})
|
|
266
270
|
}
|
|
267
271
|
const idx = sites ? await Promise.all(sites.map((s) => siteIndex(keys.kidx, s))) : null
|
|
268
272
|
const parche = await buildSealedPatch({
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Copia vendorizada de @dotrino/roadmap@0.4.
|
|
1
|
+
Copia vendorizada de @dotrino/roadmap@0.4.20 (dotrino-roadmap/src/{index,manifests}.js).
|
|
2
2
|
NO se edita a mano: la escribe `node vendor.mjs` y la vigila test/vendor-up-to-date.test.mjs.
|
|
3
3
|
El registro de versiones rotas que viaja con cada versión. Importa @dotrino/compat/ranges.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
export const MANIFESTS = Object.freeze({
|
|
9
9
|
"dotrino": {
|
|
10
10
|
"manifest": "dotrino",
|
|
11
|
-
"seq":
|
|
11
|
+
"seq": 29,
|
|
12
12
|
"issued": "2026-10-15",
|
|
13
13
|
"note": "Registro comun de compatibilidad del ecosistema. Se edita a mano en este repo y se consulta con @dotrino/roadmap. Rangos: ver @dotrino/compat/ranges (x.y.z, >= > <= <, «a - b», «*», o una lista que es un O).",
|
|
14
14
|
"products": {
|
|
@@ -31,7 +31,7 @@ export const MANIFESTS = Object.freeze({
|
|
|
31
31
|
"identity": {
|
|
32
32
|
"repo": "imdotrino/dotrino-identity",
|
|
33
33
|
"npm": "@dotrino/identity",
|
|
34
|
-
"current": "0.117.
|
|
34
|
+
"current": "0.117.2",
|
|
35
35
|
"protocol": 1,
|
|
36
36
|
"requires": {}
|
|
37
37
|
},
|
|
@@ -47,11 +47,11 @@ export const MANIFESTS = Object.freeze({
|
|
|
47
47
|
},
|
|
48
48
|
"messenger": {
|
|
49
49
|
"repo": "imdotrino/dotrino-messenger",
|
|
50
|
-
"current": "0.8.
|
|
50
|
+
"current": "0.8.1",
|
|
51
51
|
"protocol": 2,
|
|
52
52
|
"requires": {
|
|
53
53
|
"identity": ">=0.117.0",
|
|
54
|
-
"proxy-client": ">=0.
|
|
54
|
+
"proxy-client": ">=0.36.1",
|
|
55
55
|
"store": ">=0.12.3",
|
|
56
56
|
"proxy": "1.6.1+"
|
|
57
57
|
}
|
|
@@ -119,7 +119,7 @@ export const MANIFESTS = Object.freeze({
|
|
|
119
119
|
"proxy-client": {
|
|
120
120
|
"repo": "imdotrino/dotrino-proxy-client",
|
|
121
121
|
"npm": "@dotrino/proxy-client",
|
|
122
|
-
"current": "0.
|
|
122
|
+
"current": "0.36.1",
|
|
123
123
|
"protocol": 2,
|
|
124
124
|
"requires": {
|
|
125
125
|
"identity": ">=0.53.0",
|
|
@@ -129,25 +129,25 @@ export const MANIFESTS = Object.freeze({
|
|
|
129
129
|
"remote-agent": {
|
|
130
130
|
"repo": "imdotrino/dotrino-remote-agent",
|
|
131
131
|
"npm": "@dotrino/remote-agent",
|
|
132
|
-
"current": "0.
|
|
132
|
+
"current": "0.20.1",
|
|
133
133
|
"protocol": 1,
|
|
134
134
|
"requires": {
|
|
135
|
-
"identity": ">=0.
|
|
135
|
+
"identity": ">=0.117.2",
|
|
136
136
|
"vault": ">=0.85.0",
|
|
137
|
-
"proxy-client": ">=0.
|
|
137
|
+
"proxy-client": ">=0.36.1",
|
|
138
138
|
"vaultd": ">=0.154.0"
|
|
139
139
|
}
|
|
140
140
|
},
|
|
141
141
|
"terminal-agent": {
|
|
142
142
|
"repo": "imdotrino/dotrino-terminal",
|
|
143
143
|
"npm": "@dotrino/terminal-agent",
|
|
144
|
-
"current": "0.
|
|
144
|
+
"current": "0.48.0",
|
|
145
145
|
"protocol": 2,
|
|
146
146
|
"requires": {
|
|
147
|
-
"remote-agent": ">=0.
|
|
148
|
-
"identity": ">=0.
|
|
149
|
-
"vault": ">=0.
|
|
150
|
-
"proxy-client": ">=0.
|
|
147
|
+
"remote-agent": ">=0.20.1",
|
|
148
|
+
"identity": ">=0.117.2",
|
|
149
|
+
"vault": ">=0.87.0",
|
|
150
|
+
"proxy-client": ">=0.36.1",
|
|
151
151
|
"vaultd": ">=0.142.0"
|
|
152
152
|
}
|
|
153
153
|
},
|
|
@@ -250,12 +250,12 @@ export const MANIFESTS = Object.freeze({
|
|
|
250
250
|
"vault": {
|
|
251
251
|
"repo": "imdotrino/dotrino-vault",
|
|
252
252
|
"npm": "@dotrino/vault",
|
|
253
|
-
"current": "0.
|
|
253
|
+
"current": "0.87.0",
|
|
254
254
|
"protocol": 1,
|
|
255
255
|
"requires": {
|
|
256
256
|
"vaultd": ">=0.105.0",
|
|
257
|
-
"proxy-client": "0.
|
|
258
|
-
"identity": ">=0.
|
|
257
|
+
"proxy-client": ">=0.36.1",
|
|
258
|
+
"identity": ">=0.117.2",
|
|
259
259
|
"opaque": ">=0.1.0",
|
|
260
260
|
"compat": ">=0.4.0",
|
|
261
261
|
"roadmap": ">=0.4.13",
|
|
@@ -266,11 +266,11 @@ export const MANIFESTS = Object.freeze({
|
|
|
266
266
|
"vaultd": {
|
|
267
267
|
"repo": "imdotrino/dotrino-vault",
|
|
268
268
|
"npm": "@dotrino/vaultd",
|
|
269
|
-
"current": "0.
|
|
269
|
+
"current": "0.159.0",
|
|
270
270
|
"protocol": 1,
|
|
271
271
|
"requires": {
|
|
272
|
-
"identity": ">=0.
|
|
273
|
-
"proxy-client": ">=0.
|
|
272
|
+
"identity": ">=0.117.2",
|
|
273
|
+
"proxy-client": ">=0.36.1",
|
|
274
274
|
"store": ">=0.11.0",
|
|
275
275
|
"passmanager": ">=0.17.0",
|
|
276
276
|
"compat": ">=0.4.0",
|
|
@@ -290,11 +290,11 @@ export const MANIFESTS = Object.freeze({
|
|
|
290
290
|
"passmanager": {
|
|
291
291
|
"repo": "imdotrino/dotrino-passmanager",
|
|
292
292
|
"npm": "@dotrino/passmanager",
|
|
293
|
-
"current": "0.
|
|
293
|
+
"current": "0.18.0",
|
|
294
294
|
"protocol": 1,
|
|
295
295
|
"requires": {
|
|
296
|
-
"identity": ">=0.
|
|
297
|
-
"proxy-client": ">=0.
|
|
296
|
+
"identity": ">=0.117.2",
|
|
297
|
+
"proxy-client": ">=0.36.1"
|
|
298
298
|
}
|
|
299
299
|
},
|
|
300
300
|
"passmanager-android": {
|
|
@@ -317,12 +317,12 @@ export const MANIFESTS = Object.freeze({
|
|
|
317
317
|
},
|
|
318
318
|
"passmanager-extension": {
|
|
319
319
|
"repo": "imdotrino/dotrino-passmanager",
|
|
320
|
-
"current": "0.
|
|
320
|
+
"current": "0.51.1",
|
|
321
321
|
"protocol": 1,
|
|
322
322
|
"requires": {
|
|
323
|
-
"identity": ">=0.
|
|
324
|
-
"proxy-client": ">=0.
|
|
325
|
-
"vault": ">=0.
|
|
323
|
+
"identity": ">=0.117.2",
|
|
324
|
+
"proxy-client": ">=0.36.1",
|
|
325
|
+
"vault": ">=0.87.0",
|
|
326
326
|
"proxy": "1.1.0+"
|
|
327
327
|
}
|
|
328
328
|
},
|
|
@@ -349,6 +349,30 @@ export const MANIFESTS = Object.freeze({
|
|
|
349
349
|
}
|
|
350
350
|
},
|
|
351
351
|
"broken": [
|
|
352
|
+
{
|
|
353
|
+
"product": "passmanager-extension",
|
|
354
|
+
"versions": "0.50.0",
|
|
355
|
+
"why": "la página que pide una passkey decía ella misma de qué sitio era (rpId y origen salían del mensaje, no del remitente): un sitio cualquiera podía pedir la firma de la passkey de otro sitio guardada en Dotrino",
|
|
356
|
+
"fix": "sube a la extensión 0.51.0 (se actualiza sola desde pass.dotrino.com/app)"
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"product": "passmanager-extension",
|
|
360
|
+
"versions": "0.49.1",
|
|
361
|
+
"why": "la página que pide una passkey decía ella misma de qué sitio era (rpId y origen salían del mensaje, no del remitente): un sitio cualquiera podía pedir la firma de la passkey de otro sitio guardada en Dotrino",
|
|
362
|
+
"fix": "sube a la extensión 0.51.0 (se actualiza sola desde pass.dotrino.com/app)"
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"product": "passmanager-extension",
|
|
366
|
+
"versions": "0.49.0",
|
|
367
|
+
"why": "la página que pide una passkey decía ella misma de qué sitio era (rpId y origen salían del mensaje, no del remitente): un sitio cualquiera podía pedir la firma de la passkey de otro sitio guardada en Dotrino",
|
|
368
|
+
"fix": "sube a la extensión 0.51.0 (se actualiza sola desde pass.dotrino.com/app)"
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
"product": "passmanager-extension",
|
|
372
|
+
"versions": "0.48.4",
|
|
373
|
+
"why": "la página que pide una passkey decía ella misma de qué sitio era (rpId y origen salían del mensaje, no del remitente): un sitio cualquiera podía pedir la firma de la passkey de otro sitio guardada en Dotrino",
|
|
374
|
+
"fix": "sube a la extensión 0.51.0 (se actualiza sola desde pass.dotrino.com/app)"
|
|
375
|
+
},
|
|
352
376
|
{
|
|
353
377
|
"product": "terminal-agent",
|
|
354
378
|
"versions": "0.46.4",
|
|
@@ -802,6 +826,12 @@ export const MANIFESTS = Object.freeze({
|
|
|
802
826
|
"versions": "1.6.0",
|
|
803
827
|
"why": "pasa a base64 los mensajes binarios para cruzar la malla, y una parte de 768 KB así pasa de la trama de 1 MB del otro nodo: el enlace se cae en cada reconexión al reenviarla y todo lo que va detrás entre los dos nodos se queda atascado",
|
|
804
828
|
"fix": "websocket-proxy 1.6.1"
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
"product": "proxy-client",
|
|
832
|
+
"versions": "0.36.0",
|
|
833
|
+
"why": "por token frena los bytes por lo recordado de la identidad (que puede ser de antes de que el otro se actualizara), y no solo por lo que dijo esa conexión en su saludo",
|
|
834
|
+
"fix": "@dotrino/proxy-client 0.36.1"
|
|
805
835
|
}
|
|
806
836
|
]
|
|
807
837
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copia vendorizada de @dotrino/vault@0.
|
|
1
|
+
Copia vendorizada de @dotrino/vault@0.88.0 (dotrino-vault/lib/src/{admin,b64,browser,core/approvals,core/blocked,core/browserDisk,core/commKey,core/disk,core/libVersion,core/machines,core/notices,core/notifiable,core/passwords,core/sealKey,core/sealer,core/sealers,core/secretsStore,core/sha256,core/store,core/subacta,core/threadStore,core/transport,core/vault,enroll,index,loginClient,passwordLogins,procContext,protocol,revocation,sealed}.js).
|
|
2
2
|
NO se edita a mano: la escribe `node vendor.mjs` y la vigila test/vendor-up-to-date.test.mjs.
|
|
3
3
|
browser.js es la bóveda de esta pestaña: el MISMO núcleo que el binario (core/), con
|
|
4
4
|
el disco en IndexedDB. Importa @dotrino/{compat,roadmap,passmanager,store/core} y
|
|
@@ -311,14 +311,14 @@ export async function startVault ({ platform, proxyUrl, log = console.log, onEnr
|
|
|
311
311
|
* eso se filtran explícitamente aquí, que es donde se construye la lista, y no en quien la
|
|
312
312
|
* consume (`sealed-passwords.md` §2.1).
|
|
313
313
|
*
|
|
314
|
-
* `main`
|
|
315
|
-
*
|
|
314
|
+
* `main` y `passkeys` (la generación aparte de la privada de una passkey) son los MISMOS:
|
|
315
|
+
* los aparatos con `contraseñas`. Hubo un permiso `passkeys` aparte y se fusionó en ese
|
|
316
|
+
* (dueño, 2026-10-16: «realmente deberían ser uno solo»); `kind` se queda por el formato. Un miembro sin llave de cifrado no puede recibir nada: se
|
|
316
317
|
* queda fuera y se ve en `incompleteMembers`.
|
|
317
318
|
*/
|
|
318
319
|
const noEsLaBoveda = (pub) => pub !== master && pub !== commKey.pub()
|
|
319
|
-
const passwordRecipients = (
|
|
320
|
+
const passwordRecipients = (_kind = 'main') => (actaCache?.members || [])
|
|
320
321
|
.filter((m) => m.encPub && noEsLaBoveda(m.pub) && Acta.memberCan(actaCache, m.pub, 'passwords'))
|
|
321
|
-
.filter((m) => kind !== 'passkeys' || Acta.memberCan(actaCache, m.pub, 'passkeys'))
|
|
322
322
|
.map((m) => ({ pub: m.pub, encPub: m.encPub }))
|
|
323
323
|
|
|
324
324
|
/**
|