Skip to content

Conversation

@ThomasK33
Copy link
Member

Adds an opt-in Settings β†’ Experiments feature to expose mux's API server on LAN/VPN.

  • Persists bind host/port in ~/.mux/config.json (apiServerBindHost, apiServerPort).
  • Computes and surfaces networkBaseUrls for copy/paste (LAN/VPN IPs) + auth token.
  • Adds RPC to view/update settings and restart the HTTP/WS server.

Validation:

  • make static-check
  • bun test src/node/services/serverService.test.ts src/node/services/serverLockfile.test.ts src/node/config.test.ts

πŸ“‹ Implementation Plan

Configurable bind URL for the local API server (Experiment)

Answer (current behavior)

  • Electron desktop always binds the HTTP/WS API server to 127.0.0.1 (src/node/services/serverService.ts).
  • You can currently override:
    • MUX_SERVER_PORT (port; default is random)
    • MUX_SERVER_AUTH_TOKEN (auth token)
    • MUX_NO_API_SERVER=1 (disable the server)
  • The standalone CLI server supports --host, but the desktop app does not expose a bind-host setting today.

Consequence: if mux only listens on loopback, your phone (LAN) and VPN clients (e.g., Tailscale) can’t connect.


Goal

Add an opt-in experiment that lets users configure the bind host/interface (and ideally a stable port) for the desktop app’s HTTP/WS API server, with UI text that clearly explains:

  • enabling it makes mux listen beyond localhost,
  • other devices on your LAN/VPN can connect,
  • this has security implications.

Recommended approach (config-backed experiment + connection info)

Net estimate: ~350–500 LoC product code.

What the experiment does (user-facing)

When enabled, mux can bind its local API server to a non-loopback interface (e.g. 0.0.0.0 or a specific IP), making it reachable from:

  • devices on your local network (phone on the same Wi‑Fi), and
  • VPN interfaces (e.g. Tailscale).

It should also surface copy/paste connection info:

  • connect URL(s) for detected interfaces (LAN IP, Tailscale IP, etc.)
  • the auth token required to access /api + /orpc

Security/UX requirements

  • The experiment description must explicitly warn that:
    • devices on your local network can connect to your mux instance (with the token), and
    • requests are over unencrypted HTTP (token can be sniffed on untrusted networks).
  • Recommend: only enable on trusted networks and prefer Tailscale for encrypted transport.

Implementation plan

1) Add a new experiment entry (UI discoverability)

Files:

  • src/common/constants/experiments.ts

Steps:

  • Add a new ID, e.g. EXPERIMENT_IDS.CONFIGURABLE_BIND_URL = "configurable-bind-url".
  • Add a definition with clear copy, e.g.:
    • name: "Expose API server on LAN/VPN"
    • description (draft):
      • "Allow mux to listen on a non-localhost address so other devices on your LAN/VPN can connect. Anyone on your network with the auth token can access your mux API. HTTP only; use only on trusted networks (Tailscale recommended)."
    • enabledByDefault: false
    • userOverridable: true
    • showInSettings: true

2) Persist API server bind settings in ~/.mux/config.json

Why: main process needs this at app launch; localStorage isn’t available.

Files:

  • src/common/types/project.ts (extend ProjectsConfig)
  • src/node/config.ts (load/save)

Add config fields (proposal):

  • apiServerBindHost?: string (default: unset β†’ 127.0.0.1)
  • apiServerPort?: number (default: unset β†’ current behavior; env var still overrides)

Notes:

  • Keep behavior backward-compatible by treating missing fields as defaults.
  • Keep MUX_SERVER_PORT as highest-precedence override (useful for CI / power users).

3) Update server startup to respect the configured bind host

Files:

  • src/node/services/serverService.ts
  • src/desktop/main.ts

Steps:

  • Extend StartServerOptions to accept host?: string.

  • Pass host through to createOrpcServer({ host, port, ... }).

  • In src/desktop/main.ts, decide host/port in this precedence order:

    1. MUX_SERVER_PORT env var (existing)
    2. config apiServerPort
    3. default 0 (random)

    and for host:

    1. config apiServerBindHost
    2. default 127.0.0.1

4) Make connection URLs discoverable (LAN/Tailscale)

Today createOrpcServer rewrites wildcard bind hosts (0.0.0.0 / ::) to 127.0.0.1 for baseUrl, which is fine for the desktop app + CLI discovery.

To support phones/remote clients, add additional URLs rather than changing baseUrl:

Files:

  • src/node/services/serverLockfile.ts
  • src/node/services/serverService.ts
  • (optional) src/node/orpc/server.ts (only if we want server.ts to return more metadata)

