mDNS / Avahi Service Discovery
WendyOS uses Avahi to broadcast its presence over mDNS (Multicast DNS, RFC 6762) so that the wendy-agent on the host can discover the device without knowing its IP address in advan
mDNS / Avahi Service Discovery
WendyOS uses Avahi to broadcast its presence over mDNS (Multicast DNS, RFC 6762) so that the wendy-agent on the host can discover the device without knowing its IP address in advance. The device announces a _wendyos._udp service that carries the device UUID, name, and display name as DNS-SD TXT records.
Service Advertisement
The Avahi service definition is installed at /etc/avahi/services/wendyos-mdns.service:
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h</name>
<service protocol="any">
<type>_wendyos._udp</type>
<port>50051</port>
<txt-record>id=WENDY_DEVICE_ID</txt-record>
<txt-record>name=WENDY_DEVICE_NAME</txt-record>
<txt-record>displayname=WENDY_DISPLAY_NAME</txt-record>
</service>
</service-group>The placeholder tokens (WENDY_DEVICE_ID etc.) are replaced during boot. On
later boots, the same updater replaces the current TXT record values again; it
is not limited to the original placeholders (see below).
- Service type:
_wendyos._udp - Port:
50051pre-provisioning; updated to50052(mTLS) automatically once the device enrolls - Instance name: The device hostname (
%hexpands to the current hostname as set by Avahi) - TXT records:
id,name,displayname, plus provisioning records such astls,assetid, andorgidwhen applicable
Provisioning updates the port and may add tls, assetid, and orgid. Some
service templates also contain an optional fqdn record. The agent preserves
these records when it updates the hostname-derived TXT values.
Boot-time mDNS Identity Update
On every boot, wendyos-identity.service runs update-mdns-uuid.sh from
recipes-core/wendyos-identity/. The script reads
/etc/wendyos/device-uuid and /etc/wendyos/device-name, derives the title-case
display name, and replaces the current id, name, and displayname values in
the Avahi service file. Matching records by key makes this update idempotent; it
is not a one-time placeholder replacement. The equivalent data flow is:
UUID=$(cat /etc/wendyos/device-uuid)
DEVICE_NAME=$(cat /etc/wendyos/device-name 2>/dev/null || echo "unknown-device")
DISPLAY_NAME=$(echo "$DEVICE_NAME" | sed 's/-/ /g' | \
awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2));}1')
# Replace the value of each matching <txt-record>key=...</txt-record> entry.
set_txt id "$UUID"
set_txt name "$DEVICE_NAME"
set_txt displayname "$DISPLAY_NAME"The displayname field converts the hyphen-separated device name to Title Case (e.g. my-device becomes My Device).
If UUID or device-name files are not yet present, the script retries for up to
10 seconds to handle ordering races. After updating the file it calls
avahi-daemon --reload if the daemon is already running.
wendy device rename persists the operator-selected hostname in
/etc/wendy-agent/hostname and updates the Avahi records at rename time; it
does not overwrite the generated /etc/wendyos/device-name. Consequently, the
boot-time identity update above can replace name and displayname with the
generated device name. At agent startup, after wendyos-identity.service and
avahi-daemon.service, ReassertHostnameAdvertisement compares the service
file with the persisted explicit hostname and restores the hostname-derived TXT
records when needed. It restarts Avahi only when it changes the service file.
The same re-assertion repairs a fresh service file after an A/B OTA slot switch.
Hostname Generation
Avahi broadcasts the device hostname as <hostname>.local. The hostname is set by generate-hostname.sh via wendyos-hostname.service, which runs before avahi-daemon.service:
Resolution order:
/etc/wendy-agent/hostname— literal hostname set viawendy device rename(no prefix)/etc/wendyos/device-name— use as-is, lowercased, prefixed withwendyos-→wendyos-<device-name>.local/etc/wendyos/device-uuid— take the last 8 hex characters (without dashes) →wendyos-<8-char-uuid-suffix>.local- Legacy fallbacks: RPi serial from
/proc/cpuinfo, first 16 chars of/etc/machine-id, first MAC address, random hex
The hostname is written to /etc/hostname using a direct write (not hostnamectl, to avoid EBUSY issues with bind mounts) and also added to /etc/hosts as 127.0.1.1 <hostname> <hostname>.local.
Rename persistence: The hostname itself remains stable across reboots and OTA updates because
/etc/wendy-agent/hostnameis backed by/dataand has highest precedence above. The AvahinameanddisplaynameTXT records are separate rootfs state and can be re-derived from/etc/wendyos/device-nameduring boot. The agent re-asserts those records from the persistent explicit hostname at startup and does nothing on devices that have never been renamed or whose records are already current.
A hostname override can be applied by creating /etc/wendyos-hostname-override.
Avahi Configuration
The avahi_%.bbappend recipe configures /etc/avahi/avahi-daemon.conf at build time:
enable-dbus=yes
enable-reflector=no # reflector wedges startup in avahi 0.8 (bug WDY-755)
use-ipv4=yes
use-ipv6=yes
publish-addresses=yes
publish-hinfo=yes
publish-workstation=yes
publish-domain=yesThe reflector is explicitly disabled. A bug in avahi 0.8 causes the daemon to hang during startup when the reflector is enabled, regardless of the number of interfaces — services never get published.
NSS is configured to resolve .local names via mDNS by appending to nsswitch.conf:
hosts: files mdns4_minimal [NOTFOUND=return] mdns4 dnsHost-Side Discovery
Linux (avahi-browse)
# One-time scan
avahi-browse -t -p _wendyos._udp
# Continuous with full resolution
avahi-browse -r _wendyos._udp
# Example output:
# =;enx...;IPv4;wendyos-my-device;_wendyos._udp;local;wendyos-my-device.local;10.42.0.2;50051;"id=xxxxxxxx-..."macOS (dns-sd)
# Browse
dns-sd -B _wendyos._udp local.
# Resolve an instance
dns-sd -L "wendyos-my-device" _wendyos._udp local.wendy-agent Discovery Code
The wendy-agent Go code (internal/shared/discovery/) uses _wendyos._udp as the service type constant. On Linux it prefers avahi-browse -rptl _wendyos._udp when Avahi is installed; otherwise it falls back to the hashicorp/mdns library which queries each multicast-capable interface individually. On macOS it browses and resolves in-process through <dns_sd.h>, the same mDNSResponder daemon that the dns-sd tool wraps, so no helper processes are spawned.
CLI-side note: The shipped CLI binary is built with CGO_ENABLED=0, so it cannot use nss-mdns to resolve .local names. Instead, it performs its own mDNS browse for .local hostnames when connecting. Set WENDY_MDNS_DEBUG=1 to log browse failures, or WENDY_MDNS_TIMEOUT (1s–30s) to adjust the timeout.
Both the primary avahi-browse path and the hashicorp/mdns fallback path parse all TXT records into a key→value map, including the tls record.
TXT records are unauthenticated: they are advertised by whatever host answers on the network, and anyone who can reach the multicast segment can advertise _wendyos._udp with any TXT values it likes. They are used for routing and display only, never for trust. A device that advertises tls=true has IsMTLS set to true, which tells the CLI to dial the mTLS port (50052) instead of the plaintext one — that arithmetic is real and still applies — but it is not what decides whether the CLI trusts the host it ends up talking to. Trust is established afterward, from the certificate that host presents during the TLS handshake, checked against the identity pin recorded for the name being dialed (see the "Device identity pinning" section of pki/README.md). A spoofed TXT record can get a connection attempt redirected to the wrong port on the wrong host; it cannot get that host trusted — the certificate check and pin still have to pass. The fallback path also resolves wendyosdevice and displayname TXT records, matching the behaviour of the primary path.
IPv6 link-local addresses returned by mDNS are annotated with the zone ID (%<ifname>) so that the caller can use them directly in net.Dial() calls.
The MDNSService struct captures:
type MDNSService struct {
InstanceName string
Hostname string
IPAddress string // link-local IPv6 includes %zone suffix
Port int
TXTRecords map[string]string // all advertised TXT records
}TXT Record Reference
| Key | Value | Example |
|---|---|---|
id | Device UUID (from /etc/wendyos/device-uuid) | 550e8400-e29b-41d4-a716-446655440000 |
name | Slug device name (from /etc/wendyos/device-name) | my-device |
displayname | Title-cased device name | My Device |
fqdn | Optional reverse-DNS name derived from the hostname when present in the service template | sh.wendy.my-device |
wendyosdevice | Device UUID (preferred over id when present) | 769dc651-4eb2-49f3-b9f6-3e473f15694a |
tls | "true" when the device is provisioned and requires mTLS (port 50052) | true |
MAC Address Generation
WendyOS generates stable, deterministic MAC addresses for the USB NCM gadget rather than using random or hardware-burned addresses. This means the host always sees the same MAC for
Networking Troubleshooting
This guide covers common WendyOS USB networking problems, their symptoms, and how to diagnose and fix them.