Documentation

Last updated: 2026-09-12

Four operating systems, two protocols, one network share

The feature request sounds tiny:

A share on a .local host should appear in the file manager without anyone typing a URL.

That is the user-facing requirement.

The implementation turns into a small compatibility matrix.

mDNS.

SSDP.

Bonjour.

Avahi.

Windows Explorer.

Multicast sockets.

UPnP device descriptions.

Icons.

Docker networking.

None of the individual parts are especially exotic.

The difficulty is that each operating system chooses a different subset of them to turn into something the user can actually see.

Discovery is not one thing

When people say "service discovery" on a home or office LAN, they often mean several separate mechanisms.

The first one Everlock needs is mDNS and DNS-SD.

mDNS lets machines resolve names such as:

everlock.local

without a central DNS server.

DNS-SD lets a service advertise what it offers:

_http._tcp
_webdav._tcp

On macOS this ecosystem is commonly encountered as Bonjour.

On Linux, Avahi provides the familiar implementation.

That solves an important part of the problem.

It does not solve the whole user experience.

macOS can know about a service without showing it

This distinction matters.

An operating system can successfully discover a DNS-SD service and still choose not to present it in its file manager.

A generic HTTP service can be perfectly visible to Bonjour tools while Finder offers no generic "all discovered HTTP servers" view.

That means:

protocol discovery
        !=
user-visible integration

A service can exist on the network.

The OS can know it exists.

The user can still have no obvious place to click it.

For a product, the third state is the one that matters.

Windows takes a different path

Windows supports .local resolution, but Explorer's Network view historically leans on a different discovery model for this kind of device presentation.

SSDP, the discovery portion of UPnP, uses multicast too, but the semantics are different.

Instead of primarily announcing a DNS service record, an SSDP device announces itself and points clients to an XML description document.

The exchange is closer to:

device announces presence
        |
        v
client learns description URL
        |
        v
client fetches device.xml
        |
        v
OS decides how to present device

That is enough for Windows Explorer to treat something as a network device rather than merely a hostname that happens to resolve.

So Everlock needs both worlds.

One user goal, two protocols

The user wants:

show my share nearby

The implementation becomes:

                 Everlock
                    |
          +---------+---------+
          |                   |
          v                   v
      mDNS/DNS-SD          SSDP/UPnP
          |                   |
     macOS/Linux           Windows

The diagram is simplified, but the lesson is useful.

There is no universal "LAN discovery" switch that all platforms interpret the same way.

If the feature is specified in terms of the user's goal rather than a protocol, multiple mechanisms may be required.

Everlock announces different service types

The mDNS side is comparatively small.

Everlock can announce a generic web service:

_http._tcp

and, for file shares, a more specific service type:

_webdav._tcp

The host and port are part of the service advertisement.

That is enough for clients that understand those service types.

The protocol does not require the service to run on port 80 simply because it is HTTP.

The advertisement tells the client where the service actually lives.

This is one of the reasons DNS-SD is useful.

It advertises service location rather than assuming fixed ports.

SSDP wants a device, not just a socket

The SSDP side has more ceremony.

To appear as a UPnP-style device, Everlock needs more than:

hostname + port

The device has an identity.

A description document.

A friendly name.

A presentation URL.

Icons.

A lifetime.

Lifecycle announcements.

The protocol is trying to describe something closer to an appliance than a generic network service.

That makes the integration richer and the implementation heavier.

Stable UUIDs matter

Each announced device needs a stable UUID.

That identifier appears in SSDP notifications and lets clients understand that repeated announcements refer to the same device.

If the UUID changed on every restart, the network browser could see a succession of new devices rather than one persistent Everlock endpoint.

So device identity becomes part of the local-discovery model.

That is already a step beyond mDNS service advertisements.

The XML document is part of the feature

An SSDP response points at a description document.

Everlock serves a device.xml for each .local virtual host under a path such as:

/upnp/<slug>/device.xml

That document gives the client enough information to turn a multicast announcement into a user-facing device.

It can include the friendly name and the URL Explorer should open when the user activates the device.

This is where protocol discovery begins to become UI integration.

The multicast packet only gets the client to the description.

The description tells the operating system what sort of thing it found.

Even the icon becomes protocol work

Once a device appears in a graphical network browser, appearance enters the protocol surface.

A generic icon technically works.

It also makes the integration feel unfinished.

So Everlock serves device icons in several sizes:

16 px
32 px
48 px
120 px

Those icon references live in the device description.

This is a useful example of how "just make it discoverable" expands.

