Files
openclaw/docs/gateway/bonjour.md

178 lines
6.2 KiB
Markdown
Raw Normal View History

---
summary: "Bonjour/mDNS discovery + debugging (Gateway beacons, clients, and common failure modes)"
read_when:
- Debugging Bonjour discovery issues on macOS/iOS
- Changing mDNS service types, TXT records, or discovery UX
title: "Bonjour Discovery"
---
2026-01-31 21:13:13 +09:00
# Bonjour / mDNS discovery
2026-01-30 03:15:10 +01:00
OpenClaw uses Bonjour (mDNS / DNSSD) as a **LANonly convenience** to discover
an active Gateway (WebSocket endpoint). It is besteffort and does **not** replace SSH or
2026-01-08 23:06:56 +01:00
Tailnet-based connectivity.
2026-01-08 23:06:56 +01:00
## Widearea Bonjour (Unicast DNSSD) over Tailscale
2026-01-08 23:06:56 +01:00
If the node and gateway are on different networks, multicast mDNS wont cross the
boundary. You can keep the same discovery UX by switching to **unicast DNSSD**
("WideArea Bonjour") over Tailscale.
2026-01-08 23:06:56 +01:00
Highlevel steps:
2026-01-31 21:13:13 +09:00
1. Run a DNS server on the gateway host (reachable over Tailnet).
2. Publish DNSSD records for `_openclaw-gw._tcp` under a dedicated zone
2026-01-30 03:15:10 +01:00
(example: `openclaw.internal.`).
2026-01-31 21:13:13 +09:00
3. Configure Tailscale **split DNS** so your chosen domain resolves via that
2026-01-08 23:06:56 +01:00
DNS server for clients (including iOS).
2026-01-30 03:15:10 +01:00
OpenClaw supports any discovery domain; `openclaw.internal.` is just an example.
iOS/Android nodes browse both `local.` and your configured widearea domain.
### Gateway config (recommended)
```json5
{
gateway: { bind: "tailnet" }, // tailnet-only (recommended)
2026-01-31 21:13:13 +09:00
discovery: { wideArea: { enabled: true } }, // enables wide-area DNS-SD publishing
}
```
2026-01-08 23:06:56 +01:00
### Onetime DNS server setup (gateway host)
```bash
2026-01-30 03:15:10 +01:00
openclaw dns setup --apply
```
This installs CoreDNS and configures it to:
2026-01-31 21:13:13 +09:00
2026-01-08 23:06:56 +01:00
- listen on port 53 only on the gateways Tailscale interfaces
2026-01-30 03:15:10 +01:00
- serve your chosen domain (example: `openclaw.internal.`) from `~/.openclaw/dns/<domain>.db`
2026-01-08 23:06:56 +01:00
Validate from a tailnetconnected machine:
```bash
2026-01-30 03:15:10 +01:00
dns-sd -B _openclaw-gw._tcp openclaw.internal.
dig @<TAILNET_IPV4> -p 53 _openclaw-gw._tcp.openclaw.internal PTR +short
```
### Tailscale DNS settings
In the Tailscale admin console:
- Add a nameserver pointing at the gateways tailnet IP (UDP/TCP 53).
2026-01-30 03:15:10 +01:00
- Add split DNS so your discovery domain uses that nameserver.
2026-01-08 23:06:56 +01:00
Once clients accept tailnet DNS, iOS nodes can browse
2026-01-30 03:15:10 +01:00
`_openclaw-gw._tcp` in your discovery domain without multicast.
### Gateway listener security (recommended)
The Gateway WS port (default `18789`) binds to loopback by default. For LAN/tailnet
access, bind explicitly and keep auth enabled.
2026-01-08 23:06:56 +01:00
For tailnetonly setups:
2026-01-31 21:13:13 +09:00
2026-01-30 03:15:10 +01:00
- Set `gateway.bind: "tailnet"` in `~/.openclaw/openclaw.json`.
2026-01-08 23:06:56 +01:00
- Restart the Gateway (or restart the macOS menubar app).
## What advertises
2026-01-30 03:15:10 +01:00
Only the Gateway advertises `_openclaw-gw._tcp`.
## Service types
2026-01-30 03:15:10 +01:00
- `_openclaw-gw._tcp` — gateway transport beacon (used by macOS/iOS/Android nodes).
2026-01-08 23:06:56 +01:00
## TXT keys (nonsecret hints)
2026-01-08 23:06:56 +01:00
The Gateway advertises small nonsecret hints to make UI flows convenient:
- `role=gateway`
2026-01-08 23:06:56 +01:00
- `displayName=<friendly name>`
- `lanHost=<hostname>.local`
- `gatewayPort=<port>` (Gateway WS + HTTP)
- `gatewayTls=1` (only when TLS is enabled)
- `gatewayTlsSha256=<sha256>` (only when TLS is enabled and fingerprint is available)
- `canvasPort=<port>` (only when the canvas host is enabled; currently the same as `gatewayPort`)
2026-01-08 23:06:56 +01:00
- `sshPort=<port>` (defaults to 22 when not overridden)
- `transport=gateway`
2026-01-30 03:15:10 +01:00
- `cliPath=<path>` (optional; absolute path to a runnable `openclaw` entrypoint)
2026-01-08 23:06:56 +01:00
- `tailnetDns=<magicdns>` (optional hint when Tailnet is available)
Security notes:
- Bonjour/mDNS TXT records are **unauthenticated**. Clients must not treat TXT as authoritative routing.
- Clients should route using the resolved service endpoint (SRV + A/AAAA). Treat `lanHost`, `tailnetDns`, `gatewayPort`, and `gatewayTlsSha256` as hints only.
- TLS pinning must never allow an advertised `gatewayTlsSha256` to override a previously stored pin.
- iOS/Android nodes should treat discovery-based direct connects as **TLS-only** and require explicit user confirmation before trusting a first-time fingerprint.
## Debugging on macOS
2026-01-08 23:06:56 +01:00
Useful builtin tools:
- Browse instances:
2026-01-08 23:06:56 +01:00
```bash
2026-01-30 03:15:10 +01:00
dns-sd -B _openclaw-gw._tcp local.
2026-01-08 23:06:56 +01:00
```
- Resolve one instance (replace `<instance>`):
2026-01-08 23:06:56 +01:00
```bash
2026-01-30 03:15:10 +01:00
dns-sd -L "<instance>" _openclaw-gw._tcp local.
2026-01-08 23:06:56 +01:00
```
2026-01-08 23:06:56 +01:00
If browsing works but resolving fails, youre usually hitting a LAN policy or
mDNS resolver issue.
## Debugging in Gateway logs
2026-01-08 23:06:56 +01:00
The Gateway writes a rolling log file (printed on startup as
`gateway log file: ...`). Look for `bonjour:` lines, especially:
2026-01-08 23:06:56 +01:00
- `bonjour: advertise failed ...`
- `bonjour: ... name conflict resolved` / `hostname conflict resolved`
2026-01-08 23:06:56 +01:00
- `bonjour: watchdog detected non-announced service ...`
2025-12-18 13:18:33 +01:00
## Debugging on iOS node
2026-01-30 03:15:10 +01:00
The iOS node uses `NWBrowser` to discover `_openclaw-gw._tcp`.
2026-01-08 23:06:56 +01:00
To capture logs:
2026-01-31 21:13:13 +09:00
- Settings → Gateway → Advanced → **Discovery Debug Logs**
- Settings → Gateway → Advanced → **Discovery Logs** → reproduce → **Copy**
2026-01-08 23:06:56 +01:00
The log includes browser state transitions and resultset changes.
## Common failure modes
2026-01-08 23:06:56 +01:00
- **Bonjour doesnt cross networks**: use Tailnet or SSH.
- **Multicast blocked**: some WiFi networks disable mDNS.
- **Sleep / interface churn**: macOS may temporarily drop mDNS results; retry.
- **Browse works but resolve fails**: keep machine names simple (avoid emojis or
punctuation), then restart the Gateway. The service instance name derives from
2026-01-08 23:06:56 +01:00
the host name, so overly complex names can confuse some resolvers.
## Escaped instance names (`\032`)
2026-01-08 23:06:56 +01:00
Bonjour/DNSSD often escapes bytes in service instance names as decimal `\DDD`
sequences (e.g. spaces become `\032`).
- This is normal at the protocol level.
2026-01-08 23:06:56 +01:00
- UIs should decode for display (iOS uses `BonjourEscapes.decode`).
## Disabling / configuration
2026-01-30 03:15:10 +01:00
- `OPENCLAW_DISABLE_BONJOUR=1` disables advertising (legacy: `OPENCLAW_DISABLE_BONJOUR`).
- `gateway.bind` in `~/.openclaw/openclaw.json` controls the Gateway bind mode.
- `OPENCLAW_SSH_PORT` overrides the SSH port advertised in TXT (legacy: `OPENCLAW_SSH_PORT`).
- `OPENCLAW_TAILNET_DNS` publishes a MagicDNS hint in TXT (legacy: `OPENCLAW_TAILNET_DNS`).
- `OPENCLAW_CLI_PATH` overrides the advertised CLI path (legacy: `OPENCLAW_CLI_PATH`).
## Related docs
2026-01-10 14:51:21 -06:00
- Discovery policy and transport selection: [Discovery](/gateway/discovery)
- Node pairing + approvals: [Gateway pairing](/gateway/pairing)