@dotrino/identity 0.15.0 → 0.16.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/package.json +1 -1
- package/src/node.js +10 -0
- package/vault/core.js +32 -9
- package/vault/peerStore.js +14 -0
package/package.json
CHANGED
package/src/node.js
CHANGED
|
@@ -50,6 +50,16 @@ function filePeers (filePath) {
|
|
|
50
50
|
}
|
|
51
51
|
return {
|
|
52
52
|
setProfile (p) { pid = p || null },
|
|
53
|
+
adoptLegacy (newPid) {
|
|
54
|
+
// Migración: copia el peers.json viejo (sin namespace) al perfil newPid.
|
|
55
|
+
try {
|
|
56
|
+
if (!fs.existsSync(filePath)) return
|
|
57
|
+
const legacy = JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
58
|
+
if (legacy && typeof legacy === 'object' && Object.keys(legacy).length) {
|
|
59
|
+
fs.writeFileSync(path.join(path.dirname(filePath), `peers.${newPid}.json`), JSON.stringify(legacy))
|
|
60
|
+
}
|
|
61
|
+
} catch (_) { /* sin peers viejos */ }
|
|
62
|
+
},
|
|
53
63
|
async initPeerStorage () {
|
|
54
64
|
const f = fileFor()
|
|
55
65
|
try {
|
package/vault/core.js
CHANGED
|
@@ -195,6 +195,16 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null })
|
|
|
195
195
|
// Canal de eventos 'vault' (p.ej. el SAS a comparar durante el emparejamiento).
|
|
196
196
|
const vaultListeners = new Set()
|
|
197
197
|
const emitVault = (p) => { for (const fn of vaultListeners) { try { fn(p) } catch (_) {} } }
|
|
198
|
+
// Si el vault RECHAZA por cert revocado, este dispositivo perdió el acceso: limpiamos el
|
|
199
|
+
// cert local (ya no sirve) y emitimos 'revoked' → @dotrino/store borra SOLO el store de
|
|
200
|
+
// ESTE perfil (los demás perfiles quedan intactos). Cualquier otro error se propaga igual.
|
|
201
|
+
const handleVaultError = (e) => {
|
|
202
|
+
if (e && /\brevoked\b/.test(e.message || '')) {
|
|
203
|
+
try { kv.removeItem(VAULT_CERT_STORAGE); kv.removeItem(VAULT_DEVICE_STORAGE) } catch (_) {}
|
|
204
|
+
emitVault({ phase: 'revoked' })
|
|
205
|
+
}
|
|
206
|
+
throw e
|
|
207
|
+
}
|
|
198
208
|
const loadVaultCert = () => { try { return JSON.parse(kv.getItem(VAULT_CERT_STORAGE) || 'null') } catch (_) { return null } }
|
|
199
209
|
const loadVaultDevice = () => { try { return JSON.parse(kv.getItem(VAULT_DEVICE_STORAGE) || 'null') } catch (_) { return null } }
|
|
200
210
|
|
|
@@ -613,7 +623,8 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null })
|
|
|
613
623
|
async vaultSign ({ payload }) {
|
|
614
624
|
const v = loadVaultCert(); const device = loadVaultDevice()
|
|
615
625
|
if (!v?.cert || !device) throw new Error('este dispositivo no está emparejado con un vault')
|
|
616
|
-
return remoteSign({ master: v.master, proxy: v.proxy, device, cert: v.cert, payload })
|
|
626
|
+
try { return await remoteSign({ master: v.master, proxy: v.proxy, device, cert: v.cert, payload }) }
|
|
627
|
+
catch (e) { return handleVaultError(e) }
|
|
617
628
|
},
|
|
618
629
|
|
|
619
630
|
// Store DELEGADO: lee/escribe el store de hilos+aperturas EN tu vault (con el cert).
|
|
@@ -621,14 +632,16 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null })
|
|
|
621
632
|
async vaultStore ({ method, args }) {
|
|
622
633
|
const v = loadVaultCert(); const device = loadVaultDevice()
|
|
623
634
|
if (!v?.cert || !device) throw new Error('este dispositivo no está emparejado con un vault')
|
|
624
|
-
return remoteStore({ master: v.master, proxy: v.proxy, device, cert: v.cert, method, args })
|
|
635
|
+
try { return await remoteStore({ master: v.master, proxy: v.proxy, device, cert: v.cert, method, args }) }
|
|
636
|
+
catch (e) { return handleVaultError(e) }
|
|
625
637
|
},
|
|
626
638
|
|
|
627
639
|
// Lista (solo lectura) de dispositivos enrolados en tu vault.
|
|
628
640
|
async listVaultDevices () {
|
|
629
641
|
const v = loadVaultCert(); const device = loadVaultDevice()
|
|
630
642
|
if (!v?.cert || !device) throw new Error('este dispositivo no está emparejado con un vault')
|
|
631
|
-
return remoteDevices({ master: v.master, proxy: v.proxy, device, cert: v.cert })
|
|
643
|
+
try { return await remoteDevices({ master: v.master, proxy: v.proxy, device, cert: v.cert }) }
|
|
644
|
+
catch (e) { return handleVaultError(e) }
|
|
632
645
|
},
|
|
633
646
|
|
|
634
647
|
// El cert de delegación de este dispositivo (para presentarlo al proxy en `identify`
|
|
@@ -739,17 +752,27 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null })
|
|
|
739
752
|
|
|
740
753
|
// ----- bootstrap -----
|
|
741
754
|
|
|
742
|
-
// Perfil activo (multi-perfil por dispositivo).
|
|
743
|
-
//
|
|
744
|
-
//
|
|
755
|
+
// Perfil activo (multi-perfil por dispositivo). Si no hay perfiles, se crea el primero; si
|
|
756
|
+
// existe una identidad ÚNICA vieja (pre-multi-perfil, claves sin namespace), se ADOPTA como
|
|
757
|
+
// "Perfil 1" — sin pérdida. A partir de acá `kv` está scopeado a `currentPid`.
|
|
745
758
|
{
|
|
746
759
|
let profiles = loadProfiles()
|
|
747
760
|
currentPid = rawKv.getItem(CURRENT_STORAGE)
|
|
748
761
|
if (!profiles.length) {
|
|
749
|
-
|
|
750
|
-
|
|
762
|
+
const pid = 'p' + crypto.randomUUID().slice(0, 8)
|
|
763
|
+
// Migración: adoptar la identidad única vieja (si la hay) copiando sus claves al namespace de pid.
|
|
764
|
+
const legacy = rawKv.getItem(KEY_STORAGE)
|
|
765
|
+
if (legacy) {
|
|
766
|
+
for (const s of ['keypair', 'enc-keypair', 'me', 'nonces', 'delegations', 'revocations', 'vault.device', 'vault.cert']) {
|
|
767
|
+
const v = rawKv.getItem('dotrino.identity.' + s)
|
|
768
|
+
if (v != null) rawKv.setItem(`dotrino.identity.p.${pid}.${s}`, v)
|
|
769
|
+
}
|
|
770
|
+
try { await peers.adoptLegacy?.(pid) } catch (_) { /* peers viejos opcionales */ }
|
|
771
|
+
}
|
|
772
|
+
currentPid = pid
|
|
773
|
+
profiles = [{ id: pid, name: '', pubkey: null }]
|
|
751
774
|
saveProfiles(profiles)
|
|
752
|
-
rawKv.setItem(CURRENT_STORAGE,
|
|
775
|
+
rawKv.setItem(CURRENT_STORAGE, pid)
|
|
753
776
|
} else if (!currentPid || !profiles.find((p) => p.id === currentPid)) {
|
|
754
777
|
currentPid = profiles[0].id
|
|
755
778
|
rawKv.setItem(CURRENT_STORAGE, currentPid)
|
package/vault/peerStore.js
CHANGED
|
@@ -40,6 +40,20 @@ export function onDirty (fn) { _markDirty = fn }
|
|
|
40
40
|
export function setProfile (pid) { _pid = pid || null }
|
|
41
41
|
function peersKey () { return _pid ? `peers.${_pid}.v1` : IDB_PEERS_KEY }
|
|
42
42
|
|
|
43
|
+
/** Migración: copia el peer book VIEJO (pre-multi-perfil, sin namespace) al perfil `pid`. */
|
|
44
|
+
export async function adoptLegacy (pid) {
|
|
45
|
+
try {
|
|
46
|
+
_idb = _idb || await openIdb()
|
|
47
|
+
let legacy = await idbGet(_idb, IDB_PEERS_KEY) // 'peers.v1' (viejo)
|
|
48
|
+
if (!legacy || typeof legacy !== 'object' || !Object.keys(legacy).length) {
|
|
49
|
+
try { const raw = localStorage.getItem(PEERS_STORAGE); legacy = raw ? JSON.parse(raw) : null } catch (_) { legacy = null }
|
|
50
|
+
}
|
|
51
|
+
if (legacy && typeof legacy === 'object' && Object.keys(legacy).length) {
|
|
52
|
+
await idbPut(_idb, `peers.${pid}.v1`, legacy)
|
|
53
|
+
}
|
|
54
|
+
} catch (_) { /* sin peers viejos que adoptar */ }
|
|
55
|
+
}
|
|
56
|
+
|
|
43
57
|
function openIdb () {
|
|
44
58
|
return new Promise((resolve, reject) => {
|
|
45
59
|
let req
|