mDNS (multicast DNS / DNS-SD) advertises services on the local network under *.local. names.
NetNeighbor uses zeroconf browsing callbacks; it does not perform an active LAN scan.
Typical service types discovered:
| Type | Meaning |
|---|---|
_http._tcp |
Web UI |
_smb._tcp, _microsoft-ds._tcp |
File sharing |
_ssh._tcp |
SSH |
_ftp._tcp |
FTP |
_telnet._tcp |
Telnet |
_esp3d._tcp |
ESP3D firmware |
| Area | File / symbol | Role |
|---|---|---|
| Provider | discovery/mdns.py |
MDNSDiscovery — zeroconf browser/listener, TXT decode, host aggregation |
| Contract | discovery/base.py |
BaseDiscovery._emit("device", payload) → normalized dict to manager |
| Orchestration | discovery/manager.py |
Receives mdns payloads, applies overrides, stores and notifies |
| Mapping hints | config/device_types.json |
Per-service defaults (type, icon, default_port) |
| UI payload | utils/details_payload.py |
build_mdns_payload — per-service sections for the Services tab |
| Rules | config/mdns_rules.json |
TXT → summary line mappings and optional type_rules |
- Service types from
config/device_types.json(mdnssection) are browsed immediately. - DNS-SD enumeration (
_services._dns-sd._udp) runs in background to catch unlisted types. - Enumeration repeats on a timer and on
refresh()— no types need to be listed statically.
mDNS announces per-service. NetNeighbor aggregates services to one logical host entry:
- Track per-service payloads
(service_type, instance_name) - Map each service to a host key (prefer IP, fallback hostname)
- Emit an aggregated payload with all services under
metadata["services"]
A device like ESP3D exposing both _esp3d._tcp and _telnet._tcp appears as one entry
with both services visible in the Details dialog.
A presentation URL is built only if _http._tcp is actually present. Port numbers alone do not imply HTTP — this avoids wrong browser links for non-HTTP services on common ports.
- Raw
ServiceInfo.textis decoded preserving duplicate keys and ordering →metadata["services"][].txt_records - A flat
metadata["txt"](last-key-wins) is kept for type heuristics - Details dialog shows one expandable row per service with TXT pairs nested under it
Type starts from device_types.json (per-service mapping), then refined entirely by the
type_rules in mdns_rules.json (first match wins). All classification heuristics
(fluidnc→cnc, laserjet→networkprinter, synology/qnap/nas→nas, 3D-printer/CNC keywords, …)
live in that JSON — there are no hardcoded type rules in mdns.py.
A host stays online while at least one tracked service remains. Removing one service updates the aggregated payload; offline is emitted only when all services are gone — avoids flapping for multi-service hosts.
When the last service disappears while the host is still otherwise alive, the removal is delayed by a 120 s grace period to absorb mDNS TTL jitter. A user-initiated refresh() reschedules any pending grace-removes to a short 15 s window, so a device that truly left the network disappears quickly instead of lingering for the full grace period.
Brutal disconnections (power loss, cable pull) emit no byebye and would otherwise persist until the mDNS TTL expires. The manager's periodic ICMP probe (see BACKEND_ARCHITECTURE.md) flips such hosts offline within ~180 s and drops non-monitored ones from the list.
Lets you map TXT keys onto summary lines and extend type classification without Python changes.
User overlay: ~/.config/netneighbor/mdns_rules.json — merged with the bundled file.
See COMMUNITY_OVERRIDES.md.
Single source of truth. This bundled file is the rules — there is no Python copy. The startup integrity check (
utils/config_integrity.py) refuses to launch with a "corrupted installation, please reinstall" dialog if it is missing or not valid JSON.
Ordered list; each entry emits at most one first-tab detail row.
| Field | Type | Meaning |
|---|---|---|
label |
string | Detail column heading (match SSDP wording to enable cross-protocol merge) |
keys |
list | TXT key aliases, priority order, case-insensitive |
Evaluated after fixed heuristics. First match wins.
| Field | Type | Meaning |
|---|---|---|
contains_any |
list | Lowercase substrings searched in service key, name, and TXT pairs |
type |
string | Internal type id (e.g. nas, router) |
- Add
summary_from_txtentries matching your devices' actual TXT keys (check Services tab in Details). - Use the same
labelas SSDP counterparts so SSDP+mDNS detail rows merge correctly. - Add
type_ruleswith specificcontains_anybefore generic ones. - Validate:
python -m json.tool config/mdns_rules.json - Restart the app (rules are cached at startup).
- Enable
mdnsin~/.config/netneighbor/logging.json - Details dialog → Services tab: verify TXT records, services list, URL present only when
_http._tcpexists
SSDP.md— parallel SSDP pipelineWSD.md— WS-Discovery pipelineNETBIOS.md— NetBIOS pipelineCOMMUNITY_OVERRIDES.md— user overlay rulesBACKEND_ARCHITECTURE.md— discovery manager overview