fix(safesocket/js): use unique memconn name per IPN instance

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 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 20:02:24 +00:00
parent 6e83d5291b
commit 915dca44fe
+8 -1
View File
@@ -5,15 +5,22 @@ package safesocket
import ( import (
"context" "context"
"fmt"
"net" "net"
"sync/atomic"
"github.com/akutz/memconn" "github.com/akutz/memconn"
) )
const memName = "Tailscale-IPN" 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) { 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) { func connect(ctx context.Context, _ string) (net.Conn, error) {