The user did not ask for an XML document or multiple PNG sizes.

They asked for the share to show up properly in the file manager.

The implementation details follow from that user-visible requirement.

Presence expires

SSDP devices also need to keep announcing that they are alive.

An ssdp:alive notification includes a cache lifetime.

Clients are allowed to forget the device when that lifetime expires.

So the announcement interval has to remain comfortably below the declared maximum age.

Otherwise the device can disappear between refreshes.

On shutdown, a byebye notification lets clients remove it immediately instead of waiting for cache expiration.

This is another difference from thinking of discovery as a one-time registration.

SSDP presence is leased.

The device has to keep saying:

I still exist

Port 1900 may already be occupied

Active SSDP searches use UDP port 1900.

On a normal Linux host, Everlock may not be the only software interested in that port.

A media server or another UPnP component may already be listening.

Treating that as a fatal error would make the entire frontend fail because an optional discovery mechanism could not obtain exclusive ownership of a shared protocol port.

That would be backwards.

Everlock therefore uses SO_REUSEADDR where possible.

If receiving active searches still cannot be enabled, discovery can degrade.

Periodic announcements can continue even if active M-SEARCH responses are unavailable.

The frontend remains useful.

Optional integration should fail softly

This becomes a general design principle.

The core file share can work without SSDP.

So failure to bind the SSDP receive socket should not prevent the file share from starting.

The hierarchy is:

core service
   |
   +--> local discovery
            |
            +--> active SSDP search responses

Losing the deepest optional layer should not take down the layers above it.

That makes the system more resilient in the messy reality of machines that already run other discovery software.

Then Docker quietly removes the network

The most confusing failure was not in either protocol.

It was in the deployment environment.

Both mDNS and SSDP rely on multicast.

Docker's default bridge networking puts the container on another network segment.

The service starts.

The announcer logs normally.

The code believes packets were sent.

Nothing on the LAN sees them.

There may be no useful error at all.

From inside the container, the feature looks healthy.

From the laptop on the desk, the Everlock share does not exist.

Multicast makes network topology part of the feature

This is why LAN discovery cannot be treated as an application-only concern.

The network path matters.

For the deployment described here, host networking is the practical requirement:

--network host

or the corresponding container configuration.

That is not merely a performance or convenience option.

It determines whether the multicast packets reach the LAN where discovery is supposed to happen.

This is worth making explicit in documentation because the symptoms point in the wrong direction.

Everything works on the developer machine.

Nothing works in the container.

It is natural to suspect application code.

The real boundary is the network namespace.

Four operating systems means four interpretations

The article title says four operating systems because the implementation has to survive several families of behavior rather than one abstract standard.

macOS gives Bonjour strong native integration, but not every service type appears in Finder.

Linux commonly uses Avahi and desktop integrations that vary.

Windows resolves .local names but uses SSDP/UPnP for the Network-style device browsing Everlock wants.

Other devices and embedded systems may understand yet another subset.

The feature therefore cannot be tested by checking one machine.

The correct test matrix is the user-visible goal on each target platform.

Not just:

did the packet leave?

But:

can the person actually find the share?

Standards still need platform knowledge

The surprising part of this work is that all the pieces are standards.

Nothing here requires a proprietary Everlock discovery protocol.

And yet using standards does not create a uniform experience.

The operating system still decides:

which service types matter
which device profiles get UI
which icons are shown
which network scopes are searched
which protocols feed the file manager

The standard is the vocabulary.

The platform decides which words become visible.

The useful mental model

I now think about LAN discovery as four layers:

service exists
    |
    v
protocol advertises it
    |
    v
operating system understands it
    |
    v
user interface presents it

A lot of debugging stops at the second layer.

A packet capture confirms the advertisement.

A command-line discovery tool sees the service.

Technically, discovery works.

But the feature request was never:

Emit correct multicast packets.

It was:

Make the share appear in the file manager.

That is a much stronger requirement.

Compatibility surfaces hide behind simple features

This is the part worth carrying to other features.

A user-visible goal can look like one integration point and actually span several compatibility boundaries.

Here it included:

two discovery protocols
platform-specific UI behavior
shared UDP ports
device metadata
lifecycle announcements
container network topology

The single-protocol version would have been easier.

It also would have worked mostly for whichever operating system happened to be on the developer's desk.

For Everlock, the better definition is the user goal.

If the share is visible where people expect to find nearby services, the feature works.

Everything else is implementation detail.

For configuration details, see the private mDNS page.

updates mdns ssdp upnp networking discovery