0022. Finding the hub on the network
- Status: Accepted
- Date: 2026-10-28
Context
Section titled “Context”A hub on a shop’s LAN usually gets its IP address from the router. After a power cut or a router
change, the address can change, and every paired device loses the hub until someone types the
new address into each one (Change hub address, M7). The hub already announces itself over
mDNS as _openkedai._tcp (ADR 0019), but nothing listened.
Following a hub automatically has a risk that typing an address doesn’t: a device follows whatever answers on the network. Anyone on the shop’s Wi-Fi can announce a service with any name. A device that sent its token to such a service would hand over the shop’s access.
Decision
Section titled “Decision”The hub’s identity.
- Each hub makes a random hub id once and keeps it in its registry (
hub_state.hub_id). - The id is in the hub’s mDNS TXT record (
id, next toapi), inGET /api/hub, in the credentials from claiming, pairing and rejoining, and inGET /api/devices/me. - Devices store it with the hub’s address (
hub.id). It is optional in the protocol, so devices paired before M8.5 learn it the first time they follow the hub.
Proving the hub is the device’s own, before the token is sent.
- The hub stores only the SHA-256 of each device token. A device that finds a candidate hub
sends
POST /api/hub/provewith its device id and a fresh random nonce (no token). - The hub answers with
HMAC-SHA256(key = token hash, message = nonce)for each working token of that device. The device computes the same from its own token and compares. - Only a hub that holds the token’s hash can answer. A new nonce each time means an answer can’t be replayed. A removed device has no working token, so its old hub can’t prove anything.
- Only after the proof does the device send its token, to
GET /api/devices/me. It follows the hub if the answer names the same shop and device. This is the same check as Change hub address.
When a device looks.
- The app feeds the sync engine’s status to a
HubFollower. It starts looking when the hub stops answering, or when the hub at the old address doesn’t know the device (another hub took its address). - It looks 5 seconds after the hub is lost, then every 20 seconds while it stays lost. Each look browses for 4 seconds. A device follows a restarted hub within about 30 seconds of it answering again.
- It considers only hubs advertising its hub id. A device without a stored id considers every hub that has one, and the proof filters the rest.
- When the hub is still at its old address, the device just syncs again at once.
- It doesn’t look for a hub whose address is
https://. Such a hub is on the internet or behind a proxy with a fixed name, so it doesn’t move around a LAN.
Native code. A new Expo module, openkedai-discovery, has one call:
browse(type, timeoutMs). It lists the services found, each with its IPv4 addresses, port and
TXT record.
- iOS: Network.framework’s
NWBrowser, resolving each service by connecting to it over IPv4. The app declaresNSBonjourServices: ["_openkedai._tcp"]for iOS’s local network permission. - Android:
NsdManager, resolving one service at a time (older Android allows only one at a time). - Web preview and Expo Go: browsing isn’t available, and the app simply doesn’t look.
Joining. The join screen and Change hub address list the hubs found on the network. Tapping one fills in its address; the pairing code is still needed.
Consequences
Section titled “Consequences”- Hub IP changes: devices reconnect on their own, with no one touching them. A fixed address in the router is no longer needed, though it is still a good idea.
- Spoofed hubs: something announcing the shop’s hub id gets a nonce, never a token.
- A copy of the hub’s registry (
hub.sqlite) could pass the proof. Whoever has that file has the hub’s data anyway. - Plain HTTP on the LAN can still be watched, as ADR 0019 says.
- A copy of the hub’s registry (
- Network limits: mDNS doesn’t cross routers or VLANs, and some guest Wi-Fi networks block it. There, devices keep retrying the old address, and Change hub address remains the way to move them.
- Android emulator: the emulator’s own network usually doesn’t receive the Mac’s mDNS, so checking discovery on Android may need a real device on the same Wi-Fi.
- Old hubs: devices can’t follow a hub older than M8.5. Its info has no hub id, and it has no prove route.
- Hub port: the hub now advertises the port it actually listens on, not the configured one. They differ only when the configured port is 0.
For AI tools: llms.txt ·llms-full.txtopenkedai.com