@dotrino/vaultd 0.58.0 → 0.59.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 +6 -1
- package/package.json +1 -1
- package/src/ctl.js +19 -3
- package/src/tui/app.js +36 -5
- package/src/tui/i18n.js +4 -0
- package/src/vaultControl.js +2 -2
package/lib/src/enroll.js
CHANGED
|
@@ -277,7 +277,12 @@ export function createEnrollDesk ({
|
|
|
277
277
|
pend.continuity = (okC && d.continuity.member === d.dpub) ? d.continuity : null
|
|
278
278
|
}
|
|
279
279
|
pend.from = from // la bóveda NO conoce el código: lo aprende cuando lo tipeas
|
|
280
|
-
|
|
280
|
+
// EL NOMBRE QUE PUSISTE AQUÍ MANDA. El aparato manda el suyo al enrolarse, y hasta
|
|
281
|
+
// ahora pisaba siempre al de la bóveda — como el aparato usa por defecto el apodo del
|
|
282
|
+
// PERFIL, acababas con varios dispositivos llamados igual que tú y sin forma de saber
|
|
283
|
+
// cuál era cuál. Si en `pair` le diste un nombre, ese es el nombre; el del aparato
|
|
284
|
+
// sigue valiendo como propuesta cuando no dijiste nada.
|
|
285
|
+
if (d.label && !pend.label) pend.label = String(d.label).slice(0, 60)
|
|
281
286
|
// Camino A: de qué cuenta estamos hablando. Se guarda para poder comprobar, cuando
|
|
282
287
|
// llegue el acta sellada, que es la que este dispositivo dijo que iba a entregar.
|
|
283
288
|
if (intent === 'adopt' && typeof d.profileId === 'string') pend.profileId = d.profileId
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/vaultd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.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": {
|
package/src/ctl.js
CHANGED
|
@@ -205,6 +205,17 @@ async function cmdPair (args = []) {
|
|
|
205
205
|
// --approval: el aparato que entre pedirá tu aprobación (teléfono) en cada petición de
|
|
206
206
|
// claves privadas. Por defecto NO pide; se cambia después con `caps <ID> +permiso`.
|
|
207
207
|
const approval = args.includes('--approval')
|
|
208
|
+
// --name <n>: CÓMO SE VA A LLAMAR el aparato que entre, decidido aquí y antes de nada.
|
|
209
|
+
// Sin esto el nombre lo ponía el propio aparato, que por defecto usa el apodo del
|
|
210
|
+
// PERFIL: acababas con varios dispositivos llamados igual que tú, y para distinguirlos
|
|
211
|
+
// había que renombrarlos después —cuando ya no sabías cuál era cuál—.
|
|
212
|
+
const nIdx = args.indexOf('--name')
|
|
213
|
+
let label = null
|
|
214
|
+
if (nIdx >= 0) {
|
|
215
|
+
const v = args[nIdx + 1]
|
|
216
|
+
if (!v || v.startsWith('-')) { console.error('uso: dotrino-vault pair --name "teléfono de casa"'); process.exit(2) }
|
|
217
|
+
label = v.slice(0, 60)
|
|
218
|
+
}
|
|
208
219
|
// --admin: el aparato que entre por esta invitación podrá ADMINISTRAR (la consola).
|
|
209
220
|
// El QR no lleva nada: es una nota local de la bóveda y el permiso se aplica al aprobar
|
|
210
221
|
// con el código que tecleas aquí. Ahorra el `caps <ID> +administra` de después, que en un
|
|
@@ -258,9 +269,11 @@ async function cmdPair (args = []) {
|
|
|
258
269
|
// meter el dispositivo en una cuenta que ya vive aquí, se ESTRENA una (vacía) y
|
|
259
270
|
// entra a ella; las demás no se tocan. En la TUI esto es una pregunta con sus
|
|
260
271
|
// opciones; en la CLI es una bandera, para que siga sirviendo en un script.
|
|
272
|
+
// `--name` lleva su valor detrás: no puede confundirse con el nombre de la cuenta.
|
|
273
|
+
const consumido = (i) => nIdx >= 0 && i === nIdx + 1
|
|
261
274
|
const naIdx = args.findIndex((a) => a === '--new-account')
|
|
262
275
|
if (naIdx >= 0) {
|
|
263
|
-
const next = args[naIdx + 1]
|
|
276
|
+
const next = consumido(naIdx + 1) ? null : args[naIdx + 1]
|
|
264
277
|
const name = (next && !next.startsWith('-')) ? next : `cuenta ${new Date().toISOString().slice(0, 10)}`
|
|
265
278
|
const d = await profileRequest('add', { name, ...(pairKek ? { kek: pairKek } : {}) })
|
|
266
279
|
if (d.error) { console.error('%s', d.error); process.exit(1) }
|
|
@@ -275,7 +288,7 @@ async function cmdPair (args = []) {
|
|
|
275
288
|
const adIdx = args.findIndex((a) => a === '--adopt')
|
|
276
289
|
const adopt = adIdx >= 0
|
|
277
290
|
if (adopt) {
|
|
278
|
-
const next = args[adIdx + 1]
|
|
291
|
+
const next = consumido(adIdx + 1) ? null : args[adIdx + 1]
|
|
279
292
|
const name = (next && !next.startsWith('-')) ? next : `cuenta del dispositivo`
|
|
280
293
|
const d = await profileRequest('add', { name, adopt: true, ...(pairKek ? { kek: pairKek } : {}) })
|
|
281
294
|
if (d.error) { console.error('%s', d.error); process.exit(1) }
|
|
@@ -298,7 +311,7 @@ async function cmdPair (args = []) {
|
|
|
298
311
|
|
|
299
312
|
// La petición se escribe SIEMPRE (aunque no haya --service): lleva a qué perfil
|
|
300
313
|
// se empareja el dispositivo.
|
|
301
|
-
writeReq('pair-request.json', { ...(service ? { service } : {}), ...(scope ? { scope } : {}), ...(adopt ? { mode: 'adopt' } : {}), ...(approval ? { approval: true } : {}), ...(admin ? { admin: true } : {}) })
|
|
314
|
+
writeReq('pair-request.json', { ...(service ? { service } : {}), ...(scope ? { scope } : {}), ...(adopt ? { mode: 'adopt' } : {}), ...(approval ? { approval: true } : {}), ...(admin ? { admin: true } : {}), ...(label ? { label } : {}) })
|
|
302
315
|
sendSignal(s.pid, 'SIGUSR1')
|
|
303
316
|
|
|
304
317
|
let pair = null
|
|
@@ -1469,6 +1482,9 @@ function help () {
|
|
|
1469
1482
|
estrena una cuenta VACÍA en este vault y mete ahí al dispositivo
|
|
1470
1483
|
(sin la bandera entra a la cuenta activa, o a la de --profile)
|
|
1471
1484
|
pair --service <ns> empareja un SERVICIO (proxy, geo…) con acceso SOLO a sus secretos
|
|
1485
|
+
pair --name <nombre> cómo se llamará el aparato que entre. Sin esto el nombre lo pone
|
|
1486
|
+
ÉL, y por defecto usa el apodo del perfil: acabas con varios
|
|
1487
|
+
dispositivos llamados igual que tú
|
|
1472
1488
|
pair --approval el aparato que entre pedirá tu aprobación (teléfono) al recibir claves
|
|
1473
1489
|
pair --admin el aparato que entre podrá ADMINISTRAR (es lo que es una consola).
|
|
1474
1490
|
El QR no lleva nada: el permiso se aplica al aprobar el código aquí
|
package/src/tui/app.js
CHANGED
|
@@ -918,12 +918,41 @@ async function descartarCuenta (term, st, id, volverA) {
|
|
|
918
918
|
if (r.ok) flash(st, i.accountDiscarded)
|
|
919
919
|
}
|
|
920
920
|
|
|
921
|
+
/**
|
|
922
|
+
* PRIMERO EL NOMBRE, y luego el QR.
|
|
923
|
+
*
|
|
924
|
+
* Sin preguntarlo, el nombre lo pone el propio aparato al enrolarse, y por defecto usa el
|
|
925
|
+
* apodo del PERFIL: te quedaban tres dispositivos llamados igual que tú y ninguna forma de
|
|
926
|
+
* saber cuál era cuál sin renombrarlos después —cuando ya no te acordabas—. Se pregunta
|
|
927
|
+
* ANTES porque después ya estás mirando un QR con el teléfono en la mano.
|
|
928
|
+
*
|
|
929
|
+
* Enter en blanco sigue valiendo: deja que lo diga el aparato, que es el comportamiento de
|
|
930
|
+
* siempre para quien no quiera decidirlo.
|
|
931
|
+
*/
|
|
932
|
+
function beginPairingNamed (term, st, profile, service = null, after = null) {
|
|
933
|
+
const i = L(st)
|
|
934
|
+
setInput(st, {
|
|
935
|
+
label: i.nameDeviceLabel,
|
|
936
|
+
hint: i.nameDeviceHint,
|
|
937
|
+
onSubmit: async (nombre) => {
|
|
938
|
+
st.input = null
|
|
939
|
+
const ok = await beginPairing(term, st, profile, service, (nombre || '').trim().slice(0, 60) || null)
|
|
940
|
+
// `after` es para quien tenga que limpiar si el emparejamiento no llega a abrirse
|
|
941
|
+
// (la cuenta que nació para él). Va aquí porque con el nombre de por medio ya no se
|
|
942
|
+
// puede mirar el valor de retorno desde fuera.
|
|
943
|
+
if (after) await after(ok)
|
|
944
|
+
},
|
|
945
|
+
onCancel: () => { st.input = null }
|
|
946
|
+
})
|
|
947
|
+
return true
|
|
948
|
+
}
|
|
949
|
+
|
|
921
950
|
/**
|
|
922
951
|
* Abre el emparejamiento contra `profile` y salta a la pantalla del QR. Con `service`,
|
|
923
952
|
* el QR es el de un SERVICIO de ese namespace (cert limitado a sus variables).
|
|
924
953
|
*/
|
|
925
|
-
async function beginPairing (term, st, profile, service = null) {
|
|
926
|
-
const r = await guard(term, st, L(st).startingPairing, () => vc.startPairing({ profile, ...(service ? { service } : {}) }))
|
|
954
|
+
async function beginPairing (term, st, profile, service = null, label = null) {
|
|
955
|
+
const r = await guard(term, st, L(st).startingPairing, () => vc.startPairing({ profile, ...(service ? { service } : {}), ...(label ? { label } : {}) }))
|
|
927
956
|
// `service` se pega al estado porque el daemon no lo devuelve: la pantalla del QR
|
|
928
957
|
// tiene que poder decir qué se está entregando, que no es lo mismo un aparato tuyo
|
|
929
958
|
// que una máquina que solo va a leer la configuración del proxy.
|
|
@@ -983,7 +1012,7 @@ async function onKeyPairMode (term, st, key) {
|
|
|
983
1012
|
if (key.name === 'escape' || ch === 'b') { st.screen = 'devices'; return true }
|
|
984
1013
|
if (key.name !== 'enter' || !cur) return true
|
|
985
1014
|
|
|
986
|
-
if (cur.mode === 'here') {
|
|
1015
|
+
if (cur.mode === 'here') { return beginPairingNamed(term, st, activeId(st)) }
|
|
987
1016
|
|
|
988
1017
|
// SERVICIO: entra a la cuenta activa, pero con un certificado que solo sirve para
|
|
989
1018
|
// pedir las variables de SU namespace. El nombre del ns es el que luego pide el
|
|
@@ -1025,8 +1054,10 @@ async function onKeyPairMode (term, st, key) {
|
|
|
1025
1054
|
// Si el emparejamiento no llega a abrirse, la cuenta que se creó PARA él se va con
|
|
1026
1055
|
// él: si no, cada intento fallido dejaba una cuenta vacía —y encima activa— que
|
|
1027
1056
|
// luego había que ir a borrar a mano adivinando cuál era.
|
|
1028
|
-
|
|
1029
|
-
|
|
1057
|
+
beginPairingNamed(term, st, created, null, async (ok) => {
|
|
1058
|
+
if (!ok) await descartarCuenta(term, st, created, previa)
|
|
1059
|
+
else st.pairing.born = { id: created, from: previa }
|
|
1060
|
+
})
|
|
1030
1061
|
},
|
|
1031
1062
|
onCancel: () => { st.input = null }
|
|
1032
1063
|
})
|
package/src/tui/i18n.js
CHANGED
|
@@ -140,6 +140,8 @@ const es = {
|
|
|
140
140
|
cantRemoveMaster: 'Esta bóveda es la que manda: no se quita a sí misma.',
|
|
141
141
|
revokedCount: (n) => ` Revocados: ${n}`,
|
|
142
142
|
startingPairing: 'Iniciando emparejamiento…',
|
|
143
|
+
nameDeviceLabel: '¿Cómo se va a llamar este aparato?',
|
|
144
|
+
nameDeviceHint: 'Enter para dejar que lo diga él (usará tu apodo, y así no se distinguen)',
|
|
143
145
|
noPending: 'No hay ningún dispositivo pendiente',
|
|
144
146
|
noPendingToReject: 'No hay ningún dispositivo pendiente para rechazar',
|
|
145
147
|
rejecting: 'Rechazando…',
|
|
@@ -435,6 +437,8 @@ const en = {
|
|
|
435
437
|
cantRemoveMaster: 'This vault is the Master: it does not remove itself.',
|
|
436
438
|
revokedCount: (n) => ` Revoked: ${n}`,
|
|
437
439
|
startingPairing: 'Starting pairing…',
|
|
440
|
+
nameDeviceLabel: 'What will this device be called?',
|
|
441
|
+
nameDeviceHint: 'Enter to let it say (it will use your nickname, and then they all look alike)',
|
|
438
442
|
noPending: 'No device is waiting',
|
|
439
443
|
noPendingToReject: 'No device is waiting to be rejected',
|
|
440
444
|
rejecting: 'Rejecting…',
|
package/src/vaultControl.js
CHANGED
|
@@ -604,10 +604,10 @@ export function pairUrl (qr) {
|
|
|
604
604
|
return { url: inviteUrl(qr), code, payload: JSON.stringify(qr), b64: code }
|
|
605
605
|
}
|
|
606
606
|
|
|
607
|
-
export async function startPairing ({ profile, service } = {}) {
|
|
607
|
+
export async function startPairing ({ profile, service, label } = {}) {
|
|
608
608
|
requireAlive()
|
|
609
609
|
rm(F.pair); rm(F.pending)
|
|
610
|
-
await writeReq(F.pairReq, service ? { service } : {}, profile)
|
|
610
|
+
await writeReq(F.pairReq, { ...(service ? { service } : {}), ...(label ? { label } : {}) }, profile)
|
|
611
611
|
signalOrCleanup('SIGUSR1', [F.pairReq])
|
|
612
612
|
for (let i = 0; i < 50; i++) {
|
|
613
613
|
await sleep(100)
|