import BlindPairing from 'blind-pairing'
import b4a from 'b4a'
/**
* DeviceLinker — Blind-pairing device linking for mnemonic exchange
*
* Uses blind-pairing over Hyperswarm for secure mnemonic transfer
* between devices owned by the same user.
*/
export class DeviceLinker {
constructor (swarm, opts = {}) {
this._swarm = swarm
this._identityManager = opts.identityManager || null
this._onLinked = opts.onLinked || null // callback(mnemonic, remoteInfo)
}
/**
* Create a link invite (source device)
* Generates invite hex that the target device uses to pair.
* @returns {Promise<{ invite: string, promise: Promise<Object> }>}
*/
async createInvite () {
return new Promise((resolve, reject) => {
const identityManager = this._identityManager
const mnemonic = identityManager ? identityManager.getMnemonic() : null
if (!mnemonic) {
reject(new Error('No mnemonic available to share'))
return
}
const member = BlindPairing.member(this._swarm, {
onadd: (candidate) => {
candidate.open(b4a.from(JSON.stringify({
mnemonic,
instanceKey: identityManager ? identityManager.getPublicKeyHex() : null
})))
}
})
member.invite().then(invite => {
resolve({
invite: b4a.toString(invite, 'hex'),
member
})
}).catch(reject)
})
}
/**
* Join with invite (target device)
* Uses invite hex from source device to receive mnemonic.
* @param {string} inviteHex - Hex-encoded invite from source
* @returns {Promise<Object>} { mnemonic, instanceKey }
*/
async joinWithInvite (inviteHex) {
const identityManager = this._identityManager
const onLinked = this._onLinked
return new Promise((resolve, reject) => {
const invite = b4a.from(inviteHex, 'hex')
const candidate = BlindPairing.candidate(this._swarm, invite, {
onopen: (data) => {
try {
const payload = JSON.parse(b4a.toString(data))
if (identityManager && payload.mnemonic) {
identityManager.setMnemonic(payload.mnemonic).catch(() => {})
}
if (onLinked) onLinked(payload.mnemonic, payload)
resolve(payload)
} catch (err) {
reject(err)
}
}
})
candidate.on('error', reject)
})
}
destroy () {
// Cleanup if needed
}
}
export default DeviceLinker