@dotrino/vault 0.92.0 → 0.93.1

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/src/enroll.js CHANGED
@@ -272,7 +272,7 @@ export function createEnrollDesk ({
272
272
  return { ignored: true }
273
273
  }
274
274
  if (!pend || Date.now() > pend.exp) {
275
- audit('rejected', { what: 'hello', reason: 'sin-sesion' })
275
+ audit('rejected', { what: 'hello', reason: 'no-session' })
276
276
  return fail(from, 'no pairing session open for that code', String(p?.sn || ''))
277
277
  }
278
278
  // La respuesta va FIRMADA por la maestra y el `sn` va dentro de lo firmado. Eso ata
package/src/env.js CHANGED
@@ -64,14 +64,14 @@ export function serviceDir (vault, ns) {
64
64
  // Con dos, en cambio, elegir por el usuario sería adivinar cuál de sus cuentas usar — así
65
65
  // que se para y se dice cuáles hay. Eso es lo contrario de un repliegue: no se resuelve
66
66
  // una ambigüedad a la brava, se señala.
67
- const donde = listEnrolled().filter((e) => !e.legacy && e.ns === ns)
68
- if (donde.length === 1) return path.join(serviceRoot(), donde[0].vault, ns)
69
- if (donde.length > 1) {
70
- throw new Error(`"${ns}" is enrolled in more than one vault (${donde.map((e) => e.vault).join(', ')}): pick one with --vault <label>`)
67
+ const where = listEnrolled().filter((e) => !e.legacy && e.ns === ns)
68
+ if (where.length === 1) return path.join(serviceRoot(), where[0].vault, ns)
69
+ if (where.length > 1) {
70
+ throw new Error(`"${ns}" is enrolled in more than one vault (${where.map((e) => e.vault).join(', ')}): pick one with --vault <label>`)
71
71
  }
72
- const otros = listEnrolled().filter((e) => !e.legacy)
73
- throw new Error(otros.length
74
- ? `"${ns}" is not enrolled on this machine (there is: ${otros.map((e) => `${e.vault}/${e.ns}`).join(', ')})`
72
+ const others = listEnrolled().filter((e) => !e.legacy)
73
+ throw new Error(others.length
74
+ ? `"${ns}" is not enrolled on this machine (there is: ${others.map((e) => `${e.vault}/${e.ns}`).join(', ')})`
75
75
  : `"${ns}" is not enrolled on this machine: run \`dotrino-env enroll --vault <label> --ns ${ns}\` first`)
76
76
  }
77
77
 
@@ -94,9 +94,9 @@ export function listEnrolled () {
94
94
  // Un enrolamiento del formato viejo es un directorio con `service-identity.json`
95
95
  // DENTRO; uno del nuevo tiene subdirectorios. Así se distinguen sin adivinar.
96
96
  if (readServiceIdentity(path.join(serviceRoot(), d.name))) { out.push({ legacy: true, ns: d.name }); continue }
97
- let hijos = []
98
- try { hijos = fs.readdirSync(path.join(serviceRoot(), d.name)) } catch (_) { continue }
99
- for (const ns of hijos) {
97
+ let children = []
98
+ try { children = fs.readdirSync(path.join(serviceRoot(), d.name)) } catch (_) { continue }
99
+ for (const ns of children) {
100
100
  if (isValidSecretsNs(ns) && readServiceIdentity(path.join(serviceRoot(), d.name, ns))) out.push({ vault: d.name, ns })
101
101
  }
102
102
  }
package/src/index.js CHANGED
@@ -72,7 +72,7 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
72
72
  * Sin acta van nulos y `verifyDelegation` contesta `no-acta`: no hay con qué decidir, así
73
73
  * que no se decide que sí.
74
74
  */
75
- async function contextoActa () {
75
+ async function actaContext () {
76
76
  const acta = (await identity.profileActa?.().catch(() => null))?.acta || null
77
77
  if (!acta) return { actaSeq: null, sealers: null }
78
78
  return { actaSeq: acta.seq, sealers: sealersOf(acta) }
@@ -81,7 +81,7 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
81
81
  let _selfCert = null
82
82
  const getSelfCert = async () => {
83
83
  // Se rehace cuando el acta cambia, no cuando pasa el tiempo: el papel ya no caduca.
84
- const { actaSeq } = await contextoActa()
84
+ const { actaSeq } = await actaContext()
85
85
  if (_selfCert && _selfCert.seq === actaSeq) return _selfCert
86
86
  const { cert } = await identity.signDelegation(iss, SIGN_SCOPE)
87
87
  _selfCert = cert
@@ -187,7 +187,7 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
187
187
  if (typeof d.ts !== 'number' || Math.abs(Date.now() - d.ts) > FRESH_WINDOW_MS) {
188
188
  return send(from, { type: MSG.ERROR, error: 'stale request: ts outside the ±5 min window (possible replay, or the device clock is off)' })
189
189
  }
190
- const chk = await verifyChain({ data: d, signature: p.signature, cert: p.cert, ...(await contextoActa()), revoked: await revocationSet() })
190
+ const chk = await verifyChain({ data: d, signature: p.signature, cert: p.cert, ...(await actaContext()), revoked: await revocationSet() })
191
191
  if (!chk.ok) return send(from, { type: MSG.ERROR, error: 'unauthorized: ' + chk.reason })
192
192
  // Reusar el label del cert original (si sigue registrado en delegations).
193
193
  const { issued } = await identity.listDelegations()
@@ -210,7 +210,7 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
210
210
  const d = p?.data
211
211
  if (!d || !p.signature || !p.cert) return send(from, { type: MSG.ERROR, error: 'invalid request' })
212
212
  if (typeof d.ts !== 'number' || Math.abs(Date.now() - d.ts) > FRESH_WINDOW_MS) return
213
- const chk = await verifyChain({ data: d, signature: p.signature, cert: p.cert, ...(await contextoActa()) })
213
+ const chk = await verifyChain({ data: d, signature: p.signature, cert: p.cert, ...(await actaContext()) })
214
214
  if (!chk.ok) return send(from, { type: MSG.ERROR, error: 'unauthorized: ' + chk.reason })
215
215
  const { issued, revoked, revokedCerts } = await identity.listDelegations()
216
216
  const devices = await Promise.all((issued || []).map(async (x) => ({
@@ -248,7 +248,7 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
248
248
  const chk = await verifyChain({
249
249
  data: d, signature: p.signature, cert: p.cert,
250
250
  ...(expectedScope ? { expectedScope } : {}),
251
- ...(await contextoActa()), revoked: await revocationSet(),
251
+ ...(await actaContext()), revoked: await revocationSet(),
252
252
  })
253
253
  if (!chk.ok) {
254
254
  send(from, { type: MSG.ERROR, error: 'unauthorized: ' + chk.reason })
@@ -261,7 +261,7 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
261
261
  if (expectedScope) {
262
262
  const record = (await identity.profileActa?.().catch(() => null))?.acta || null
263
263
  if (record && !memberCanScope(record, chk.device, expectedScope)) {
264
- send(from, { type: MSG.ERROR, error: 'unauthorized: acta — this member no longer has that permission' })
264
+ send(from, { type: MSG.ERROR, error: 'unauthorized: record — this member no longer has that permission' })
265
265
  return null
266
266
  }
267
267
  }
@@ -356,7 +356,7 @@ export async function startDeviceVault (identity, { proxyUrl, client: injectedCl
356
356
  if (p.type === MSG.HELLO) handle('hello', desk.handleHello(_from, p), _from)
357
357
  else if (p.type === MSG.ENROLL) handle('enroll', desk.handleEnroll(_from, p), _from)
358
358
  // Camino A: el aparato devuelve su acta sellada admitiendo a esta bóveda.
359
- else if (p.type === MSG.ACTA_SEALED) handle('acta', desk.handleActaSealed(_from, p), _from)
359
+ else if (p.type === MSG.ACTA_SEALED) handle('record-sealed', desk.handleActaSealed(_from, p), _from)
360
360
  else if (p.type === MSG.RENEW) handle('renew', handleRenew(_from, p), _from)
361
361
  else if (p.type === MSG.DEVICES) handle('devices', handleDevices(_from, p), _from)
362
362
  // Lo que faltaba para que un aparato enrolado aquí pueda hacer lo mismo que contra
package/src/kek.js CHANGED
@@ -182,21 +182,21 @@ function commandKey (dir, cfg, magic) {
182
182
  */
183
183
  export function assertSameMachine (dir, material, hasData) {
184
184
  const f = path.join(dir, MACHINE_FILE)
185
- const huella = crypto.createHash('sha256').update('dotrino-atrest-machine\u0000' + material).digest('hex').slice(0, 32)
186
- let previa = null
187
- try { previa = fs.readFileSync(f, 'utf8').trim() } catch (_) {}
185
+ const fingerprint = crypto.createHash('sha256').update('dotrino-atrest-machine\u0000' + material).digest('hex').slice(0, 32)
186
+ let previous = null
187
+ try { previous = fs.readFileSync(f, 'utf8').trim() } catch (_) {}
188
188
 
189
- if (previa && previa !== huella && hasData) {
189
+ if (previous && previous !== fingerprint && hasData) {
190
190
  throw new KekError('kek-machine-changed',
191
191
  'this data was written by a DIFFERENT machine and the key is derived from the machine itself, so it cannot be opened here. ' +
192
192
  'In a container this is usually the container id changing on every run: the fix is to stop deriving the key from the machine — ' +
193
193
  'give the profile a KMS provider (see docs/llaves-de-hardware.md) and re-create it, or restore this volume on the original host. ' +
194
194
  'Nothing was modified.')
195
195
  }
196
- if (!previa) {
196
+ if (!previous) {
197
197
  try {
198
198
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 })
199
- fs.writeFileSync(f, huella + '\n', { mode: 0o600 })
199
+ fs.writeFileSync(f, fingerprint + '\n', { mode: 0o600 })
200
200
  } catch (_) { /* si no se puede escribir la huella, no se bloquea el arranque */ }
201
201
  }
202
202
  }
package/src/localdesk.js CHANGED
@@ -60,28 +60,28 @@ export async function connectLocal ({ masterId, timeoutMs = 3000 } = {}) {
60
60
  s.once('error', (e) => { clearTimeout(t); reject(new Error('no local vault desk at ' + socketPath + ': ' + e.message)) })
61
61
  })
62
62
 
63
- const oyentes = new Set()
63
+ const listeners = new Set()
64
64
  let buf = ''
65
65
  sock.setEncoding('utf8')
66
66
  sock.on('data', (chunk) => {
67
67
  buf += chunk
68
68
  let i
69
69
  while ((i = buf.indexOf('\n')) >= 0) {
70
- const linea = buf.slice(0, i); buf = buf.slice(i + 1)
71
- if (!linea.trim()) continue
70
+ const line = buf.slice(0, i); buf = buf.slice(i + 1)
71
+ if (!line.trim()) continue
72
72
  let p = null
73
- try { p = JSON.parse(linea) } catch (_) { continue }
73
+ try { p = JSON.parse(line) } catch (_) { continue }
74
74
  // `from` es el mismo para todo: al otro lado solo hay una bóveda.
75
- for (const fn of oyentes) { try { fn('local', p) } catch (_) {} }
75
+ for (const fn of listeners) { try { fn('local', p) } catch (_) {} }
76
76
  }
77
77
  })
78
78
 
79
- const enviar = (obj) => { sock.write(JSON.stringify(obj) + '\n') }
79
+ const send = (obj) => { sock.write(JSON.stringify(obj) + '\n') }
80
80
  return {
81
81
  local: true,
82
82
  token: 'local',
83
- send: (_to, obj) => enviar(obj),
84
- sendByPubkey: (_pub, obj) => enviar(obj),
83
+ send: (_to, obj) => send(obj),
84
+ sendByPubkey: (_pub, obj) => send(obj),
85
85
  // El mostrador local no pide identificarse: la puerta la abre el certificado que va
86
86
  // dentro de cada mensaje, igual que por el proxio. Identificarse ahí sirve para ser
87
87
  // DIRECCIONABLE, y aquí no hay a quién direccionar.
@@ -96,8 +96,8 @@ export async function connectLocal ({ masterId, timeoutMs = 3000 } = {}) {
96
96
  identifyAs: async () => ({ ok: true }),
97
97
  on: (ev, fn) => {
98
98
  if (ev !== 'message') return () => {}
99
- oyentes.add(fn)
100
- return () => oyentes.delete(fn)
99
+ listeners.add(fn)
100
+ return () => listeners.delete(fn)
101
101
  },
102
102
  close: () => { try { sock.destroy() } catch (_) {} }
103
103
  }
package/src/lock.js CHANGED
@@ -42,7 +42,7 @@ export const HEARTBEAT_MS = 10_000
42
42
  * carga o un disco de red lento no pueden costar que otro le quite el candado a uno vivo. */
43
43
  export const STALE_MS = 60_000
44
44
 
45
- const quienSoy = () => ({ pid: process.pid, host: os.hostname(), desde: Date.now() })
45
+ const whoAmI = () => ({ pid: process.pid, host: os.hostname(), desde: Date.now() })
46
46
 
47
47
  /**
48
48
  * Toma el candado del directorio, o explica quién lo tiene y se rinde.
@@ -52,12 +52,12 @@ export function takeLock (dir, { staleMs = STALE_MS, heartbeatMs = HEARTBEAT_MS
52
52
  const f = path.join(dir, LOCK_FILE)
53
53
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 })
54
54
 
55
- const intentar = () => {
55
+ const attemptOnce = () => {
56
56
  try {
57
57
  // 'wx' = O_CREAT|O_EXCL: o lo creo yo, o alguien lo tiene. Esa atomicidad es lo
58
58
  // único que aquí hace de exclusión.
59
59
  const fd = fs.openSync(f, 'wx', 0o600)
60
- fs.writeSync(fd, JSON.stringify(quienSoy()))
60
+ fs.writeSync(fd, JSON.stringify(whoAmI()))
61
61
  fs.closeSync(fd)
62
62
  return true
63
63
  } catch (e) {
@@ -66,44 +66,44 @@ export function takeLock (dir, { staleMs = STALE_MS, heartbeatMs = HEARTBEAT_MS
66
66
  }
67
67
  }
68
68
 
69
- if (!intentar()) {
70
- const edad = (() => {
69
+ if (!attemptOnce()) {
70
+ const age = (() => {
71
71
  try { return Date.now() - fs.statSync(f).mtimeMs } catch (_) { return Infinity }
72
72
  })()
73
- let dueño = null
74
- try { dueño = JSON.parse(fs.readFileSync(f, 'utf8')) } catch (_) {}
73
+ let owner = null
74
+ try { owner = JSON.parse(fs.readFileSync(f, 'utf8')) } catch (_) {}
75
75
 
76
- if (edad <= staleMs) {
76
+ if (age <= staleMs) {
77
77
  const e = new Error(
78
- `another vault is running on this data (host ${dueño?.host || '?'}, process ${dueño?.pid ?? '?'}, ` +
79
- `last heartbeat ${Math.round(edad / 1000)}s ago)`
78
+ `another vault is running on this data (host ${owner?.host || '?'}, process ${owner?.pid ?? '?'}, ` +
79
+ `last heartbeat ${Math.round(age / 1000)}s ago)`
80
80
  )
81
81
  e.code = 'vault-locked'
82
- e.owner = dueño
82
+ e.owner = owner
83
83
  throw e
84
84
  }
85
85
  // Abandonado: se quita y se vuelve a intentar UNA vez. Si en ese hueco entró otro,
86
86
  // pierde este — que es lo correcto: dos que ven el candado viejo a la vez, solo uno
87
87
  // gana el `O_EXCL`.
88
88
  try { fs.unlinkSync(f) } catch (_) {}
89
- if (!intentar()) {
89
+ if (!attemptOnce()) {
90
90
  const e = new Error('another vault took this data first')
91
91
  e.code = 'vault-locked'
92
92
  throw e
93
93
  }
94
94
  }
95
95
 
96
- const latido = setInterval(() => {
96
+ const heartbeat = setInterval(() => {
97
97
  // `utimes` y no reescribir: tocar la fecha basta y no puede dejar el archivo a medias.
98
98
  try { fs.utimesSync(f, new Date(), new Date()) } catch (_) {}
99
99
  }, heartbeatMs)
100
- latido.unref?.()
100
+ heartbeat.unref?.()
101
101
 
102
- let suelto = false
102
+ let loose = false
103
103
  const release = () => {
104
- if (suelto) return
105
- suelto = true
106
- clearInterval(latido)
104
+ if (loose) return
105
+ loose = true
106
+ clearInterval(heartbeat)
107
107
  // Solo se borra SI SIGUE SIENDO MÍO. Si otro me lo quitó por viejo, borrarlo aquí le
108
108
  // quitaría a él un candado que sí es suyo.
109
109
  try {
@@ -54,18 +54,18 @@ export const WAKE_WAIT_MS = 30_000
54
54
  */
55
55
  function waitFor (transport, from, match, timeoutMs) {
56
56
  return new Promise((resolve, reject) => {
57
- let listo = false
58
- const fin = (fn, arg) => { if (!listo) { listo = true; clearTimeout(reloj); quitar(); fn(arg) } }
59
- const reloj = setTimeout(() => fin(reject, err('no-answer', 'the vault did not answer in time')), timeoutMs)
60
- const oyente = (quien, payload) => {
61
- if (from && quien !== from) return
57
+ let ready = false
58
+ const fin = (fn, arg) => { if (!ready) { ready = true; clearTimeout(clock); remove(); fn(arg) } }
59
+ const clock = setTimeout(() => fin(reject, err('no-answer', 'the vault did not answer in time')), timeoutMs)
60
+ const listener = (who, payload) => {
61
+ if (from && who !== from) return
62
62
  const p = typeof payload === 'string' ? parseJson(payload) : payload
63
63
  if (p && match(p)) fin(resolve, p)
64
64
  }
65
- const off = transport.on('message', oyente)
66
- const quitar = () => {
65
+ const off = transport.on('message', listener)
66
+ const remove = () => {
67
67
  if (typeof off === 'function') return off()
68
- transport.off?.('message', oyente)
68
+ transport.off?.('message', listener)
69
69
  }
70
70
  })
71
71
  }
@@ -103,8 +103,8 @@ export async function loginWithPassword ({ transport, address, password, label =
103
103
  let tokens = await transport.list(vaultChannel(code))
104
104
  // UNA BÓVEDA QUE DUERME (la de una Lambda): el proxio la está despertando y lo dice. Se
105
105
  // espera a que se anuncie en vez de contestar que esa cuenta no existe.
106
- const hasta = Date.now() + WAKE_WAIT_MS
107
- while (!tokens.length && tokens.waking && Date.now() < hasta) {
106
+ const until = Date.now() + WAKE_WAIT_MS
107
+ while (!tokens.length && tokens.waking && Date.now() < until) {
108
108
  await new Promise((resolve) => setTimeout(resolve, 2000))
109
109
  tokens = await transport.list(vaultChannel(code))
110
110
  }
@@ -115,25 +115,25 @@ export async function loginWithPassword ({ transport, address, password, label =
115
115
  // De una en una, y parando en cuanto la contraseña resulte estar mal: cada intento GASTA
116
116
  // uno del freno en la bóveda que lo atiende (`passwordLogins.js`), así que repartir el
117
117
  // mismo error entre las réplicas solo sirve para bloquearse en todas a la vez.
118
- let ultimo = null
118
+ let last = null
119
119
  for (const token of tokens) {
120
120
  try {
121
- return await unIntento({ transport, token, user, code, dir, password, label, timeoutMs, opaque })
121
+ return await oneAttempt({ transport, token, user, code, dir, password, label, timeoutMs, opaque })
122
122
  } catch (e) {
123
123
  // Se para: son cosas del que entra, y probar con otra bóveda no las arregla.
124
124
  if (e?.code === 'login-failed' || e?.code === 'too-many-tries' || e?.code === 'wrong-account' ||
125
125
  e?.code === 'bad-blob' || e?.code === 'bad-keys' || e?.code === 'bad-reply') throw e
126
- ultimo = e // «esa no atiende inicios de sesión», «no contestó»: quizá la siguiente sí
126
+ last = e // «esa no atiende inicios de sesión», «no contestó»: quizá la siguiente sí
127
127
  }
128
128
  }
129
- throw ultimo || err('no-vault', 'none of the vaults on that address could let you in')
129
+ throw last || err('no-vault', 'none of the vaults on that address could let you in')
130
130
  }
131
131
 
132
- async function unIntento ({ transport, token, user, code, dir, password, label, timeoutMs, opaque }) {
132
+ async function oneAttempt ({ transport, token, user, code, dir, password, label, timeoutMs, opaque }) {
133
133
  const start = await opaque.loginStart({ password })
134
134
  transport.send(token, { type: MSG.LOGIN_START, user, request: start.request })
135
135
  const r1 = await waitFor(transport, token, (p) => p.type === MSG.LOGIN_RESPONSE || p.type === MSG.ERROR, timeoutMs)
136
- if (r1.type === MSG.ERROR) throw deVuelta(r1)
136
+ if (r1.type === MSG.ERROR) throw back(r1)
137
137
 
138
138
  let fin
139
139
  try {
@@ -148,7 +148,7 @@ async function unIntento ({ transport, token, user, code, dir, password, label,
148
148
 
149
149
  transport.send(token, { type: MSG.LOGIN_FINISH, lid: r1.lid, finalization: fin.finalization, label })
150
150
  const ok = await waitFor(transport, token, (p) => p.type === MSG.LOGIN_OK || p.type === MSG.ERROR, timeoutMs)
151
- if (ok.type === MSG.ERROR) throw deVuelta(ok)
151
+ if (ok.type === MSG.ERROR) throw back(ok)
152
152
 
153
153
  const acta = ok.acta
154
154
  const cert = ok.cert
@@ -168,9 +168,9 @@ async function unIntento ({ transport, token, user, code, dir, password, label,
168
168
  if (!keys?.sign) throw err('bad-blob', 'the key package does not carry a signing key')
169
169
 
170
170
  // 4. La llave que salió del paquete tiene que SER la del papel. Se prueba firmando.
171
- const reto = { op: 'login.proof', sub: cert.sub, sid: ok.sid, ts: Date.now() }
172
- const { signature } = await signWithDevice({ privateJwk: keys.sign, data: reto })
173
- if (!(await verifyDeviceSig({ publickey: cert.sub, data: reto, signature }))) {
171
+ const challenge = { op: 'login.proof', sub: cert.sub, sid: ok.sid, ts: Date.now() }
172
+ const { signature } = await signWithDevice({ privateJwk: keys.sign, data: challenge })
173
+ if (!(await verifyDeviceSig({ publickey: cert.sub, data: challenge, signature }))) {
174
174
  throw err('bad-keys', 'the keys in the package are not the ones this certificate is for')
175
175
  }
176
176
 
@@ -196,7 +196,7 @@ async function unIntento ({ transport, token, user, code, dir, password, label,
196
196
  }
197
197
 
198
198
  /** Un error de la bóveda con su `code` intacto: `too-many-tries` se arregla esperando. */
199
- function deVuelta (p) {
199
+ function back (p) {
200
200
  const code = p.code || 'login-error'
201
201
  const e = err(code, p.error || 'the vault refused the login')
202
202
  if (typeof p.waitMs === 'number') e.waitMs = p.waitMs
@@ -227,10 +227,10 @@ export async function closeLogin ({ transport, token, user, sid, publickey, priv
227
227
  if (typeof sign !== 'function' && !privateJwk && !privateKey) {
228
228
  throw err('no-signature', 'closeLogin: give it the device key (privateJwk/privateKey) or a sign(data)')
229
229
  }
230
- const firmado = typeof sign === 'function'
230
+ const signed = typeof sign === 'function'
231
231
  ? await sign(data)
232
232
  : await signWithDevice({ privateJwk, privateKey, publickey, data })
233
- const signature = typeof firmado === 'string' ? firmado : firmado?.signature
233
+ const signature = typeof signed === 'string' ? signed : signed?.signature
234
234
  if (typeof signature !== 'string') throw err('no-signature', 'closeLogin: sign(data) returned no signature')
235
235
  transport.send(token, { type: MSG.LOGIN_CLOSE, data, signature })
236
236
  try {
@@ -67,7 +67,7 @@ export const loginAddress = (user, fingerprint) => `${user}@${accountCode(finger
67
67
  */
68
68
  export async function accountFingerprint (identity) {
69
69
  const pid = (await identity?.profileActa?.().catch(() => null))?.acta?.profileId
70
- if (!pid) throw err('no-acta', 'this vault has no account record yet: there is no address to build')
70
+ if (!pid) throw err('no-record', 'this vault has no account record yet: there is no address to build')
71
71
  return (await pubkeyId(pid)).slice(0, 16)
72
72
  }
73
73
 
@@ -489,14 +489,14 @@ export async function registerLogin ({
489
489
  if (scope !== undefined) {
490
490
  throw Object.assign(new Error('registerLogin takes `caps` (account record permissions), not `scope`'), { code: 'use-caps' })
491
491
  }
492
- const pedidos = [...new Set(Array.isArray(caps) ? caps : [])]
493
- const raros = pedidos.filter((c) => !DEVICE_CAPS.includes(c))
494
- if (!pedidos.length || raros.length) {
495
- throw Object.assign(new Error(`not device permissions: ${raros.join(', ') || '(none)'} — expected any of ${DEVICE_CAPS.join(', ')}`), { code: 'bad-caps' })
492
+ const requests = [...new Set(Array.isArray(caps) ? caps : [])]
493
+ const oddOnes = requests.filter((c) => !DEVICE_CAPS.includes(c))
494
+ if (!requests.length || oddOnes.length) {
495
+ throw Object.assign(new Error(`not device permissions: ${oddOnes.join(', ') || '(none)'} — expected any of ${DEVICE_CAPS.join(', ')}`), { code: 'bad-caps' })
496
496
  }
497
497
  // El certificado lleva el scope de los permisos que lo tienen; los demás (`unattended`,
498
498
  // `passkeys`) viven solo en el acta, que es quien decide.
499
- const scopes = pedidos.map((c) => capScope(c)).filter(Boolean)
499
+ const scopes = requests.map((c) => capScope(c)).filter(Boolean)
500
500
  if (typeof identity?.admitMember !== 'function') {
501
501
  throw Object.assign(new Error('this vault cannot add devices to the account record: no login was created'), { code: 'admit-unavailable' })
502
502
  }
@@ -504,9 +504,9 @@ export async function registerLogin ({
504
504
  await logins.registerFinish({ user, upload, pub, encPub, deviceId, label, blob })
505
505
  try {
506
506
  const { cert } = await identity.signDelegation(pub, scopes, { label: label || `login:${user}` })
507
- await identity.admitMember({ pub, encPub, label: label || `login:${user}`, cn: null, caps: pedidos, cert })
507
+ await identity.admitMember({ pub, encPub, label: label || `login:${user}`, cn: null, caps: requests, cert })
508
508
  logins.setCert({ user, cert, iss: identity.me?.publickey || null })
509
- return { ok: true, user, deviceId, cert, caps: pedidos }
509
+ return { ok: true, user, deviceId, cert, caps: requests }
510
510
  } catch (e) {
511
511
  // NADA DE MEDIAS ALTAS: si no entra en el acta, no queda un usuario que pueda entrar a
512
512
  // una cuenta que no lo reconoce. Se deshace y se dice.
package/src/proc.js CHANGED
@@ -89,8 +89,8 @@ export function readProcContext (pid) {
89
89
  * Node, y entonces no hay nada que comprobar al otro lado.
90
90
  */
91
91
  export function selfContext () {
92
- const medido = readProcContext(process.pid)
93
- if (medido) return { ...medido, user: userName(), host: os.hostname() }
92
+ const measured = readProcContext(process.pid)
93
+ if (measured) return { ...measured, user: userName(), host: os.hostname() }
94
94
  const { argv, truncated } = trimArgv([process.execPath, ...process.argv.slice(1)])
95
95
  return {
96
96
  pid: process.pid,
@@ -111,9 +111,9 @@ export function selfContext () {
111
111
  * secretos, así que se refresca solo cuando cambia de máquina o de sistema.
112
112
  */
113
113
  export function selfMachine () {
114
- const tipo = os.type()
115
- const sistema = tipo === 'Darwin' ? 'macOS' : tipo === 'Windows_NT' ? 'Windows' : tipo
116
- return cleanMachine({ kind: 'node', os: `${sistema} ${os.release()}`, name: os.hostname() })
114
+ const kind = os.type()
115
+ const system = kind === 'Darwin' ? 'macOS' : kind === 'Windows_NT' ? 'Windows' : kind
116
+ return cleanMachine({ kind: 'node', os: `${system} ${os.release()}`, name: os.hostname() })
117
117
  }
118
118
 
119
119
  const safeCwd = () => { try { return process.cwd() } catch (_) { return '' } }
@@ -90,8 +90,8 @@ export const sameCommand = (a, b) => {
90
90
  /** El comando en una línea, para un log o una pantalla estrecha. */
91
91
  export function commandLine (ctx, max = 160) {
92
92
  const argv = Array.isArray(ctx?.argv) ? ctx.argv : []
93
- const linea = argv.length ? argv.join(' ') : (ctx?.exe || '')
94
- return linea.length > max ? linea.slice(0, max - 1) + '…' : linea
93
+ const line = argv.length ? argv.join(' ') : (ctx?.exe || '')
94
+ return line.length > max ? line.slice(0, max - 1) + '…' : line
95
95
  }
96
96
 
97
97
  export default { sanitizeContext, commandFingerprint, sameCommand, commandLine, trimArgv }