Documentation

Configuration

Everything TailMux does is driven by a single YAML file. Profiles own suffixes; the router turns those suffixes into routes.

View as Markdown

Location#

TailMux loads its config from ~/.config/tailmux/config.yaml. Generate a starter file with tailmux init and validate any edits before starting:

zsh — tailmux
$tailmux init$tailmux config validate

Use examples/tailmux.example.yaml as the canonical reference. A trimmed version:

config.yaml
1version: 12 3router:4  http_proxy: "127.0.0.1:43100"5  socks5_proxy: "127.0.0.1:43101"6  pac_listen: "127.0.0.1:43180"7  profile_http_proxy_base: 431108  profile_socks5_proxy_base: 431119  profile_udp_port_base: 0        # 0 lets tsnet choose UDP ports10  profile_hostname_base: "tailmux"11 12profiles:13  work:14    display_name: "Work"15    backend: "tsnet"16    auth_key_env: ""              # e.g. TAILMUX_WORK_AUTHKEY17    accept_routes: false18    suffixes:19      - ".acme-corp.ts.net"20      - ".corp.example.com"21    match_root: true22    ip_routes: []23 24  personal:25    display_name: "Home lab"26    backend: "tsnet"27    suffixes:28      - ".home-lab.ts.net"29      - ".home.example.com"30    match_root: true31 32security:33  require_loopback_listeners: true34  allow_cross_profile_fallback: false35  allow_ip_literals: false36  allow_unknown_tsnet: false

Router#

The router block defines the single public-facing endpoints and the per-profile bases:

  • http_proxy — the one TailMux HTTP/CONNECT proxy endpoint clients talk to.
  • socks5_proxy — SOCKS5 endpoint for domain CONNECT dispatch.
  • pac_listen — local endpoint serving /proxy.pac.
  • profile_*_base — bases used to derive each profile's internal runtime ports and hostname.
  • profile_udp_port_base — set 0 to let tsnet choose UDP ports, or a fixed base for more stable peer-to-peer path discovery.

Profiles#

Each entry under profiles is one tailnet identity. Key fields:

  • backendtsnet (recommended) or tailscaled.
  • display_name — human label shown in the GUI and menu bar.
  • auth_key_env — name of an environment variable holding an auth key for non-interactive login.
  • accept_routes — set true only for profile-owned app connectors or subnet routes.
  • control_url — optional custom control server.
  • ip_routes — advanced: explicit profile-owned IP/CIDR destinations for raw TCP tools. Keep empty unless the range cannot overlap any other profile.

Suffixes & ownership#

suffixes is the heart of routing: each profile owns a list of DNS suffixes. The router matches an incoming hostname against these lists and dispatches to the owning profile. With match_root: true, the bare apex of a suffix is also owned.

Every suffix must be owned by exactly one profile. Overlapping suffixes across profiles are rejected at validation time. This rejection is the mechanism that guarantees no cross-profile fallback can ever happen.

Backends#

tsnet (recommended) embeds a Tailscale node inside TailMux. TailMux starts one embedded server per profile, each with its own state directory, hostname, and node key. No external process, no second native VPN route.

tailscaled (compatibility) runs one external userspace tailscaled process per profile and forwards through profile-specific listeners. Keep it for comparison and low-level debugging — it is not the primary path.

Security block#

The security block makes the hard isolation constraints explicit and enforceable:

  • require_loopback_listeners: true — bind all proxy/control ports to loopback.
  • allow_cross_profile_fallback: false — never retry on a different tailnet.
  • allow_ip_literals: false — deny raw-IP destinations by default.
  • allow_unknown_tsnet: false — deny unclassified .ts.net hosts.
These default to the safe values. Changing them weakens isolation — only do so deliberately, per profile range that provably cannot overlap.