From 915dca44fe7691dc729e906af4e92f6a26e3c366 Mon Sep 17 00:00:00 2001 From: Codinget Date: Sat, 13 Jun 2026 20:02:24 +0000 Subject: [PATCH] fix(safesocket/js): use unique memconn name per IPN instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each call to newIPN() starts an independent Go backend that calls safesocket.Listen() to serve the ipnserver IPC channel. Because memName was a global constant, the second instance would fail with "addr unavailable" and log.Fatal the whole WASM process. Use an atomic counter to give each listener a distinct name (Tailscale-IPN-1, Tailscale-IPN-2, …). The connect() path is unchanged: in the wasm/tsconnect build all LocalAPI calls go through the in-process httptest handler, so connect() is never called. Co-Authored-By: Claude Sonnet 4.6 --- safesocket/safesocket_js.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/safesocket/safesocket_js.go b/safesocket/safesocket_js.go index 746fea511..0807885a8 100644 --- a/safesocket/safesocket_js.go +++ b/safesocket/safesocket_js.go @@ -5,15 +5,22 @@ package safesocket import ( "context" + "fmt" "net" + "sync/atomic" "github.com/akutz/memconn" ) const memName = "Tailscale-IPN" +// memSeq ensures each IPN instance in the same WASM process gets a distinct +// memconn address, so concurrent instances do not conflict on the registry. +var memSeq atomic.Int64 + func listen(path string) (net.Listener, error) { - return memconn.Listen("memu", memName) + name := fmt.Sprintf("%s-%d", memName, memSeq.Add(1)) + return memconn.Listen("memu", name) } func connect(ctx context.Context, _ string) (net.Conn, error) {