A support mail with four questions. Every one of them pointed at something worth fixing.
A firewall alert from the internet
A user running Little Snitch saw DeviceShelf accept an incoming connection from 85.217.149.46 on port 8088. The reverse name of that address is o046.scanner.modat.io, an internet-wide scanner. He had switched on “Share on the LAN” for his phone and asked whether that was supposed to happen.
It was not. With sharing on, the desktop bound the port on every address the machine has, and the bearer token was the whole defence. The dashboard shell and the health probe answered anyone; only the data needed the token. DeviceShelf opens no port on a router, so for the scanner to arrive at all his router must forward the port, or the machine sits on a public address. Either way, a switch that says “LAN” should not mean “and whoever gets through”.
From 1.9.51 the desktop listens on local addresses only: loopback, the private ranges, the 100.64/10 space that Tailscale uses, IPv6 unique-local. A globally routable address of the machine gets no socket. And every request is judged by where it comes from, before the health probe, before the dashboard shell, before the token is looked at. Not on a local network, no answer. The address is the peer of the accepted connection, never a header a client can write.
One thing this cannot do. If a router forwards the port to the machine's private address, the connection still arrives at a listener the phone needs, so a per-connection firewall still has something to show. It gets 403 now and nothing else. The forward itself is a router setting.
The server edition is unchanged. It is meant to be exposable behind a proxy, and that is the operator's decision.
The port has a field, and a taken port is named
The port lived in the settings file and nowhere on screen. It has a field now, under “Advanced” on the same card, and it takes 1024 to 65535 only.
Testing turned up a second thing. A dev server was listening on [::1]:4300, the IPv6 side of localhost. DeviceShelf was given port 4300 and took 127.0.0.1:4300, the IPv4 side, right next to it. The operating system saw two different addresses and had no objection. The card said “running · http://localhost:4300”, and that link, which resolves to ::1 first, opened the other program.
DeviceShelf now asks whether anything on this computer already answers on the port, on either loopback address, and refuses if so. The card says which port is taken, in red, under the port field. Local-only mode holds both loopback addresses, so localhost means DeviceShelf.
The card is “Connect your phone”
The old card was laid out the way the server is built: a switch for the HTTP server, a second switch for “also on the LAN” that did nothing without the first, a token, a port. The mail that started this said: “Honestly I’m not fully sure what that’s supposed to do; I merely wanted the custom names and types to sync.”
The card is laid out by that sentence now. One switch, “Connect your phone”, does everything in one save: server on, shared on the local network, an access key made if there is none. Under it sits the pairing code with the two things nobody had been told: which entry to tap in the mobile app, and that keeping edits in step in both directions is a separate switch on the phone, “Share device metadata”, off by default. Seeing the desktop's names on the phone needs nothing more than the pairing.
The three real states, off, this computer only, devices on the local network, are one choice under “Advanced”, with the access key and the port. “Replace” asks before it makes a new key, because a new key cuts off every paired device. Nothing changes for existing setups: a machine that only served scripts on itself shows the switch off and “This computer only” selected, and its scripts keep running.
Every save of any setting used to restart the API and cut the phone's connection for a moment. It restarts only when the port, the key, the sharing or the switch changed.
The iPhone says how a licence is unlocked
The App Store build has no key field and no QR scanner; Apple's rules do not allow unlocking with a key there. The line above that empty space still read “Already have a key? Paste it below.” Somebody who had just bought a licence, in this case through a bundle, saw an instruction with nothing to follow it, and the only input on screen was the email box of the purchase dialog. That reads like being asked to pay twice.
The activation screen and the licence card now name the path that works: open the activation link from the licence email on the phone and tap “Open in the DeviceShelf app”. The activation page on the website says the same for iPhone and iPad instead of telling everyone to paste a key.
“Share device metadata” and its explanation exist in French, Italian, Spanish, Arabic and Chinese now. They were German and English only, and the desktop card quotes that label by name.
Compatibility
Desktop 1.9.51, server 1.9.51, mobile 1.5.31. Stored settings are untouched; the card only reads them differently. A paired phone stays paired. The server edition's behaviour does not change.