@dotrino/vaultd 0.26.2 → 0.38.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/README.md +142 -25
- package/bin/dotrino-vaultd.js +4 -4
- package/lib/README.md +13 -2
- package/lib/src/admin.js +88 -3
- package/lib/src/config.js +1 -1
- package/lib/src/enroll.js +26 -22
- package/lib/src/env.js +37 -17
- package/lib/src/envtext.js +94 -0
- package/lib/src/index.js +4 -4
- package/lib/src/invite.js +8 -8
- package/lib/src/protocol.js +12 -0
- package/lib/src/service.js +218 -92
- package/package.json +9 -4
- package/src/ctl.js +375 -90
- package/src/daemon.js +167 -45
- package/src/manager.js +6 -6
- package/src/profiles.js +27 -4
- package/src/secretsStore.js +191 -25
- package/src/transport.js +2 -2
- package/src/tui/app.js +512 -126
- package/src/tui/i18n.js +118 -22
- package/src/vault.js +351 -44
- package/src/vaultControl.js +253 -64
package/src/tui/i18n.js
CHANGED
|
@@ -37,7 +37,7 @@ const es = {
|
|
|
37
37
|
tabMe: 'Perfil',
|
|
38
38
|
// PERMISOS de un dispositivo (§9.1: se dice el beneficio, no el scope del cert).
|
|
39
39
|
titleCaps: 'Permisos del dispositivo',
|
|
40
|
-
capsFor: (id,
|
|
40
|
+
capsFor: (id, name) => `Permisos de ${id}${name ? ' · ' + name : ''}`,
|
|
41
41
|
capsNoMember: ' (este dispositivo ya no está en el acta)',
|
|
42
42
|
capsApplyHint: 'Enter marca o desmarca. Cada cambio se aplica y se avisa a tus otros aparatos.',
|
|
43
43
|
capName: {
|
|
@@ -63,7 +63,7 @@ const es = {
|
|
|
63
63
|
loadingProfile: 'Cargando el perfil…',
|
|
64
64
|
noProfile: ' (esta bóveda todavía no tiene perfil)',
|
|
65
65
|
noProfileHint: ' Edita tu nombre o tu foto en un dispositivo emparejado y pulsa F5.',
|
|
66
|
-
profileUpdated: (
|
|
66
|
+
profileUpdated: (when) => ` actualizado ${when}`,
|
|
67
67
|
fieldName: 'nombre',
|
|
68
68
|
fieldPhoto: 'foto',
|
|
69
69
|
fieldFirstName: 'nombres',
|
|
@@ -74,7 +74,6 @@ const es = {
|
|
|
74
74
|
links: 'Enlaces',
|
|
75
75
|
otherData: 'Otros datos',
|
|
76
76
|
hidden: ' (oculto)',
|
|
77
|
-
noName: '(sin nombre)',
|
|
78
77
|
no: 'no',
|
|
79
78
|
helpMe: ['←→ pestaña', 'F5 refrescar', 'Esc bóvedas', 'l English', 'q salir'],
|
|
80
79
|
tabsHint: ' (←→ cambiar)',
|
|
@@ -87,7 +86,7 @@ const es = {
|
|
|
87
86
|
locked: '🔒 bloqueada',
|
|
88
87
|
unlocked: '🔓 abierta',
|
|
89
88
|
passwordOf: (name) => `Contraseña de "${name}"`,
|
|
90
|
-
passwordToEdit: 'necesaria para
|
|
89
|
+
passwordToEdit: 'necesaria para abrir la bóveda: sin ella no se ve ni se toca',
|
|
91
90
|
unlocking: 'Desbloqueando…',
|
|
92
91
|
loading: 'Cargando…',
|
|
93
92
|
loadingDevices: 'Cargando dispositivos…',
|
|
@@ -170,14 +169,52 @@ const es = {
|
|
|
170
169
|
savingVar: 'Guardando variable…',
|
|
171
170
|
varSaved: (ns, key) => `Guardado ${ns}/${key}`,
|
|
172
171
|
|
|
172
|
+
// CARGAR VARIAS DE UNA VEZ (tecla c). Una a una, cada variable es un cambio de
|
|
173
|
+
// configuración y el servicio se reinicia a media carga; juntas, se reinicia una vez.
|
|
174
|
+
loadLabel: (ns) => `Cargar varias en "${ns}"`,
|
|
175
|
+
loadHint: 'CLAVE=valor CLAVE2=valor2 · o la ruta de un archivo .env',
|
|
176
|
+
loadNoFile: (f) => `No se pudo leer ${f}`,
|
|
177
|
+
loadingVars: 'Cargando variables…',
|
|
178
|
+
loadedVars: (n, ns) => `${n} variables cargadas en ${ns} · un solo aviso de cambio`,
|
|
179
|
+
envErr: {
|
|
180
|
+
shape: (e) => `línea ${e.line}: no tiene la forma CLAVE=valor`,
|
|
181
|
+
key: (e) => `línea ${e.line}: «${e.key}» va en MAYUSCULAS_CON_GUION_BAJO`,
|
|
182
|
+
novalue: (e) => `línea ${e.line}: ${e.key} no tiene valor`,
|
|
183
|
+
dup: (e) => `línea ${e.line}: ${e.key} ya venía en la línea ${e.first}`,
|
|
184
|
+
empty: () => 'No hay ninguna variable que cargar'
|
|
185
|
+
},
|
|
186
|
+
loadNothing: 'No se cargó nada:',
|
|
187
|
+
varPublic: 'pública',
|
|
188
|
+
newVarPublicAsk: '¿Que su valor se pueda VER desde la consola remota? (las demás no salen de esta máquina)',
|
|
189
|
+
makePublicConfirm: '¿Dejar que su valor se vea desde la consola remota?',
|
|
190
|
+
changingVisibility: 'Cambiando visibilidad…',
|
|
191
|
+
nowPublic: 'Ahora su valor se puede ver desde la consola remota',
|
|
192
|
+
nowPrivate: 'Ahora su valor no sale de esta máquina',
|
|
193
|
+
|
|
194
|
+
// variables de UN aparato (Dispositivos → tecla e). Las del scope las comparten todos
|
|
195
|
+
// los aparatos que sirven ese namespace; estas las lee solo él, y le ganan.
|
|
196
|
+
titleDevVars: 'Variables del dispositivo',
|
|
197
|
+
devVarsFor: (id, name) => `Variables de ${id}${name ? ' · ' + name : ''}`,
|
|
198
|
+
devVarsService: (cn) => `servicio «${cn}» — solo las lee este aparato, y pisan a las del scope`,
|
|
199
|
+
noDevVars: '(sin variables propias — pulsa N para agregar la primera)',
|
|
200
|
+
devVarsOnlyServices: 'Solo un servicio lee variables, y este aparato no lo es',
|
|
201
|
+
devVarsElsewhere: 'Las variables de UN aparato se ponen en Dispositivos (tecla e).',
|
|
202
|
+
removeDevVarConfirm: (id, key) => `¿Quitar la variable ${key} de ${id}?`,
|
|
203
|
+
|
|
173
204
|
// emparejamiento — la PREGUNTA es del vault, que es quien lo inicia
|
|
174
205
|
pairModeIntro: 'Un dispositivo puede entrar a una cuenta que ya vive aquí, o estrenar una.',
|
|
175
206
|
pairModeHere: (name) => `Entrar a esta cuenta: ${name}`,
|
|
176
207
|
pairModeHereHint: 'el dispositivo pasa a ver y firmar lo de esta cuenta',
|
|
177
208
|
pairModeNew: 'Estrenar una cuenta nueva en este vault',
|
|
178
209
|
pairModeNewHint: 'se crea aquí, vacía, y el dispositivo entra a ELLA (las otras no se tocan)',
|
|
210
|
+
pairModeService: 'Conectar un servicio (proxy, geo…)',
|
|
211
|
+
pairModeServiceHint: 'entra a esta cuenta pero solo lee SUS variables: no firma ni ve tu contenido',
|
|
179
212
|
pairModeAdopt: 'Adoptar la cuenta que trae el dispositivo',
|
|
180
213
|
pairModeAdoptSoon: 'todavía no: el dispositivo aún no sabe entregar la suya',
|
|
214
|
+
serviceNsLabel: '¿Qué servicio es? (proxy, geo, results…)',
|
|
215
|
+
serviceNsHint: 'minúsculas, sin espacios; es el nombre con el que ese programa pide su configuración',
|
|
216
|
+
serviceNsBad: 'Nombre no válido: minúsculas, números y guiones (hasta 32)',
|
|
217
|
+
pairService: (ns) => `Este QR entrega un SERVICIO «${ns}»: solo podrá leer las variables de ${ns}.`,
|
|
181
218
|
newAccountLabel: 'Nombre de la cuenta nueva',
|
|
182
219
|
newAccountHint: 'nace vacía; el dispositivo será su primer invitado',
|
|
183
220
|
accountCreated: (name) => `Cuenta creada: ${name}`,
|
|
@@ -205,21 +242,29 @@ const es = {
|
|
|
205
242
|
// La barra dice lo que se PUEDE hacer AHORA, no todo lo que existe: aprobar/rechazar solo
|
|
206
243
|
// valen si hay alguien esperando, y renombrar/revocar solo si hay un aparato seleccionado.
|
|
207
244
|
// Anunciar teclas muertas confunde y además las quema para otros usos.
|
|
208
|
-
|
|
245
|
+
// `e variables` solo en un SERVICIO: es el único que las lee, así que en un teléfono
|
|
246
|
+
// era una tecla que solo sabía decir que no.
|
|
247
|
+
helpDevices: ({ pending, hasDevices, isService } = {}) => [
|
|
209
248
|
'←→ pestaña', '↑↓', 'p emparejar',
|
|
210
|
-
...(
|
|
211
|
-
...(
|
|
249
|
+
...(pending ? ['a aprobar', 'x rechazar'] : []),
|
|
250
|
+
...(hasDevices ? ['r renombrar', 'c permisos'] : []),
|
|
251
|
+
...(isService ? ['e variables'] : []),
|
|
252
|
+
...(hasDevices ? ['v revocar'] : []),
|
|
212
253
|
'F5 refrescar', 'Esc bóvedas', 'l English', 'q salir'
|
|
213
254
|
],
|
|
214
255
|
renameDeviceLabel: (id) => `¿Cómo quieres llamar a ${id}?`,
|
|
215
256
|
renameDeviceHint: 'el nombre con el que lo reconoces (Esc cancela)',
|
|
216
|
-
renaming: 'Renombrando…',
|
|
217
257
|
deviceRenamed: (n) => `Ahora se llama «${n}»`,
|
|
218
|
-
helpSecrets: ({
|
|
219
|
-
'←→ pestaña', '↑↓', 'n nueva variable',
|
|
220
|
-
...(
|
|
258
|
+
helpSecrets: ({ hasSecrets } = {}) => [
|
|
259
|
+
'←→ pestaña', '↑↓', 'n nueva variable', 'i cargar varias',
|
|
260
|
+
...(hasSecrets ? ['t pública/privada', 'x quitar (variable/scope)'] : []),
|
|
221
261
|
'F5 refrescar', 'Esc bóvedas', 'l English', 'q salir'
|
|
222
262
|
],
|
|
263
|
+
helpDevVars: ({ hasVars } = {}) => [
|
|
264
|
+
'↑↓', 'n nueva variable', 'i cargar varias',
|
|
265
|
+
...(hasVars ? ['t pública/privada', 'x quitar'] : []),
|
|
266
|
+
'F5 refrescar', 'Esc dispositivos', 'l English', 'q salir'
|
|
267
|
+
],
|
|
223
268
|
helpPairing: ['a aprobar', 'x rechazar', 'r reiniciar', '↑↓ scroll', 'Esc atrás', 'l English'],
|
|
224
269
|
helpPairMode: ['↑↓', 'Enter elegir', 'Esc atrás', 'l English', 'q salir'],
|
|
225
270
|
|
|
@@ -246,6 +291,13 @@ const es = {
|
|
|
246
291
|
errNotApplied: 'El daemon no aplicó el cambio (revisa los logs del servicio).',
|
|
247
292
|
errNotDeleted: 'El daemon no borró la variable (revisa los logs del servicio).',
|
|
248
293
|
errPairFailed: 'El daemon no inició el emparejamiento.',
|
|
294
|
+
// El código NO es la contraseña de la bóveda: son los seis dígitos que el aparato
|
|
295
|
+
// enseña en su pantalla. Si no coincide, la bóveda no firma nada y el aparato sigue
|
|
296
|
+
// esperando ahí, así que se puede volver a intentar con A.
|
|
297
|
+
errWrongCode: 'El código no coincide con el que muestra el dispositivo: no se emitió ningún certificado. Míralo otra vez y pulsa A.',
|
|
298
|
+
errProfileLocked: 'Bóveda bloqueada: ábrela con su contraseña',
|
|
299
|
+
errWrongPassword: (n) => `Contraseña incorrecta${n ? ` — van ${n} intentos fallidos` : ''}`,
|
|
300
|
+
errTooManyTries: (s) => `Demasiados intentos: espera ${s || '?'} s antes de volver a probar`,
|
|
249
301
|
errMasterWithMembers: 'Esta bóveda es el Master de esta cuenta y hay otros dispositivos: pásale primero el Master a uno que esté conectado. Si la borras así, se quedan con su llave y sin nadie que pueda volver a firmar el acta.'
|
|
250
302
|
}
|
|
251
303
|
|
|
@@ -260,7 +312,6 @@ const en = {
|
|
|
260
312
|
daemonRunning: 'running',
|
|
261
313
|
daemonStopped: 'STOPPED',
|
|
262
314
|
activeVault: 'Active vault: ',
|
|
263
|
-
noName: '(unnamed)',
|
|
264
315
|
tooSmall: 'Terminal too small',
|
|
265
316
|
tooSmallHint: (cols, rows) => `Resize to ≥ 24×9 (now ${cols}×${rows}).`,
|
|
266
317
|
|
|
@@ -268,7 +319,7 @@ const en = {
|
|
|
268
319
|
tabSecrets: 'Scopes & variables',
|
|
269
320
|
tabMe: 'Profile',
|
|
270
321
|
titleCaps: 'Device permissions',
|
|
271
|
-
capsFor: (id,
|
|
322
|
+
capsFor: (id, name) => `Permissions for ${id}${name ? ' · ' + name : ''}`,
|
|
272
323
|
capsNoMember: ' (this device is no longer in the record)',
|
|
273
324
|
capsApplyHint: 'Enter ticks or unticks. Each change applies and your other devices are told.',
|
|
274
325
|
capName: {
|
|
@@ -292,7 +343,7 @@ const en = {
|
|
|
292
343
|
loadingProfile: 'Loading profile…',
|
|
293
344
|
noProfile: ' (this vault has no profile yet)',
|
|
294
345
|
noProfileHint: ' Edit your name or photo on a paired device and press F5.',
|
|
295
|
-
profileUpdated: (
|
|
346
|
+
profileUpdated: (when) => ` updated ${when}`,
|
|
296
347
|
fieldName: 'name',
|
|
297
348
|
fieldPhoto: 'photo',
|
|
298
349
|
fieldFirstName: 'first name',
|
|
@@ -315,7 +366,7 @@ const en = {
|
|
|
315
366
|
locked: '🔒 locked',
|
|
316
367
|
unlocked: '🔓 unlocked',
|
|
317
368
|
passwordOf: (name) => `Password for "${name}"`,
|
|
318
|
-
passwordToEdit: 'needed to
|
|
369
|
+
passwordToEdit: 'needed to open this vault: without it, nothing is shown or touched',
|
|
319
370
|
unlocking: 'Unlocking…',
|
|
320
371
|
loading: 'Loading…',
|
|
321
372
|
loadingDevices: 'Loading devices…',
|
|
@@ -395,13 +446,48 @@ const en = {
|
|
|
395
446
|
savingVar: 'Saving variable…',
|
|
396
447
|
varSaved: (ns, key) => `Saved ${ns}/${key}`,
|
|
397
448
|
|
|
449
|
+
loadLabel: (ns) => `Load several into "${ns}"`,
|
|
450
|
+
loadHint: 'KEY=value KEY2=value2 · or the path to a .env file',
|
|
451
|
+
loadNoFile: (f) => `Could not read ${f}`,
|
|
452
|
+
loadingVars: 'Loading variables…',
|
|
453
|
+
loadedVars: (n, ns) => `${n} variables loaded into ${ns} · a single change notice`,
|
|
454
|
+
envErr: {
|
|
455
|
+
shape: (e) => `line ${e.line}: not in the form KEY=value`,
|
|
456
|
+
key: (e) => `line ${e.line}: "${e.key}" must be UPPERCASE_WITH_UNDERSCORES`,
|
|
457
|
+
novalue: (e) => `line ${e.line}: ${e.key} has no value`,
|
|
458
|
+
dup: (e) => `line ${e.line}: ${e.key} already came on line ${e.first}`,
|
|
459
|
+
empty: () => 'There is no variable to load'
|
|
460
|
+
},
|
|
461
|
+
loadNothing: 'Nothing was loaded:',
|
|
462
|
+
varPublic: 'public',
|
|
463
|
+
newVarPublicAsk: 'Let its value be SEEN from the remote console? (the rest never leave this machine)',
|
|
464
|
+
makePublicConfirm: 'Let its value be seen from the remote console?',
|
|
465
|
+
changingVisibility: 'Changing visibility…',
|
|
466
|
+
nowPublic: 'Its value can now be seen from the remote console',
|
|
467
|
+
nowPrivate: 'Its value no longer leaves this machine',
|
|
468
|
+
|
|
469
|
+
// variables de UN aparato (Dispositivos → tecla e)
|
|
470
|
+
titleDevVars: 'Device variables',
|
|
471
|
+
devVarsFor: (id, name) => `Variables of ${id}${name ? ' · ' + name : ''}`,
|
|
472
|
+
devVarsService: (cn) => `“${cn}” service — only this device reads them, and they override the scope ones`,
|
|
473
|
+
noDevVars: '(no variables of its own — press N to add the first one)',
|
|
474
|
+
devVarsOnlyServices: 'Only a service reads variables, and this device is not one',
|
|
475
|
+
devVarsElsewhere: 'Variables for ONE device are set in Devices (key e).',
|
|
476
|
+
removeDevVarConfirm: (id, key) => `Remove variable ${key} from ${id}?`,
|
|
477
|
+
|
|
398
478
|
pairModeIntro: 'A device can join an account that already lives here, or start a new one.',
|
|
399
479
|
pairModeHere: (name) => `Join this account: ${name}`,
|
|
400
480
|
pairModeHereHint: 'the device gets to see and sign for this account',
|
|
401
481
|
pairModeNew: 'Start a new account in this vault',
|
|
402
482
|
pairModeNewHint: 'created here, empty, and the device joins THAT one (the others are untouched)',
|
|
483
|
+
pairModeService: 'Connect a service (proxy, geo…)',
|
|
484
|
+
pairModeServiceHint: 'joins this account but only reads ITS variables: it neither signs nor sees your content',
|
|
403
485
|
pairModeAdopt: 'Adopt the account the device brings',
|
|
404
486
|
pairModeAdoptSoon: 'not yet: the device cannot hand its own over',
|
|
487
|
+
serviceNsLabel: 'Which service is it? (proxy, geo, results…)',
|
|
488
|
+
serviceNsHint: 'lowercase, no spaces; it is the name that program uses to ask for its settings',
|
|
489
|
+
serviceNsBad: 'Invalid name: lowercase letters, digits and dashes (up to 32)',
|
|
490
|
+
pairService: (ns) => `This QR hands out a SERVICE “${ns}”: it will only read ${ns}’s variables.`,
|
|
405
491
|
newAccountLabel: 'Name of the new account',
|
|
406
492
|
newAccountHint: 'born empty; the device will be its first guest',
|
|
407
493
|
accountCreated: (name) => `Account created: ${name}`,
|
|
@@ -419,21 +505,27 @@ const en = {
|
|
|
419
505
|
helpConfirm: 'y confirm · n/Esc cancel',
|
|
420
506
|
|
|
421
507
|
helpProfiles: ['↑↓', 'Enter open', 'p pair', 'n new', 'r rename', 'd delete', 'c password', 'x drop-password', 'u unlock', 'k lock', 'l Español', 'q quit'],
|
|
422
|
-
helpDevices: ({
|
|
508
|
+
helpDevices: ({ pending, hasDevices, isService } = {}) => [
|
|
423
509
|
'←→ tab', '↑↓', 'p pair',
|
|
424
|
-
...(
|
|
425
|
-
...(
|
|
510
|
+
...(pending ? ['a approve', 'x reject'] : []),
|
|
511
|
+
...(hasDevices ? ['r rename', 'c permissions'] : []),
|
|
512
|
+
...(isService ? ['e variables'] : []),
|
|
513
|
+
...(hasDevices ? ['v revoke'] : []),
|
|
426
514
|
'F5 refresh', 'Esc vaults', 'l Español', 'q quit'
|
|
427
515
|
],
|
|
428
516
|
renameDeviceLabel: (id) => `What do you want to call ${id}?`,
|
|
429
517
|
renameDeviceHint: 'the name you recognise it by (Esc cancels)',
|
|
430
|
-
renaming: 'Renaming…',
|
|
431
518
|
deviceRenamed: (n) => `Now called "${n}"`,
|
|
432
|
-
helpSecrets: ({
|
|
433
|
-
'←→ tab', '↑↓', 'n new variable',
|
|
434
|
-
...(
|
|
519
|
+
helpSecrets: ({ hasSecrets } = {}) => [
|
|
520
|
+
'←→ tab', '↑↓', 'n new variable', 'i load several',
|
|
521
|
+
...(hasSecrets ? ['t public/private', 'x remove (variable/scope)'] : []),
|
|
435
522
|
'F5 refresh', 'Esc vaults', 'l Español', 'q quit'
|
|
436
523
|
],
|
|
524
|
+
helpDevVars: ({ hasVars } = {}) => [
|
|
525
|
+
'↑↓', 'n new variable', 'i load several',
|
|
526
|
+
...(hasVars ? ['t public/private', 'x remove'] : []),
|
|
527
|
+
'F5 refresh', 'Esc devices', 'l Español', 'q quit'
|
|
528
|
+
],
|
|
437
529
|
helpPairing: ['a approve', 'x reject', 'r restart', '↑↓ scroll', 'Esc back', 'l Español'],
|
|
438
530
|
helpPairMode: ['↑↓', 'Enter choose', 'Esc back', 'l Español', 'q quit'],
|
|
439
531
|
|
|
@@ -458,6 +550,10 @@ const en = {
|
|
|
458
550
|
errNotApplied: 'The daemon did not apply the change (check the service logs).',
|
|
459
551
|
errNotDeleted: 'The daemon did not delete the variable (check the service logs).',
|
|
460
552
|
errPairFailed: 'The daemon did not start the pairing.',
|
|
553
|
+
errWrongCode: 'The code does not match the one shown by the device: no certificate was issued. Check it and press A again.',
|
|
554
|
+
errProfileLocked: 'Vault locked: open it with its password',
|
|
555
|
+
errWrongPassword: (n) => `Wrong password${n ? ` — ${n} failed attempts so far` : ''}`,
|
|
556
|
+
errTooManyTries: (s) => `Too many attempts: wait ${s || '?'} s before trying again`,
|
|
461
557
|
errMasterWithMembers: 'This vault is the Master of this account and it has other devices: hand the Master over to one that is online first. If you delete it like this, they keep their key with nobody able to sign the record again.'
|
|
462
558
|
}
|
|
463
559
|
|