From 2937fb237208c93d4b82e9f910bef1fdaa48e041 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Mon, 13 Apr 2026 16:55:07 -0700 Subject: [PATCH] Add mDNS republish logic with exponential backoff --- src/server/mdns.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/server/mdns.ts b/src/server/mdns.ts index 75897d8..3187eb6 100644 --- a/src/server/mdns.ts +++ b/src/server/mdns.ts @@ -4,7 +4,12 @@ import { LOCAL_HOST } from '%config' import { networkInterfaces } from 'os' import { hostLog } from './tui' +const MAX_REPUBLISH_DELAY = 30_000 +const REPUBLISH_BASE_DELAY = 1_000 + +const _killed = new Set() const _publishers = new Map() +const _republishAttempts = new Map() const isEnabled = process.env.NODE_ENV === 'production' && process.platform === 'linux' @@ -51,10 +56,12 @@ export function publishApp(name: string) { }) _publishers.set(name, proc) + _republishAttempts.delete(name) hostLog(`mDNS: published ${host} -> ${ip}`) proc.exited.then(() => { _publishers.delete(name) + if (!_killed.delete(name)) republish(name) }) } catch { hostLog(`mDNS: failed to publish ${host}`) @@ -64,9 +71,11 @@ export function publishApp(name: string) { export function unpublishApp(name: string) { if (!isEnabled) return + _republishAttempts.delete(name) const proc = _publishers.get(name) if (!proc) return + _killed.add(name) proc.kill() _publishers.delete(name) hostLog(`mDNS: unpublished ${toSubdomain(name)}.${LOCAL_HOST}`) @@ -75,9 +84,20 @@ export function unpublishApp(name: string) { export function unpublishAll() { if (!isEnabled) return + _republishAttempts.clear() for (const [name, proc] of _publishers) { + _killed.add(name) proc.kill() hostLog(`mDNS: unpublished ${toSubdomain(name)}.${LOCAL_HOST}`) } _publishers.clear() } + +function republish(name: string) { + const attempts = _republishAttempts.get(name) ?? 0 + const delay = Math.min(REPUBLISH_BASE_DELAY * 2 ** attempts, MAX_REPUBLISH_DELAY) + _republishAttempts.set(name, attempts + 1) + + hostLog(`mDNS: ${toSubdomain(name)}.${LOCAL_HOST} exited unexpectedly, retrying in ${delay}ms`) + setTimeout(() => publishApp(name), delay) +}