Discovery & transports
Fased has two distinct problems that look similar on the surface:- Operator remote control: the macOS menu bar app controlling a gateway running elsewhere.
- Node pairing: iOS/Android (and future nodes) finding a gateway and pairing securely.
Terms
- Gateway: a single long-running gateway process that owns state (sessions, pairing, node registry) and runs channels. Most setups use one per host; isolated multi-gateway setups are possible.
- Gateway WS (control plane): the WebSocket endpoint on
127.0.0.1:18789by default; it can be bound to LAN or tailnet viagateway.bind, but loopback remains the recommended baseline. - Direct WS transport: a LAN/tailnet-facing Gateway WS endpoint (no SSH).
- SSH transport (fallback): remote control by forwarding
127.0.0.1:18789over SSH. - Legacy TCP bridge (deprecated/removed): older node transport (see Bridge protocol); no longer advertised for discovery.
Why we keep both “direct” and SSH
- Direct WS is the best UX on the same network and within a tailnet:
- auto-discovery on LAN via Bonjour
- pairing tokens + ACLs owned by the gateway
- no shell access required; protocol surface can stay tight and auditable
- SSH remains the universal fallback:
- works anywhere you have SSH access (even across unrelated networks)
- survives multicast/mDNS issues
- requires no new inbound ports besides SSH
Discovery inputs (how clients learn where the gateway is)
1) Bonjour / mDNS (LAN only)
Bonjour is best-effort and does not cross networks. It is only used for “same LAN” convenience. Target direction:- The gateway advertises its WS endpoint via Bonjour.
- Clients browse and show a “pick a gateway” list, then store the chosen endpoint.
Service beacon details
- Service types:
_fased-gw._tcp(gateway transport beacon)
- TXT keys (non-secret):
role=gatewaylanHost=<hostname>.localgatewayPort=18789(Gateway WS + HTTP)gatewayTls=1(only when TLS is enabled)gatewayTlsSha256=<sha256>(only when TLS is enabled and fingerprint is available)canvasPort=<port>(canvas host port; currently the same asgatewayPortwhen the canvas host is enabled)tailnetDns=<magicdns>(optional hint; auto-detected when Tailscale is available)sshPort=<port>(only indiscovery.mdns.mode: "full")cliPath=<path>(only indiscovery.mdns.mode: "full")
- Bonjour/mDNS TXT records are unauthenticated. Clients must treat TXT values as UX hints only.
- Routing (host/port) should prefer the resolved service endpoint (SRV + A/AAAA) over TXT-provided
lanHost,tailnetDns, orgatewayPort. - TLS pinning must never allow an advertised
gatewayTlsSha256to override a previously stored pin. - iOS/Android nodes should treat discovery-based direct connects as TLS-only and require an explicit “trust this fingerprint” confirmation before storing a first-time pin (out-of-band verification).
FASED_DISABLE_BONJOUR=1disables advertising.discovery.mdns.mode: "off"disables mDNS from config.discovery.mdns.mode: "minimal"is the default and omitssshPort/cliPath.discovery.mdns.mode: "full"includessshPort/cliPath.gateway.bindin~/.fased/fased.jsoncontrols the Gateway bind mode.FASED_SSH_PORToverrides the SSH port advertised in full TXT mode.FASED_TAILNET_DNSpublishes atailnetDnshint (MagicDNS).FASED_CLI_PATHoverrides the advertised CLI path in full TXT mode.
2) Tailnet (cross-network)
Once you leave one LAN, Bonjour stops helping. The preferred direct target is then:- Tailscale MagicDNS name (preferred) or a stable tailnet IP.
tailnetDns as an optional hint for clients (including wide-area beacons).
3) Manual / SSH target
When there is no direct route, or when you deliberately keep the gateway loopback-only, clients can always connect via SSH by forwarding the loopback gateway port. See Remote access.Transport selection (client policy)
Recommended client behavior:- If a paired direct endpoint is configured and reachable, use it.
- Else, if Bonjour finds a gateway on LAN, offer a one-tap “Use this gateway” choice and save it as the direct endpoint.
- Else, if a tailnet DNS/IP is configured, try direct.
- Else, fall back to SSH.
Pairing + auth (direct transport)
The gateway is the source of truth for node/client admission.- Pairing requests are created/approved/rejected in the gateway (see Gateway pairing).
- The gateway enforces:
- auth (token / keypair)
- scopes/ACLs (the gateway is not a raw proxy to every method)
- rate limits
Responsibilities by component
- Gateway: advertises discovery beacons, owns pairing decisions, and hosts the WS endpoint.
- macOS app: helps you pick a gateway, shows pairing prompts, and uses SSH only as a fallback.
- iOS/Android nodes: browse Bonjour as a convenience and connect to the paired Gateway WS.