Steps:

  • Extend ServerLockDataSchema to include optional fields, e.g.:
    • bindHost?: string
    • port?: number
    • networkBaseUrls?: string[] (derived from os.networkInterfaces(), filtered to non-internal addresses)
  • Update ServerLockfile.acquire(...) to accept and persist these optional fields.
  • In ServerService.startServer, after the server is listening (we know actualPort), compute:
    • networkBaseUrls for each relevant interface IP: http://<ip>:<port>
    • (optionally) include ws://<ip>:<port>/orpc/ws as well if needed later

5) Add backend RPC for viewing/updating bind settings (+ restarting server)

We want the experiment UI to be able to:

  • read current bind settings
  • update them
  • restart the HTTP/WS server so changes apply immediately

Files:

  • src/common/orpc/schemas/api.ts (add schemas under server)
  • src/node/orpc/router.ts (implement handlers)

Proposed API surface:

  • server.getApiServerStatus β†’ returns current:
    • running: boolean
    • baseUrl (loopback)
    • networkBaseUrls (if any)
    • token (so user can copy it for phone usage)
    • bindHost, port
  • server.setApiServerSettings β†’ persists config and restarts the HTTP server.

Restart semantics:

  • Restart only the HTTP/WS server (ServerService.stopServer() + startServer()); this should not break the Electron renderer since it uses MessagePort.
  • Defensive behavior:
    • Validate host string (non-empty) and port range before writing.
    • If restart fails, attempt to revert to the previous working settings.

6) Update Settings β†’ Experiments UI to include controls + warnings

Files:

  • src/browser/components/Settings/sections/ExperimentsSection.tsx

Approach:

  • Keep the experiment toggle in the experiments list, but for this specific ID render an β€œexpanded” row beneath it that:
    • chooses bind host:
      • 127.0.0.1 (localhost only)
      • 0.0.0.0 (all interfaces: LAN + VPN)
      • optional: β€œCustom…” text input
    • chooses port:
      • default β€œRandom (changes each start)” vs a numeric input
    • shows computed networkBaseUrls + a β€œCopy” button
    • shows the token + β€œCopy token” button
    • shows a bold warning blurb (see copy below)

Suggested warning copy (UI):

Exposes mux’s API server to your LAN/VPN. Devices on your local network can connect if they have the auth token. Traffic is unencrypted HTTP; enable only on trusted networks (Tailscale recommended).


Validation

  • Unit tests for:
    • ServerLockDataSchema backward compatibility (old lock files still parse)
    • network URL computation (filters out internal interfaces; stable ordering)
  • Manual checks:
    • With bind host 0.0.0.0 and fixed port, confirm phone can load http://<LAN-IP>:<port>/api/docs.
    • Confirm /api/* requests require the auth token.

Alternatives considered

A) Environment variable only (MUX_SERVER_HOST)

Net estimate: ~20–40 LoC.

  • Add MUX_SERVER_HOST env var read in src/desktop/main.ts and pass into ServerService.startServer.
  • No experiments UI.

Pros: trivial.
Cons: doesn’t satisfy β€œExperiments tab” UX + doesn’t help discoverability.

B) Make the existing experiments system config-backed

Net estimate: ~250–400 LoC in addition to the feature.

  • Today experiment overrides are stored in localStorage; main process can’t read them.
  • We could generalize experiments to optionally persist overrides in ~/.mux/config.json and expose them to both main + renderer.

Pros: more consistent long-term.
Cons: larger cross-cutting refactor than necessary for a single setting.


Generated with mux β€’ Model: openai:gpt-5.2 β€’ Thinking: xhigh

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Change-Id: Ie1b6293b5a41c8f7dbba3589e64357736d1ee3ee
Signed-off-by: Thomas Kosiewski <[email protected]>
Change-Id: I9f29384286bd75aa25000758c887f608df99d607
Signed-off-by: Thomas Kosiewski <[email protected]>
@ThomasK33 ThomasK33 force-pushed the configurable-bind-url branch from 9c50205 to ecf123c Compare December 22, 2025 14:56
Change-Id: I85049e30b2aa89f81782681cc0bba1154080594b
Signed-off-by: Thomas Kosiewski <[email protected]>
Change-Id: If5babd087a2ccd55402daf74c4128dc64107cd9b
Signed-off-by: Thomas Kosiewski <[email protected]>
Change-Id: I5436c9b73c6c4d46c4eb4584cc694fd25bc96fad
Signed-off-by: Thomas Kosiewski <[email protected]>
Change-Id: I6d189db020bdd8901aaedc21f1998fad40f1ceac
Signed-off-by: Thomas Kosiewski <[email protected]>
@ThomasK33 ThomasK33 added this pull request to the merge queue Dec 22, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to no response for status checks Dec 22, 2025
@ThomasK33 ThomasK33 added this pull request to the merge queue Dec 22, 2025
Merged via the queue into main with commit 21848f5 Dec 22, 2025
20 checks passed
@ThomasK33 ThomasK33 deleted the configurable-bind-url branch December 22, 2025 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant