// probe-vault-setup.js — break down what `vault.setup` does into individual
// per-step calls with their own timeouts, so we can see WHICH call hangs
// instead of just "vault.setup timed out at 60s".
//
// `_vaultSetup` (router.js:293) does roughly:
// 1. await backend.api.core.confirmRecoveryPhrase()
// 2. const raw = await backend.api.core.getRecoveryPhrase()
// 3. if (phrase) { await backend.api.core.storeRecoveryPhrase(phrase) }
// else { phrase = generateMnemonic();
// await backend.api.core.restoreIdentity(phrase) }
// 4. await this._vault.encrypt(...)
//
// All four of those backend.api.core calls await `onlineBoot.ready()`
// internally. If onlineBoot never resolves, step 1 hangs forever.
//
// This probe wraps each call in a 15s timeout and prints which one fires
// first (or which one succeeds). On stortron — where the user reported
// vault.setup timeout — this should show whether step 1 hangs immediately
// (onlineBoot deadlock) or some later step is the culprit.
//
// SAFETY: uses real ~/.config/keet-cli storage, but DOES NOT create a new
// identity. Operates on whatever the active identity is. Reads only.
// If a recovery phrase already exists it won't be overwritten. The vault
// encrypt step is skipped (no actual mnemonic stored) so this is non-
// destructive.
//
// Run: bare test/probe-vault-setup.js
import path from 'bare-path'
import os from 'bare-os'
import { startDaemon } from '../index.js'
const STORAGE = path.join(os.homedir(), '.config', 'keet-cli')
const SOCKET = '/tmp/keet-daemon-vault-probe.sock'
const PER_STEP_TIMEOUT_MS = 15000
function withTimeout (promise, ms, label) {
return new Promise((resolve, reject) => {
const t = setTimeout(() => reject(new Error(label + ' timed out after ' + ms + 'ms')), ms)
promise.then(v => { clearTimeout(t); resolve(v) },
e => { clearTimeout(t); reject(e) })
})
}
async function timed (label, fn) {
const t0 = Date.now()
console.log('[probe] ' + label.padEnd(36) + ' starting...')
try {
const result = await withTimeout(fn(), PER_STEP_TIMEOUT_MS, label)
console.log('[probe] → OK in ' + (Date.now() - t0) + 'ms')
return { ok: true, result }
} catch (err) {
console.log('[probe] → FAIL after ' + (Date.now() - t0) + 'ms')
console.log(' ' + (err && err.message))
if (err && err.stack && !err.message.includes('timed out')) {
for (const line of err.stack.split('\n').slice(0, 6)) console.log(' ' + line)
}
return { ok: false, err }
}
}
async function main () {
console.log('[probe] storage:', STORAGE)
console.log('[probe] starting daemon (gives onlineBoot 30s to settle)...')
const daemon = await startDaemon({ storage: STORAGE, socketPath: SOCKET })
console.log('[probe] daemon up. backend.ready resolved (offlineBoot only).')
const backend = daemon.router.backend
const core = backend.api.core
// Wait a few seconds to give onlineBoot a chance to settle on its own
// before we start probing. If onlineBoot.ready() resolves naturally,
// step 1 below will return immediately.
console.log('[probe] sleeping 5s to let onlineBoot settle...')
await new Promise(r => setTimeout(r, 5000))
console.log('')
console.log('== probing each vault.setup sub-call independently ==')
await timed('confirmRecoveryPhrase()', () => core.confirmRecoveryPhrase())
const phraseRes = await timed('getRecoveryPhrase()', () => core.getRecoveryPhrase())
let phrase = null
if (phraseRes.ok) {
const raw = phraseRes.result
phrase = typeof raw === 'string' && raw.length > 0 ? raw : (raw && raw.phrase) || null
console.log('[probe] getRecoveryPhrase returned: ' + (phrase ? 'phrase ' + phrase.split(' ').length + ' words' : 'null/empty'))
}
// Also probe onlineBoot state directly via getIdentity (a different
// onlineBoot-dependent call) so we know if it's confirmRecoveryPhrase
// specifically or onlineBoot universally.
await timed('getIdentity()', () => core.getIdentity())
// Try restoreIdentity with a NEW mnemonic — only if getRecoveryPhrase
// returned empty (meaning fresh-storage scenario).
if (!phrase) {
console.log('[probe] no existing phrase, would generate + restoreIdentity in real flow')
console.log('[probe] SKIPPING restoreIdentity to keep this probe non-destructive')
} else {
console.log('[probe] phrase already present; storeRecoveryPhrase is the next step (skipped)')
}
console.log('')
console.log('== shutdown ==')
try { await daemon.uds.stop() } catch {}
try { await daemon.router.core.close() } catch {}
try { await daemon.core.close() } catch {}
Bare.exit(0)
}
main().catch(err => {
console.error('[probe] uncaught:', err && err.message)
if (err && err.stack) console.error(err.stack)
Bare.exit(1)
})