import b4a from 'b4a' import c from 'compact-encoding' import { PROTOCOL_NAME, PROTOCOL_APP_CHANNEL_NAME, PROTOCOL_CAP_CHANNEL_NAME, PROTOCOL_CHAT_CHANNEL_NAME, PROTOCOL_MESHDROP_CHANNEL_NAME } from 'bare-os-protocol/constants.js' import { bareOsChatMuxEnabled } from './bare-os-chat-service.js' import { bareOsMeshdropMuxEnabled } from './bare-os-meshdrop-service.js' import { bareOsHostBooterInfo, bareOsHostBooterWarn } from './bare-os-host-booter-log.js' import { getKernelCapabilityWords } from 'bare-os-protocol' /** * Host-side booter log for swarm disk (stderr JSON when BARE_OS_BOOT_TRACE=json|ndjson; else stderr or console.warn). * @param {'info'|'warn'} level * @param {string} message * @param {Record | null} [detail] */ function emitSwarmDiskHostLog(level, message, detail = null) { const env = globalThis.process?.env const trace = env && (env.BARE_OS_BOOT_TRACE === 'json' || env.BARE_OS_BOOT_TRACE === 'ndjson') const err = globalThis.process?.stderr if (trace && err && typeof err.write === 'function') { err.write( `${JSON.stringify({ type: 'booterHost', bootTraceSchemaVersion: 2, component: 'swarm_disk', level, message, detail, ts: Date.now() })}\n` ) } if (level === 'warn') { bareOsHostBooterWarn( 'swarm_disk', message, detail ? JSON.stringify(detail) : '' ) return } bareOsHostBooterInfo( 'swarm_disk', message, detail ? JSON.stringify(detail) : '' ) } /** Default max wait for block 0 (MBR) before failing boot (override **`BARE_OS_MBR_READ_TIMEOUT_MS`**). */ const MBR_READ_TIMEOUT_MS_DEFAULT = 60_000 /** Cap-channel (`bare-os-cap-v1`) message size bound when **`BARE_OS_PROTOMUX_CAP_CHANNEL`** is enabled. */ export const BARE_OS_PROTOMUX_CAP_CHANNEL_MESSAGE_MAX_BYTES = 65536 /** * Max wait (ms) for block 0 / MBR replication before boot failure. * Exported for microbench / unit introspection (same rules as booter). * @param {SwarmDisk} disk */ export function mbrReadTimeoutMsForDisk(disk) { const env = globalThis.process?.env const raw = String(env?.BARE_OS_MBR_READ_TIMEOUT_MS ?? '').trim() if (raw) { const n = Math.floor(Number(raw)) if (Number.isFinite(n) && n >= 3000) return Math.min(n, 600_000) } const adapt = env?.BARE_OS_MBR_READ_TIMEOUT_ADAPTIVE === '1' || env?.BARE_OS_MBR_READ_TIMEOUT_ADAPTIVE === 'true' if (adapt && disk && disk.peers && typeof disk.peers.size === 'number') { const n = disk.peers.size if (n <= 1) return Math.min(120_000, MBR_READ_TIMEOUT_MS_DEFAULT + 30_000) if (n >= 4) return Math.max(45_000, MBR_READ_TIMEOUT_MS_DEFAULT - 15_000) } return MBR_READ_TIMEOUT_MS_DEFAULT } /** * Interval (ms) to re-send pending block read requests to all peers while waiting for MBR / block data. * **`0`** disables periodic rebroadcast (initial + per-connect sends only). * Override **`BARE_OS_MBR_READ_REBROADCAST_MS`** (defaults **5000**, clamp **2000**–**60000**). * @param {Record | null | undefined} env */ export function mbrReadRebroadcastMsFromEnv(env) { const raw = String(env?.BARE_OS_MBR_READ_REBROADCAST_MS ?? '').trim() if (raw === '0' || raw.toLowerCase() === 'false') return 0 const n = Math.floor(Number(raw)) if (Number.isFinite(n) && n >= 0) { if (n === 0) return 0 return Math.min(60_000, Math.max(2_000, n)) } return 5_000 } export class SwarmDisk { constructor() { this.localRAM = new Map() this.peers = new Set() this.pendingReads = new Map() this.pendingSearches = new Map() this.pendingRpc = new Map() this.searchIdCounter = 0 this.rpcIdCounter = 0 this.drive = null this.personalDrive = null this.os = null /** @type {import('hyperdrive').default[]} */ this.auxiliaryDrives = [] /** @type {Record | null} Last `bare_os.capabilities` RPC result (or error object). */ this.seedCapabilityInfo = null /** @type {Record | null} Last `bare_os.replication_status` RPC result when available. */ this.seedReplicationStatus = null /** @type {Record | null} */ this.seedManifestHints = null /** @type {Record | null} */ this.seedPeerHealth = null /** @type {Record | null} */ this.seedStagingSlot = null /** @type {Record | null} */ this.seedReplicationQueue = null /** @type {Record | null} */ this.seedCapabilityAttestation = null /** @type {Record | null} */ this.seedMbrLayout = null /** @type {Record | null} */ this.seedSnapshotHints = null /** @type {Record | null} */ this.seedPeerFirewallStats = null /** @type {Record | null} */ this.seedReplicationPlan = null /** @type {Record | null} */ this.seedDhtBootstrapHint = null /** @type {Record | null} */ this.seedSnapshotChain = null /** @type {Record | null} */ this.seedMirrorCompactionHint = null /** @type {Record | null} */ this.seedUpdaterState = null /** @type {Record | null} */ this.seedBlindPeerTopologyV2 = null /** @type {Record | null} */ this.seedCompactPing = null /** @type {Record | null} */ this.seedCorestoreStats = null /** @type {Record | null} */ this.seedSnapshotManifestSlice = null /** @type {Record | null} */ this.seedMirrorDriveHintV2 = null /** @type {Record | null} */ this.seedHrpcRegistrySummary = null /** @type {Record | null} */ this.seedProtomuxCapabilityAd = null /** Cumulative count of buffers received on optional Protomux app-channel pairs (operator observability). */ this.protomuxAppChannelRxTotal = 0 /** Cumulative count of buffers received on optional Protomux cap-channel pairs (`bare-os-cap-v1`). */ this.protomuxCapChannelRxTotal = 0 /** Cumulative chat `event` messages received on `bare-os-chat-v1` (when enabled). */ this.protomuxChatChannelRxTotal = 0 /** Cumulative meshdrop envelopes received on `bare-os-meshdrop-v1` (when enabled). */ this.protomuxMeshdropChannelRxTotal = 0 /** @type {ReturnType | null} */ this.bareOsChatService = null /** @type {ReturnType | null} */ this.bareOsMeshdropService = null /** @type {Record | null} Host-requested Hyperswarm caps (from env); surfaced on disk.os RPC. */ this.swarmConnectionBudget = null /** @type {Record | null} */ this.seedDhtAddressBook = null /** @type {Record | null} */ this.seedReplicationThrottleHint = null /** @type {Record | null} */ this.seedBundlebeeStage = null /** @type {Record | null} */ this.seedHttpDhtProxyHint = null /** @type {Record | null} */ this.seedProtomuxRpcPoolHint = null /** @type {Record | null} */ this.seedHyperblobStoreHint = null /** @type {Record | null} */ this.seedSigningRequestQueueHint = null /** @type {Record | null} */ this.seedCoreStorageLayoutHint = null /** @type {Record | null} */ this.seedMirrorDriveCompactionV3 = null /** @type {Record | null} */ this.seedBundlebeeCliStage = null /** @type {Record | null} */ this.seedReadyGuardV2 = null /** @type {Record | null} */ this.seedBlindRelayCircuitHint = null /** @type {Record | null} */ this.seedHttpDhtProxyRoutes = null /** @type {string[]} MBR-derived drive key hex list (primary + failovers). */ this.mbrKeysHex = [] /** * 512-byte MBR block used at boot (copy); enables peer block-0 service when mirrored to {@link SwarmDisk#localRAM}. * @type {Uint8Array | null} */ this.bootMbr512 = null /** True after peer system seed eligibility passed and MBR was published to `localRAM`. */ this.peerSystemSeedActive = false /** * Local Noise wire static key (32 bytes), last seen from `Hyperswarm` socket `publicKey`. * Peers validate chat `senderPk` against `socket.remotePublicKey`, which matches **this** — not `swarm.keyPair.publicKey`. */ this.localNoiseWirePk = /** @type {Uint8Array | null} */ (null) } /** * One JSON-RPC-style round-trip to a specific peer. * @param {unknown} peer * @param {string} module * @param {string} method * @param {string[]} args * @param {number} timeoutMs * @returns {Promise} */ _rpcOne(peer, module, method, args, timeoutMs) { const id = this.rpcIdCounter++ return new Promise((resolve, reject) => { const to = setTimeout(() => { this.pendingRpc.delete(id) reject(new Error('swarm-disk rpc: timeout')) }, timeoutMs) this.pendingRpc.set(id, (m) => { clearTimeout(to) if (m.success) { const r = m.result || '' try { resolve(r ? JSON.parse(r) : null) } catch { resolve(r) } } else { reject(new Error(m.error || 'swarm-disk rpc failed')) } }) const chan = /** @type {{ chan: { messages: { send: (m: unknown) => void }[] } }} */ ( peer ).chan void chan.fullyOpened().then((opened) => { if (!opened) return if (!this.pendingRpc.has(id)) return try { chan.messages[5].send({ id, module, method, args }) } catch (err) { this.pendingRpc.delete(id) clearTimeout(to) reject( err instanceof Error ? err : new Error((err && err.message) || String(err)) ) } }) }) } /** * JSON-RPC-style call to swarm peers. For **`bare_os`**, tries each peer in turn when * **`BARE_OS_SWARM_RPC_TRY_PEERS`** is not **`0`/`false`** (default: try all). * **`capabilities`** responses must include **`kernelCapabilityWords`** (wire v2). * @param {string} module * @param {string} method * @param {string[]} [args] * @param {number} [timeoutMs] * @returns {Promise} */ async rpc(module, method, args = [], timeoutMs = 8000) { if (!this.peers.size) throw new Error('swarm-disk rpc: no peers') const peers = [...this.peers] const mod = String(module || '') const meth = String(method || '') const env = globalThis.process?.env const tryAll = String(env?.BARE_OS_SWARM_RPC_TRY_PEERS ?? '1').toLowerCase() !== '0' && String(env?.BARE_OS_SWARM_RPC_TRY_PEERS ?? '1').toLowerCase() !== 'false' && mod === 'bare_os' && peers.length > 1 const perPeerMs = tryAll ? Math.min( 2500, Math.max(800, Math.floor(timeoutMs / Math.min(peers.length, 4))) ) : timeoutMs if (tryAll) { /** @type {Error | null} */ let lastErr = null for (const peer of peers) { try { const r = await this._rpcOne(peer, mod, meth, args, perPeerMs) if (meth === 'capabilities') { const words = getKernelCapabilityWords(r) if (!words) { lastErr = new Error( 'swarm-disk rpc: capabilities missing kernelCapabilityWords' ) continue } } return r } catch (e) { lastErr = e instanceof Error ? e : new Error((e && e.message) || String(e)) } } throw lastErr || new Error('swarm-disk rpc: all peers failed') } return this._rpcOne(peers[0], mod, meth, args, timeoutMs) } /** * @param {import('corestore').default} store * @param {import('hyperswarm').default} swarm * @param {import('hyperdrive').default} Hyperdrive */ async initPersonalDrive(store, swarm, Hyperdrive) { const localStore = store.namespace('bare-os-personal-v1', { writable: true }) this.personalDrive = new Hyperdrive(localStore) await this.personalDrive.ready() if (!this.personalDrive.writable) { try { await this.personalDrive.close() } catch (_) {} this.personalDrive = new Hyperdrive(localStore) await this.personalDrive.ready() } if (!this.personalDrive.writable) { emitSwarmDiskHostLog( 'warn', '[bare-os-booter] Personal Hyperdrive is not writable — identity and $HOME writes will fail. Check Corestore path permissions and that no other process holds the store read-only.', { writable: false, idPrefix: b4a.toString(this.personalDrive.id, 'hex').slice(0, 16) } ) } emitSwarmDiskHostLog( 'info', 'Personal Hyperdrive mounted; joining swarm discovery', { idPrefix: b4a.toString(this.personalDrive.id, 'hex').slice(0, 16), writable: this.personalDrive.writable } ) swarm.join(this.personalDrive.discoveryKey) } addPeer(mux, socket) { const disk = this /** @type {any} */ let chan const context = { onread(index) { const data = disk.localRAM.get(index) if (data) chan.messages[1].send({ index, data }) }, ondata(m) { const cb = disk.pendingReads.get(m.index) if (cb) { disk.pendingReads.delete(m.index) cb(m.data) } }, ongossip() {}, async onsearchreq(m) { const matches = disk.os ? await disk.os.searchLocal?.(m.query) : [] chan.messages[4].send({ id: m.id, matches: matches || [] }) }, onsearchres(m) { const st = disk.pendingSearches.get(m.id) if (!st || st.settled) return st.results.push(m.matches || []) st.remaining-- if (st.remaining <= 0) st.finish() }, async onrpcreq(m) { if (!disk.os) { chan.messages[6].send({ id: m.id, success: false, result: '', error: 'OS not initialized' }) return } try { const result = await disk.os.execRpc?.(m.module, m.method, m.args) chan.messages[6].send({ id: m.id, success: true, result: String(result ?? ''), error: '' }) } catch (err) { chan.messages[6].send({ id: m.id, success: false, result: '', error: err.message }) } }, onrpcres(m) { const cb = disk.pendingRpc.get(m.id) if (cb) { disk.pendingRpc.delete(m.id) cb(m) } } } chan = mux.createChannel({ protocol: PROTOCOL_NAME, userData: context }) chan.addMessage({ encoding: c.uint32, onmessage: (index, ch) => ch.userData.onread(index) }) chan.addMessage({ encoding: { preencode(state, m) { c.uint32.preencode(state, m.index) c.buffer.preencode(state, m.data) }, encode(state, m) { c.uint32.encode(state, m.index) c.buffer.encode(state, m.data) }, decode(state) { return { index: c.uint32.decode(state), data: c.buffer.decode(state) } } }, onmessage: (m, ch) => ch.userData.ondata(m) }) chan.addMessage({ encoding: c.buffer, onmessage: (bitfield, ch) => ch.userData.ongossip(bitfield) }) chan.addMessage({ encoding: { preencode(state, m) { c.uint32.preencode(state, m.id) c.string.preencode(state, m.query) }, encode(state, m) { c.uint32.encode(state, m.id) c.string.encode(state, m.query) }, decode(state) { return { id: c.uint32.decode(state), query: c.string.decode(state) } } }, onmessage: (m, ch) => ch.userData.onsearchreq(m) }) chan.addMessage({ encoding: { preencode(state, m) { c.uint32.preencode(state, m.id) c.array(c.string).preencode(state, m.matches) }, encode(state, m) { c.uint32.encode(state, m.id) c.array(c.string).encode(state, m.matches) }, decode(state) { return { id: c.uint32.decode(state), matches: c.array(c.string).decode(state) } } }, onmessage: (m, ch) => ch.userData.onsearchres(m) }) chan.addMessage({ encoding: { preencode(state, m) { c.uint32.preencode(state, m.id) c.string.preencode(state, m.module) c.string.preencode(state, m.method) c.array(c.string).preencode(state, m.args) }, encode(state, m) { c.uint32.encode(state, m.id) c.string.encode(state, m.module) c.string.encode(state, m.method) c.array(c.string).encode(state, m.args) }, decode(state) { return { id: c.uint32.decode(state), module: c.string.decode(state), method: c.string.decode(state), args: c.array(c.string).decode(state) } } }, onmessage: (m, ch) => ch.userData.onrpcreq(m) }) chan.addMessage({ encoding: { preencode(state, m) { c.uint32.preencode(state, m.id) c.bool.preencode(state, m.success) c.string.preencode(state, m.result) c.string.preencode(state, m.error) }, encode(state, m) { c.uint32.encode(state, m.id) c.bool.encode(state, m.success) c.string.encode(state, m.result || '') c.string.encode(state, m.error || '') }, decode(state) { return { id: c.uint32.decode(state), success: c.bool.decode(state), result: c.string.decode(state), error: c.string.decode(state) } } }, onmessage: (m, ch) => ch.userData.onrpcres(m) }) chan.open() const appChOn = globalThis.process && globalThis.process.env && (globalThis.process.env.BARE_OS_PROTOMUX_APP_CHANNEL === '1' || globalThis.process.env.BARE_OS_PROTOMUX_APP_CHANNEL === 'true') if (appChOn) { mux.pair({ protocol: PROTOCOL_APP_CHANNEL_NAME }, (achan) => { achan.addMessage({ encoding: c.buffer, onmessage: () => { try { this.protomuxAppChannelRxTotal++ } catch { /* ignore */ } } }) achan.open() }) } const capChOn = globalThis.process && globalThis.process.env && (globalThis.process.env.BARE_OS_PROTOMUX_CAP_CHANNEL === '1' || globalThis.process.env.BARE_OS_PROTOMUX_CAP_CHANNEL === 'true') if (capChOn) { mux.pair({ protocol: PROTOCOL_CAP_CHANNEL_NAME }, (cchan) => { cchan.addMessage({ encoding: c.buffer, onmessage: (buf) => { try { const n = buf ? buf.byteLength : 0 if (n > BARE_OS_PROTOMUX_CAP_CHANNEL_MESSAGE_MAX_BYTES) { emitSwarmDiskHostLog('warn', 'protomux_cap_payload_oversized', { byteLength: n, maxBytes: BARE_OS_PROTOMUX_CAP_CHANNEL_MESSAGE_MAX_BYTES }) return } this.protomuxCapChannelRxTotal++ } catch { /* ignore */ } } }) cchan.open() }) } const peer = { chan, mux, socket, id: null, chatChan: null, meshdropChan: null } if ( bareOsChatMuxEnabled(globalThis.process?.env) && this.bareOsChatService && mux.stream && !mux.stream.destroyed ) { try { this.bareOsChatService.pairOnMux(this, mux, socket, peer) } catch (e) { emitSwarmDiskHostLog('warn', 'bare_os_chat_pair_on_connect_failed', { message: (e && /** @type {{ message?: string }} */ (e).message) || String(e) }) } } if ( bareOsMeshdropMuxEnabled(globalThis.process?.env) && this.bareOsMeshdropService && mux.stream && !mux.stream.destroyed ) { try { this.bareOsMeshdropService.pairOnMux(this, mux, socket, peer) } catch (e) { emitSwarmDiskHostLog( 'warn', 'bare_os_meshdrop_pair_on_connect_failed', { message: (e && /** @type {{ message?: string }} */ (e).message) || String(e) } ) } } /** Sync local Noise static key once the secret stream exposes `publicKey` (may follow handshake). */ const cacheLocalNoiseWirePk = () => { if (socket.publicKey && socket.publicKey.byteLength === 32) { disk.localNoiseWirePk = b4a.from(socket.publicKey) } } cacheLocalNoiseWirePk() this.peers.add(peer) const pendingIndices = [...this.pendingReads.keys()] for (const idx of pendingIndices) { this._sendReadIndexToPeer(peer, idx) } const collabNd = globalThis.process && globalThis.process.env && (globalThis.process.env.BARE_OS_COLLAB_SESSION_NDJSON === '1' || globalThis.process.env.BARE_OS_COLLAB_SESSION_NDJSON === 'true') if (collabNd) { emitSwarmDiskHostLog('info', 'collab_session_peer', { peerCount: this.peers.size, capChannelEnabled: capChOn, appChannelEnabled: appChOn, note: 'Host NDJSON preview; mirror to personal-drive tooling off-guest if policy allows.' }) } const setPeerId = () => { if (socket.remotePublicKey && !peer.id) { peer.id = b4a.toString(socket.remotePublicKey, 'hex') return true } if (socket.handshakeHash && !peer.id) { peer.id = b4a.toString(socket.handshakeHash, 'hex') return true } return false } socket.on('handshake', () => { setPeerId() cacheLocalNoiseWirePk() }) if (!setPeerId()) { let attempts = 0 const tryAgain = () => { attempts++ if (attempts > 10) { if (!peer.id) peer.id = 'peer-' + Date.now().toString(36) return } if (!setPeerId()) setTimeout(tryAgain, attempts * 50) } setTimeout(tryAgain, 50) } mux.stream.on('close', () => { this.peers.delete(peer) }) if (this.drive) this.drive.replicate(mux.stream, { live: true, download: true }) if (this.personalDrive) this.personalDrive.replicate(mux.stream) for (const d of this.auxiliaryDrives || []) { try { d.replicate(mux.stream, { live: true, download: true }) } catch (_) {} } } /** * Protomux message 0: request block index (MBR). Only send after channel pairing. * @param {unknown} peer * @param {number} index */ _sendReadIndexToPeer(peer, index) { const chan = /** @type {{ chan: { messages: { send: (idx: number) => void }[] } }} */ ( peer ).chan void chan.fullyOpened().then((opened) => { if (!opened) return if (!this.pendingReads.has(index)) return try { chan.messages[0].send(index) } catch { /* ignore — channel closed or not ready */ } }) } /** * Re-broadcast a pending block read to every connected peer (lossy links, slow pairing). * @param {number} index */ _broadcastPendingReadIndex(index) { for (const p of this.peers) { this._sendReadIndexToPeer(p, index) } } async read(index) { if (this.localRAM.has(index)) return this.localRAM.get(index) return new Promise((resolve, reject) => { const timeoutMs = mbrReadTimeoutMsForDisk(this) /** @type {ReturnType | null} */ let rebroadcastIv = null const timeout = setTimeout(() => { if (rebroadcastIv) clearInterval(rebroadcastIv) this.pendingReads.delete(index) reject(new Error('MBR read timeout')) }, timeoutMs) this.pendingReads.set(index, (data) => { if (rebroadcastIv) clearInterval(rebroadcastIv) clearTimeout(timeout) resolve(data) }) const rbMs = mbrReadRebroadcastMsFromEnv(globalThis.process?.env) this._broadcastPendingReadIndex(index) if (rbMs > 0) { rebroadcastIv = setInterval(() => { if (!this.pendingReads.has(index)) { if (rebroadcastIv) clearInterval(rebroadcastIv) return } this._broadcastPendingReadIndex(index) }, rbMs) } }) } async search(query) { const id = this.searchIdCounter++ const peerList = [...this.peers] const n = peerList.length if (!n) return [] const self = this return new Promise((resolve) => { /** @type {unknown[][]} */ const results = [] const state = { remaining: n, results, settled: false, timeout: /** @type {ReturnType | null} */ (null), finish() { if (state.settled) return state.settled = true if (state.timeout) clearTimeout(state.timeout) self.pendingSearches.delete(id) resolve(results.flat()) } } state.timeout = setTimeout(() => state.finish(), 3000) self.pendingSearches.set(id, state) for (const peer of peerList) { void peer.chan.fullyOpened().then((opened) => { if (!opened) return if (state.settled) return try { peer.chan.messages[3].send({ id, query }) } catch { /* ignore */ } }) } }) } /** * (Re)pair `bare-os-chat-v1` on every live mux — used after `ensureDiskBareOsChatTransport` replaces * the service (guest → user, logout → guest) so `peer.chatChan` and handlers match the active instance. */ async pairBareOsChatExistingPeers() { if (!bareOsChatMuxEnabled(globalThis.process?.env)) return const svc = this.bareOsChatService if (!svc || typeof svc.pairOnMux !== 'function') return for (const peer of [...this.peers]) { const st = peer.mux && peer.mux.stream if (!st || st.destroyed) continue try { const mux = peer.mux const wired = peer.chatChan /** @type {Promise[]} */ const closing = [] if (wired && typeof wired.close === 'function') { try { wired.close() if (typeof wired.fullyClosed === 'function') closing.push(wired.fullyClosed()) } catch { /* ignore */ } } peer.chatChan = null const prevCh = mux && typeof mux.getLastChannel === 'function' && mux.getLastChannel({ protocol: PROTOCOL_CHAT_CHANNEL_NAME }) if (prevCh && typeof prevCh.close === 'function' && prevCh !== wired) { try { prevCh.close() if (typeof prevCh.fullyClosed === 'function') closing.push(prevCh.fullyClosed()) } catch { /* ignore */ } } if (closing.length) await Promise.all(closing) } catch { /* ignore */ } try { if (typeof peer.mux.unpair === 'function') { try { peer.mux.unpair({ protocol: PROTOCOL_CHAT_CHANNEL_NAME }) } catch { /* ignore */ } } svc.pairOnMux(this, peer.mux, peer.socket, peer) } catch (e) { emitSwarmDiskHostLog('warn', 'bare_os_chat_late_pair_failed', { message: (e && /** @type {{ message?: string }} */ (e).message) || String(e) }) } } } /** * (Re)pair `bare-os-meshdrop-v1` on every live mux — used after meshdrop service env/user transitions. */ pairBareOsMeshdropExistingPeers() { if (!bareOsMeshdropMuxEnabled(globalThis.process?.env)) return const svc = this.bareOsMeshdropService if (!svc || typeof svc.pairOnMux !== 'function') return for (const peer of [...this.peers]) { const st = peer.mux && peer.mux.stream if (!st || st.destroyed) continue try { const mux = peer.mux const wired = peer.meshdropChan if (wired && typeof wired.close === 'function') { try { wired.close() } catch { /* ignore */ } } peer.meshdropChan = null const prevCh = mux && typeof mux.getLastChannel === 'function' && mux.getLastChannel({ protocol: PROTOCOL_MESHDROP_CHANNEL_NAME }) if (prevCh && typeof prevCh.close === 'function' && prevCh !== wired) { try { prevCh.close() } catch { /* ignore */ } } } catch { /* ignore */ } try { svc.pairOnMux(this, peer.mux, peer.socket, peer) if (typeof peer.mux.unpair === 'function') { try { peer.mux.unpair({ protocol: PROTOCOL_MESHDROP_CHANNEL_NAME }) } catch { /* ignore */ } } } catch (e) { emitSwarmDiskHostLog('warn', 'bare_os_meshdrop_late_pair_failed', { message: (e && /** @type {{ message?: string }} */ (e).message) || String(e) }) } } } }