@dotrino/identity 0.115.0 → 0.116.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/package.json +4 -1
- package/src/node.js +4 -180
- package/vault/core.js +66 -7
- package/vault/facade.js +200 -0
- package/vault/index.html +10 -0
- package/vault/remote.js +45 -5
- package/vault/vault.js +29 -58
- package/vault/vendor/compat/VERSION.txt +3 -0
- package/vault/vendor/compat/advisory.js +141 -0
- package/vault/vendor/compat/index.js +188 -0
- package/vault/vendor/compat/ranges.js +93 -0
- package/vault/vendor/opaque/VERSION.txt +1 -1
- package/vault/vendor/passmanager/VERSION.txt +3 -0
- package/vault/vendor/passmanager/crypto.js +167 -0
- package/vault/vendor/passmanager/fields.js +183 -0
- package/vault/vendor/passmanager/generate.js +74 -0
- package/vault/vendor/passmanager/import.js +156 -0
- package/vault/vendor/passmanager/index.js +27 -0
- package/vault/vendor/passmanager/match.js +137 -0
- package/vault/vendor/passmanager/model.js +332 -0
- package/vault/vendor/passmanager/pubkey.js +33 -0
- package/vault/vendor/passmanager/sealed/convert.js +72 -0
- package/vault/vendor/passmanager/sealed/device.js +211 -0
- package/vault/vendor/passmanager/sealed/entry.js +209 -0
- package/vault/vendor/passmanager/sealed/index.js +20 -0
- package/vault/vendor/passmanager/sealed/keys.js +120 -0
- package/vault/vendor/passmanager/sealed/recovery.js +136 -0
- package/vault/vendor/passmanager/sealed/store.js +508 -0
- package/vault/vendor/passmanager/session-cache.js +60 -0
- package/vault/vendor/passmanager/totp.js +111 -0
- package/vault/vendor/passmanager/transport/protocol.js +83 -0
- package/vault/vendor/passmanager/transport/proxy.js +112 -0
- package/vault/vendor/passmanager/transport/sealed.js +74 -0
- package/vault/vendor/passmanager/vault/approval.js +63 -0
- package/vault/vendor/passmanager/vault/errors.js +34 -0
- package/vault/vendor/passmanager/vault/guard.js +114 -0
- package/vault/vendor/passmanager/vault/interface.js +34 -0
- package/vault/vendor/passmanager/vault/local.js +213 -0
- package/vault/vendor/passmanager/vault/remote.js +85 -0
- package/vault/vendor/passmanager/vault/responder.js +192 -0
- package/vault/vendor/passmanager/vault/sealed-local.js +199 -0
- package/vault/vendor/passmanager/vault/sealed-responder.js +159 -0
- package/vault/vendor/passmanager/vault/sealed.js +362 -0
- package/vault/vendor/passmanager/webauthn.js +221 -0
- package/vault/vendor/proxy-client/VERSION.txt +1 -1
- package/vault/vendor/proxy-client/client.js +36 -0
- package/vault/vendor/roadmap/VERSION.txt +3 -0
- package/vault/vendor/roadmap/index.js +108 -0
- package/vault/vendor/roadmap/manifests.js +797 -0
- package/vault/vendor/store/VERSION.txt +3 -0
- package/vault/vendor/store/core.js +309 -0
- package/vault/vendor/vault/VERSION.txt +4 -1
- package/vault/vendor/vault/admin.js +437 -0
- package/vault/vendor/vault/browser.js +167 -0
- package/vault/vendor/vault/core/approvals.js +204 -0
- package/vault/vendor/vault/core/blocked.js +68 -0
- package/vault/vendor/vault/core/browserDisk.js +109 -0
- package/vault/vendor/vault/core/commKey.js +68 -0
- package/vault/vendor/vault/core/disk.js +65 -0
- package/vault/vendor/vault/core/libVersion.js +8 -0
- package/vault/vendor/vault/core/machines.js +66 -0
- package/vault/vendor/vault/core/notices.js +71 -0
- package/vault/vendor/vault/core/notifiable.js +73 -0
- package/vault/vendor/vault/core/passwords.js +125 -0
- package/vault/vendor/vault/core/sealKey.js +77 -0
- package/vault/vendor/vault/core/sealer.js +172 -0
- package/vault/vendor/vault/core/sealers.js +159 -0
- package/vault/vendor/vault/core/secretsStore.js +1326 -0
- package/vault/vendor/vault/core/sha256.js +69 -0
- package/vault/vendor/vault/core/store.js +68 -0
- package/vault/vendor/vault/core/subacta.js +101 -0
- package/vault/vendor/vault/core/threadStore.js +273 -0
- package/vault/vendor/vault/core/transport.js +270 -0
- package/vault/vendor/vault/core/vault.js +4414 -0
- package/vault/vendor/vault/enroll.js +8 -4
- package/vault/vendor/vault/procContext.js +97 -0
- package/vault/vendor/vault/revocation.js +67 -0
- package/vault/vendor/vault/sealed.js +84 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transporte headless del vault: el cliente OFICIAL `@dotrino/proxy-client`
|
|
3
|
+
* corriendo en Node (mismo patrón que `dotrino-bots/src/core/transport.js`).
|
|
4
|
+
* Hace el `identify` — liga el token efímero de la conexión a una pubkey estable,
|
|
5
|
+
* habilitando el direccionamiento por pubkey (`sendByPubkey`) y la cola offline
|
|
6
|
+
* de 24 h del proxy.
|
|
7
|
+
*
|
|
8
|
+
* QUIÉN FIRMA ESE `identify`, que es lo que cambió: **la llave de comunicación**
|
|
9
|
+
* (`commKey.js`), no la maestra. La maestra sella el acta y reenvuelve sobres, y
|
|
10
|
+
* nada más; si además tuviera que firmar aquí no podría vivir bajo llave, porque
|
|
11
|
+
* esto se firma en cada conexión Y en cada reconexión — una bóveda cerrada no
|
|
12
|
+
* existiría en la red.
|
|
13
|
+
*
|
|
14
|
+
* Lo que ata el token al PERFIL entonces ya no es un certificado sino el ACTA: el
|
|
15
|
+
* proxio comprueba que quien habla sea miembro (`verifyActaMembership`) y ata el
|
|
16
|
+
* token también al `profileId`. Por eso se manda el acta siempre.
|
|
17
|
+
*
|
|
18
|
+
* IMPORTANTE: `me.publickey` puede ser null en el primer arranque (solo se puebla
|
|
19
|
+
* al fijar un nickname), así que la pubkey maestra se obtiene de forma robusta
|
|
20
|
+
* con un `signData` de cortesía si hace falta. El daemon (vault.js) además puebla
|
|
21
|
+
* `me` antes de conectar.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const DEFAULT_PROXY = 'wss://proxy.dotrino.com'
|
|
25
|
+
|
|
26
|
+
export async function masterPubkeyOf (identity) {
|
|
27
|
+
return identity.me?.publickey || (await identity.signData({ op: 'whoami', ts: Date.now() })).publickey
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Conecta el transporte y lo identifica con la maestra del vault.
|
|
32
|
+
* @param {Object} opts
|
|
33
|
+
* @param {import('@dotrino/identity/facade').IdentityFacade} opts.identity
|
|
34
|
+
* @param {Function} [opts.makeClient] quién crea el cliente del proxio. Sin él, el del
|
|
35
|
+
* ecosistema; el navegador lo pasa siempre (un service worker no puede hacer `import()`).
|
|
36
|
+
* @param {string} [opts.url] URL del proxy (default wss://proxy.dotrino.com).
|
|
37
|
+
* @returns {Promise<{ client, token:string, identify():Promise<void> }>}
|
|
38
|
+
*/
|
|
39
|
+
export async function createTransport ({ identity, url = DEFAULT_PROXY, commKey = null, log = () => {}, makeClient = null, announce = null, recheckMs = 60_000, patienceMs = 5 * 60_000, reannounceMs = 10 * 60_000 }) {
|
|
40
|
+
// Import dinámico DESPUÉS de instalar los globals que el paquete usa.
|
|
41
|
+
// `WebSocketProxyClient` (la clase) y NO el helper `getWebSocketProxyClient`:
|
|
42
|
+
// ese es un SINGLETON de proceso, y con multi-perfil el vault necesita una
|
|
43
|
+
// conexión POR PERFIL (cada maestra se identifica con su propia pubkey ante el
|
|
44
|
+
// proxy). Con el singleton, el segundo perfil reusaba el cliente del primero y
|
|
45
|
+
// su `identify` pisaba al anterior. Sigue siendo el cliente oficial del paquete.
|
|
46
|
+
// `makeClient` es la costura de las PRUEBAS, y existe por una razón concreta: comprobar
|
|
47
|
+
// que un `identify` rechazado no tumba el perfil y se reintenta solo. Sin ella habría que
|
|
48
|
+
// levantar un proxio de verdad para probar el caso que ya costó una tarde. En producción
|
|
49
|
+
// no se pasa nunca y esto es el cliente oficial de siempre.
|
|
50
|
+
const { WebSocketProxyClient } = makeClient ? {} : await import('@dotrino/proxy-client')
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* EL CANAL DIRECTO, ENCENDIDO Y CON LA PUERTA CERRADA.
|
|
54
|
+
*
|
|
55
|
+
* Encendido porque es el escalón 2 de la regla del transporte (`CLAUDE.md`) y en Node ya
|
|
56
|
+
* hay con qué (`werift`, que el pilar carga solo). Si no lo hubiera, negociar falla y se
|
|
57
|
+
* sigue por el proxio — el escalón 4 siempre está.
|
|
58
|
+
*
|
|
59
|
+
* Y con la puerta cerrada desde el primer día, que es lo que hace defendible encenderlo:
|
|
60
|
+
* **solo se le negocia a quien está en el acta.** Aceptar una señal arranca DTLS, ICE y
|
|
61
|
+
* SCTP, o sea código que parsea red no confiable, dentro del proceso que guarda la
|
|
62
|
+
* maestra. Quien decide si eso corre no puede ser el que llama a la puerta.
|
|
63
|
+
*
|
|
64
|
+
* Reconexión prácticamente ilimitada: un daemon de larga duración no debe rendirse tras
|
|
65
|
+
* unos intentos.
|
|
66
|
+
*/
|
|
67
|
+
// La puerta tiene que decidir AL INSTANTE (la señal llega y o se atiende o no), y pedir
|
|
68
|
+
// el acta es asíncrono. Así que se guarda una copia y se refresca donde ya se refresca
|
|
69
|
+
// todo: en `identify`, que es lo que corre cada vez que el acta cambia o se reconecta.
|
|
70
|
+
let actaVista = null
|
|
71
|
+
// ¿Está esta bóveda ATADA al proxio ahora mismo? Conectado no es identificado: el socket
|
|
72
|
+
// puede estar perfectamente abierto y el sobre rechazado, y entonces nadie la alcanza por
|
|
73
|
+
// su pubkey. Se expone para que `status` lo enseñe en vez de dejarlo a un log del arranque.
|
|
74
|
+
let identificado = false
|
|
75
|
+
const enElActa = (token) => {
|
|
76
|
+
// SIN ACTA NO SE NEGOCIA. No se sabe quién es nadie, y en la duda no se arranca a
|
|
77
|
+
// parsear red no confiable. Se sigue por el proxio, que es el escalón que siempre está.
|
|
78
|
+
if (!actaVista) return false
|
|
79
|
+
return (actaVista.members || []).some((m) => m?.pub === token)
|
|
80
|
+
}
|
|
81
|
+
const client = makeClient ? makeClient({ url, acceptDirectFrom: enElActa }) : new WebSocketProxyClient({
|
|
82
|
+
url, enableWebRTC: true, acceptDirectFrom: enElActa, autoReconnect: true,
|
|
83
|
+
maxReconnectAttempts: 100000, reconnectDelay: 4000
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
await client.connect()
|
|
87
|
+
|
|
88
|
+
const identify = async () => {
|
|
89
|
+
identificado = false
|
|
90
|
+
if (!client.token) return
|
|
91
|
+
const record = (await identity.profileActa?.().catch(() => null))?.acta || null
|
|
92
|
+
actaVista = record // la copia que mira la puerta del canal directo
|
|
93
|
+
const comm = commKey?.pub?.() || null
|
|
94
|
+
// ¿Nos nombra el acta? Con `cn` o sin él, lo que el proxio mira es la pertenencia.
|
|
95
|
+
const esMiembro = comm && (record?.members || []).some((m) => m?.pub === comm)
|
|
96
|
+
|
|
97
|
+
if (comm && esMiembro) {
|
|
98
|
+
// El sobre lo arma el pilar (`identifyAs`), que le pone el destinatario.
|
|
99
|
+
try {
|
|
100
|
+
await client.identifyAs({ publickey: comm, sign: (d) => commKey.sign(d), acta: record })
|
|
101
|
+
identificado = true
|
|
102
|
+
// SE DICE QUE SE IDENTIFICÓ, y con qué llave. Este camino era MUDO: al pasar de la
|
|
103
|
+
// maestra a la llave de comunicación, el único rastro de «estoy alcanzable» que
|
|
104
|
+
// quedaba en el log era el del repliegue. Así que cuando la consola decía «no
|
|
105
|
+
// contesta», no había forma de saber desde aquí si la bóveda estaba atada al proxio
|
|
106
|
+
// o no. Una línea por reconexión es barata; no tenerla cuesta una hora de sondeos.
|
|
107
|
+
log(`[vault] identified on the proxy with the communication key · record #${record?.seq ?? '?'}`)
|
|
108
|
+
return
|
|
109
|
+
} catch (e) {
|
|
110
|
+
// SOLO «esta llave no firma» cae al camino de abajo, y se distingue por el `code`.
|
|
111
|
+
// Un fallo de red no es eso: tragárselo aquí mandaría a la maestra a firmar por un
|
|
112
|
+
// problema pasajero, que es justo lo que la llave de comunicación vino a evitar.
|
|
113
|
+
if (e?.code !== 'no-signature') throw e
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// REPLIEGUE, y solo para migrar: una bóveda que todavía no ha metido su llave de
|
|
118
|
+
// comunicación en el acta se identifica con la maestra, como siempre. Exige el perfil
|
|
119
|
+
// ABIERTO —la maestra bajo llave no firma— y por eso se dice en voz alta: mientras se
|
|
120
|
+
// esté aquí, cerrar el perfil deja la bóveda sin voz.
|
|
121
|
+
//
|
|
122
|
+
// QUEDARSE SIN VOZ NO ES NO ARRANCAR, y aquí eran lo mismo. Un perfil con la maestra
|
|
123
|
+
// sellada y sin llave de comunicación todavía llegaba hasta este `signData`, que tira
|
|
124
|
+
// `vault locked`; el error subía por `createTransport` hasta `startVault` y **el perfil
|
|
125
|
+
// entero se quedaba sin abrir**. Al dueño le pasó el 2026-09-02: su perfil principal no
|
|
126
|
+
// arrancaba, `unlock` lo marcaba abierto igual y cada petición contestaba «profile is
|
|
127
|
+
// not open», con la TUI vacía y sin un error a la vista.
|
|
128
|
+
//
|
|
129
|
+
// Se dice y se sigue. Sin red se puede vivir hasta que alguien teclee la frase; sin
|
|
130
|
+
// bóveda, no.
|
|
131
|
+
try {
|
|
132
|
+
const publickey = await masterPubkeyOf(identity)
|
|
133
|
+
if (!publickey) return
|
|
134
|
+
log('[vault] identifying with the master key: this vault is not in its own record yet (open the profile once to fix it)')
|
|
135
|
+
await client.identifyAs({ publickey, sign: (d) => identity.signData(d), acta: record })
|
|
136
|
+
identificado = true
|
|
137
|
+
} catch (e) {
|
|
138
|
+
log(`[vault] cannot identify on the proxy yet: ${e.message} — the profile still opens; unlock it once and it gets its own communication key`)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* IDENTIFICARSE PUEDE FALLAR, Y ESO NO PUEDE TUMBAR EL PERFIL — NI QUEDARSE ASÍ.
|
|
143
|
+
*
|
|
144
|
+
* Esto era `await identify()` a secas, así que cualquier «no» del proxio subía por
|
|
145
|
+
* `createTransport` hasta `startVault` y el perfil **no se abría en absoluto**: no es que
|
|
146
|
+
* se quedara sin voz, es que cada petición contestaba `profile is not open` y en el
|
|
147
|
+
* `status` el perfil salía sin huella. Y no se reintentaba nunca: `identify` solo vuelve a
|
|
148
|
+
* correr con el evento `token`, o sea al RECONECTAR, y aquí el socket estaba perfectamente
|
|
149
|
+
* conectado — lo que el proxio rechazó fue el sobre.
|
|
150
|
+
*
|
|
151
|
+
* El caso real (2026-09-10): esta máquina arranca con el reloj 35 minutos atrasado (el
|
|
152
|
+
* `systemd-timesyncd` no contactó el servidor de hora hasta media hora después del
|
|
153
|
+
* arranque), el proxio rechaza el `identify` con «ts fuera de la ventana ±5min» y la
|
|
154
|
+
* bóveda se quedaba fuera de la red DESDE EL ARRANQUE HASTA QUE ALGUIEN LA REINICIABA.
|
|
155
|
+
* Con ella fuera no hay a quién timbrar: el teléfono nunca recibía un pedido de
|
|
156
|
+
* aprobación, y desde fuera eso se ve igual que «no hay pedidos».
|
|
157
|
+
*
|
|
158
|
+
* Reintentar con espera creciente lo arregla solo en cuanto el reloj se corrige, y cada
|
|
159
|
+
* intento se dice en voz alta: una bóveda inalcanzable en silencio es el fallo que ya
|
|
160
|
+
* costó un día entero (`CLAUDE.md`, el apagón del 1-2 de septiembre).
|
|
161
|
+
*/
|
|
162
|
+
/**
|
|
163
|
+
* EL ANUNCIO DE LA CUENTA (`temporary-access.md` §3.2).
|
|
164
|
+
*
|
|
165
|
+
* Quien entra con usuario y contraseña no tiene ninguna llave todavía: no sabe la pubkey
|
|
166
|
+
* de esta bóveda y no puede escribirle. Lo único que trae es el código de la dirección
|
|
167
|
+
* (`nombre@AB12-CD34-EF56`), y con él lista este canal para encontrar a quien atiende esa
|
|
168
|
+
* cuenta — esta bóveda, o cualquiera de sus réplicas.
|
|
169
|
+
*
|
|
170
|
+
* Va PEGADO a `identify` y no en el arranque, porque la pertenencia a un canal es del
|
|
171
|
+
* token, y el token cambia en cada reconexión: anunciarse una sola vez deja a la bóveda
|
|
172
|
+
* fuera del canal en cuanto se cae el socket, y eso se ve desde fuera como «esa cuenta no
|
|
173
|
+
* existe». Si el anuncio falla, la bóveda sigue sirviendo a todo lo demás y se dice en voz
|
|
174
|
+
* alta: lo que se pierde es que la encuentren por su dirección, no que deje de funcionar.
|
|
175
|
+
*/
|
|
176
|
+
let announcedAt = 0
|
|
177
|
+
const announceAccount = async () => {
|
|
178
|
+
if (!announce || !identificado) return
|
|
179
|
+
announcedAt = Date.now()
|
|
180
|
+
try { await client.publish(announce) } catch (e) {
|
|
181
|
+
log(`[vault] could not announce this account on the proxy (${e.message}) — a borrowed machine will not find this vault by its address`)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let reintento = null
|
|
186
|
+
let fallos = 0
|
|
187
|
+
const identifyWithRetry = async () => {
|
|
188
|
+
if (reintento) { clearTimeout(reintento); reintento = null }
|
|
189
|
+
try {
|
|
190
|
+
await identify()
|
|
191
|
+
await announceAccount()
|
|
192
|
+
if (fallos) log(`[vault] identified on the proxy after ${fallos + 1} attempt(s)`)
|
|
193
|
+
fallos = 0
|
|
194
|
+
} catch (e) {
|
|
195
|
+
fallos++
|
|
196
|
+
const espera = Math.min(60000, 5000 * fallos)
|
|
197
|
+
log(`[vault] could not identify on the proxy (${e.message}) — retrying in ${Math.round(espera / 1000)}s; until then this vault is unreachable and cannot ring anyone`)
|
|
198
|
+
reintento = setTimeout(() => { reintento = null; identifyWithRetry() }, espera)
|
|
199
|
+
reintento.unref?.()
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
await identifyWithRetry()
|
|
203
|
+
// Re-identificar al reconectar (el token cambia).
|
|
204
|
+
client.on('token', () => { fallos = 0; identifyWithRetry() })
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* QUE NO SE PUEDA VOLVER A QUEDAR MUDA.
|
|
208
|
+
*
|
|
209
|
+
* El 16 de septiembre esta bóveda se cayó del proxio y no volvió en 36 HORAS, con el
|
|
210
|
+
* proceso vivo y sin una sola línea en el log: el cliente del transporte se rendía tras
|
|
211
|
+
* un reintento fallido (arreglado en `@dotrino/proxy-client` 0.23.0). Desde fuera eso se
|
|
212
|
+
* ve igual que «no pasa nada»: el teléfono no timbra y nadie sabe por qué.
|
|
213
|
+
*
|
|
214
|
+
* El arreglo del pilar quita ESA causa. Esto quita el SILENCIO, que es lo que costó las
|
|
215
|
+
* 36 horas — con cualquier causa, futura incluida:
|
|
216
|
+
*
|
|
217
|
+
* · se dice cuándo se cae y cuándo vuelve, una vez cada cosa (no una línea cada 4 s);
|
|
218
|
+
* · y si lleva 5 minutos fuera, se vuelve a marcar en vez de esperar sentada.
|
|
219
|
+
*
|
|
220
|
+
* `connect()` es seguro de repetir: si ya está conectado devuelve el token y no abre
|
|
221
|
+
* nada.
|
|
222
|
+
*/
|
|
223
|
+
const RECHECK_MS = recheckMs
|
|
224
|
+
const PATIENCE_MS = patienceMs
|
|
225
|
+
const REANNOUNCE_MS = reannounceMs // la mitad de lo que el proxio guarda una entrada
|
|
226
|
+
let downSince = 0
|
|
227
|
+
let saidDown = false
|
|
228
|
+
client.on('disconnect', ({ code, reason } = {}) => {
|
|
229
|
+
// Sin socket no hay nadie atado al proxio: lo que se identificó era la conexión anterior.
|
|
230
|
+
identificado = false
|
|
231
|
+
if (!downSince) downSince = Date.now()
|
|
232
|
+
if (saidDown) return
|
|
233
|
+
saidDown = true
|
|
234
|
+
log(`[vault] off the proxy (${code || '?'}${reason ? ': ' + reason : ''}) — nobody can reach this vault while this lasts`)
|
|
235
|
+
})
|
|
236
|
+
// LOS INTENTOS SE VEN. El 2026-10-15 la bóveda pasó hora y media fuera y no quedó ni una
|
|
237
|
+
// línea de si el transporte estaba reintentando o no; en el proxio no llegó ninguno. El
|
|
238
|
+
// primero se dice, y luego uno de cada 15 (un minuto con el retardo de 4 s).
|
|
239
|
+
client.on('reconnecting', (attempt) => {
|
|
240
|
+
if (attempt === 1 || attempt % 15 === 0) log(`[vault] reconnecting to the proxy (attempt ${attempt})`)
|
|
241
|
+
})
|
|
242
|
+
client.on('reconnect_failed', (attempts) => {
|
|
243
|
+
log(`[vault] the transport gave up reconnecting after ${attempts} attempt(s) — this vault is unreachable until it is restarted`)
|
|
244
|
+
})
|
|
245
|
+
const watchdog = setInterval(async () => {
|
|
246
|
+
// CONECTADO DE VERDAD, no «tengo un token». El token es el de la última conexión y no se
|
|
247
|
+
// borra al caerse, así que mirarlo daba la bóveda por vuelta con el socket cerrado: el
|
|
248
|
+
// 2026-10-15 dijo «back on the proxy after 50s», no volvió a marcar nunca y pasó hora y
|
|
249
|
+
// media fuera, avisando cada 10 minutos de que no podía anunciarse.
|
|
250
|
+
if (identificado && client.isConnected && client.token) {
|
|
251
|
+
if (downSince) log(`[vault] back on the proxy after ${Math.round((Date.now() - downSince) / 1000)}s`)
|
|
252
|
+
downSince = 0; saidDown = false
|
|
253
|
+
// EL ANUNCIO CADUCA: el proxio borra una entrada de canal a los 20 minutos de publicada.
|
|
254
|
+
// Sin renovarlo, entrar con usuario y contraseña solo funcionaba los primeros 20 minutos
|
|
255
|
+
// tras identificarse, y desde fuera se veía como «no hay ninguna bóveda en esa dirección»
|
|
256
|
+
// con la bóveda encendida (2026-10-12, la demo de los revisores).
|
|
257
|
+
if (announce && Date.now() - announcedAt >= REANNOUNCE_MS) await announceAccount()
|
|
258
|
+
return
|
|
259
|
+
}
|
|
260
|
+
if (!downSince) downSince = Date.now()
|
|
261
|
+
const downMs = Date.now() - downSince
|
|
262
|
+
if (downMs < PATIENCE_MS) return
|
|
263
|
+
log(`[vault] ${Math.round(downMs / 60000)} min off the proxy — dialling again`)
|
|
264
|
+
try { await client.connect() } catch (e) { log(`[vault] could not reconnect: ${e.message}`) }
|
|
265
|
+
await identifyWithRetry()
|
|
266
|
+
}, RECHECK_MS)
|
|
267
|
+
watchdog.unref?.()
|
|
268
|
+
|
|
269
|
+
return { client, token: client.token, identify: identifyWithRetry, isIdentified: () => identificado }
|
|
270
|
+
}
|