@dotrino/vaultd 0.7.2 → 0.7.4

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 CHANGED
@@ -103,9 +103,12 @@ los dispositivos/variables que estás viendo:
103
103
  **entrar** a uno — lo activa si no lo estaba). Ahí también creas una bóveda
104
104
  nueva, renombras, borras y pones/quitas/usas la contraseña (candado).
105
105
  2. Al entrar caes en sus **pestañas horizontales**, que cambias con `←→`:
106
- - **Dispositivos (pares):** verlos, **emparejar** uno nuevo (muestra el QR y la
107
- URL, y espera a que se conecte), **aprobar** con el código que muestra el
108
- dispositivo, **rechazar** y **revocar**.
106
+ - **Dispositivos (pares):** verlos, **emparejar** uno nuevo, **aprobar** con el
107
+ código que muestra el dispositivo, **rechazar** y **revocar**. Al emparejar, la
108
+ bóveda **pregunta primero a qué cuenta entra el dispositivo** —a esta, o a una
109
+ cuenta nueva que se estrena para él— y recién después muestra el QR, que además
110
+ dice de qué cuenta salió. (En la CLI: `dotrino-vault pair --new-account
111
+ [nombre]`.)
109
112
  - **Scopes y variables (secretos):** ver los scopes y sus variables (nunca los
110
113
  valores), **agregar** una variable (con su scope) y **quitar** una variable o
111
114
  un scope entero.
@@ -114,6 +117,31 @@ los dispositivos/variables que estás viendo:
114
117
  sale desde cualquier pantalla. Las teclas de cada acción se listan en la barra
115
118
  inferior.
116
119
 
120
+ **Idioma (`l`).** La TUI está en **español e inglés** y la tecla `l` conmuta entre
121
+ los dos en cualquier pantalla (incluida la de "el daemon no está corriendo"). El
122
+ idioma elegido se recuerda en `prefs.json` del dir de datos. Sin elección previa
123
+ se toma del entorno (`DOTRINO_LANG`, o el locale `LC_ALL`/`LANG`), con el español
124
+ por defecto.
125
+
126
+ **Las teclas NO cambian con el idioma**: son mnemónicos en **inglés** y valen igual
127
+ en español (solo se traduce la palabra que las explica en la barra de ayuda).
128
+
129
+ | Tecla | Acción | Dónde |
130
+ |---|---|---|
131
+ | `Enter` | open — entrar a la bóveda | Bóvedas |
132
+ | `n` | new — bóveda nueva / variable nueva | Bóvedas · Scopes |
133
+ | `r` | rename (Bóvedas) · refresh (Dispositivos/Scopes) · restart (Emparejar) | — |
134
+ | `d` | delete — borrar la bóveda | Bóvedas |
135
+ | `p` | password (Bóvedas) · pair — emparejar (Dispositivos) | — |
136
+ | `x` | quitar: contraseña · dispositivo pendiente · variable/scope | todas |
137
+ | `u` / `k` | unlock / locK — candado de la bóveda | Bóvedas |
138
+ | `a` | approve — aprobar el dispositivo | Dispositivos · Emparejar |
139
+ | `v` | reVoke — revocar un dispositivo enrolado | Dispositivos |
140
+ | `y` | yes — confirmar (también se acepta `s`) | confirmaciones |
141
+ | `b` / `Esc` | back — volver | pestañas · Emparejar |
142
+ | `l` | language — español ⇄ English | todas |
143
+ | `q` | quit — salir | todas |
144
+
117
145
  ### Varios perfiles en el mismo PC
118
146
 
119
147
  Puedes tener varias identidades tuyas en la misma máquina (p. ej. personal y
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/vaultd",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
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
@@ -136,7 +136,7 @@ function showChallenge (pe) {
136
136
  console.log(' dispositivo : %s%s%s', B, pe.deviceId, Z)
137
137
  console.log('\n Ingresa el código que MUESTRA el dispositivo (el vault no lo conoce):')
138
138
  console.log(' %sdotrino-vault approve <código>%s', B, Z)
139
- console.log(' Si no reconocés este dispositivo: dotrino-vault reject %s\n', pe.deviceId)
139
+ console.log(' Si no reconoces este dispositivo: dotrino-vault reject %s\n', pe.deviceId)
140
140
  }
141
141
 
142
142
  async function cmdPair (args = []) {
@@ -144,7 +144,7 @@ async function cmdPair (args = []) {
144
144
  try { fs.rmSync(pairFile, { force: true }) } catch (_) {}
145
145
  try { fs.rmSync(pendingFile, { force: true }) } catch (_) {}
146
146
  // --service <ns>: emparejar un SERVICIO (proxy, geo…) con cert limitado a
147
- // vault:secrets:<ns> (no puede firmar como vos ni leer tus datos).
147
+ // vault:secrets:<ns> (no puede firmar como ni leer tus datos).
148
148
  const svcIdx = args.indexOf('--service')
149
149
  let service = null
150
150
  if (svcIdx >= 0) {
@@ -153,6 +153,20 @@ async function cmdPair (args = []) {
153
153
  console.error('uso: dotrino-vault pair --service <ns> (ns en minúsculas, p.ej. proxy)'); process.exit(2)
154
154
  }
155
155
  }
156
+ // `--new-account [nombre]`: la otra respuesta a «¿a qué cuenta entra?». En vez de
157
+ // meter el dispositivo en una cuenta que ya vive aquí, se ESTRENA una (vacía) y
158
+ // entra a ella; las demás no se tocan. En la TUI esto es una pregunta con sus
159
+ // opciones; en la CLI es una bandera, para que siga sirviendo en un script.
160
+ const naIdx = args.findIndex((a) => a === '--new-account')
161
+ if (naIdx >= 0) {
162
+ const next = args[naIdx + 1]
163
+ const name = (next && !next.startsWith('-')) ? next : `cuenta ${new Date().toISOString().slice(0, 10)}`
164
+ const d = await profileRequest('add', { name })
165
+ if (d.error) { console.error('%s', d.error); process.exit(1) }
166
+ if (!d.id) { console.error('El daemon no dijo qué cuenta creó.'); process.exit(1) }
167
+ PROFILE = d.id // el emparejamiento y los comandos siguientes apuntan a ELLA
168
+ console.log('Cuenta nueva: %s (%s)', name, d.id)
169
+ }
156
170
  // La petición se escribe SIEMPRE (aunque no haya --service): lleva a qué perfil
157
171
  // se empareja el dispositivo.
158
172
  writeReq('pair-request.json', service ? { service } : {})
@@ -166,11 +180,16 @@ async function cmdPair (args = []) {
166
180
  const b64 = Buffer.from(payload, 'utf8').toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
167
181
  const url = PROFILE_URL + b64
168
182
  const mins = Math.round((pair.expiresAt - Date.now()) / 60000)
169
- console.log('\nEscaneá este QR con el dispositivo que querés conectar (válido %d min):\n', mins)
183
+ // QUÉ CUENTA se comparte: el vault puede tener varias bóvedas y este QR sale de
184
+ // UNA (la activa, o la de --profile). Decirlo evita enrolar el dispositivo en la
185
+ // equivocada; es la misma línea que muestra la TUI.
186
+ const acct = pair.profileName || pair.profile
187
+ if (acct) console.log('\nCuenta que se comparte: %s%s', acct, pair.profileName && pair.profile ? ` (${pair.profile})` : '')
188
+ console.log('\nEscanea este QR con el dispositivo que quieres conectar (válido %d min):\n', mins)
170
189
  console.log(qrToString(url)) // el QR abre vault.dotrino.com/dispositivos y empareja solo
171
190
  console.log(`${R}${B}⚠ Este código deja LEER tus datos y FIRMAR con tu identidad.${Z}`)
172
- console.log(`${R} NO lo compartas con nadie, ni con "soporte". Solo escaneálo en TU dispositivo.${Z}`)
173
- console.log('\nO abrí esta dirección en el dispositivo:\n ' + url)
191
+ console.log(`${R} NO lo compartas con nadie, ni con "soporte". Solo escanéalo en TU dispositivo.${Z}`)
192
+ console.log('\nO abre esta dirección en el dispositivo:\n ' + url)
174
193
  console.log('\nO pega este código en vault.dotrino.com/dispositivos :\n ' + payload)
175
194
 
176
195
  // --save [archivo]: escribe la invitación (.dpair) para transferirla y abrirla en profile.
@@ -534,6 +553,9 @@ function help () {
534
553
  tui interfaz de terminal a pantalla completa (bóvedas, pares, secretos)
535
554
  status estado del servicio + fingerprint
536
555
  pair [--save <f>] inicia un emparejamiento (QR + espera); --save escribe la invitación (.dpair)
556
+ pair --new-account [nombre]
557
+ estrena una cuenta VACÍA en este vault y mete ahí al dispositivo
558
+ (sin la bandera entra a la cuenta activa, o a la de --profile)
537
559
  pair --service <ns> empareja un SERVICIO (proxy, geo…) con acceso SOLO a sus secretos
538
560
  secret set <ns> <CLAVE> <valor> guarda un secreto para el servicio <ns>
539
561
  secret rm <ns> <CLAVE> borra un secreto
package/src/daemon.js CHANGED
@@ -118,7 +118,11 @@ export async function runDaemon () {
118
118
  const scope = isService ? ['vault:secrets:' + pairReq.service] : ['vault:sign', 'vault:read', 'vault:store']
119
119
  const label = pairReq?.label || (isService ? 'servicio:' + pairReq.service : 'cli')
120
120
  const { qr, expiresInMs } = vault.startPairing({ scope, label, ttlMs: DEVICE_TTL_MS })
121
- writeJson(pairFile, { v: 2, qr, expiresAt: Date.now() + expiresInMs, profile: profileId })
121
+ // `profile`/`profileName`: la CUENTA del vault a la que entra el dispositivo.
122
+ // Con varias bóvedas en el mismo daemon, el QR sale de UNA y quien empareja
123
+ // tiene que verlo (lo muestran la TUI y `dotrino-vault pair`).
124
+ const profileName = mgr.profiles.get(profileId)?.name || ''
125
+ writeJson(pairFile, { v: 2, qr, expiresAt: Date.now() + expiresInMs, profile: profileId, profileName })
122
126
  // El token es un secreto efímero: no debe quedar en disco más allá de su
123
127
  // vida. Se borra al VENCER (aquí) y al APROBARSE (abajo, consumido).
124
128
  const tok = qr.token
@@ -152,7 +156,9 @@ export async function runDaemon () {
152
156
  const ref = () => mgr.resolve(req.profile || mgr.currentId())
153
157
  switch (req.op) {
154
158
  case 'list': return {} // el volcado de perfiles ya se hace abajo
155
- case 'add': { const p = await mgr.add(req.name); return { done: `perfil creado: ${p.name || p.id}` } }
159
+ // `id`: quien la crea necesita saber CUÁL quedó, no adivinar por nombre (dos
160
+ // cuentas pueden llamarse igual). Lo usa «emparejar en una cuenta nueva».
161
+ case 'add': { const p = await mgr.add(req.name); return { done: `perfil creado: ${p.name || p.id}`, id: p.id } }
156
162
  case 'rm': { const r = await mgr.remove(req.profile); return { done: `perfil borrado: ${r.name || r.id}` } }
157
163
  case 'rename': { const p = mgr.profiles.rename(ref(), req.name); return { done: `perfil renombrado: ${p.name}` } }
158
164
  case 'use': { const p = mgr.profiles.setCurrent(ref()); return { done: `perfil activo: ${p.name || p.id}` } }