Disclosure: I'm the developer of Bolo, and this post is published from my account. It was drafted and published by an AI agent working on my behalf from the project's source code.
Bolo AI lets you speak into your phone and have the text typed at the cursor on your PC. The PC side is a small Node.js server that the phone reaches over one of three routes: the home or office LAN, Tailscale, or a Cloudflare tunnel when neither is reachable. The window on the PC shows each paired phone and which route it is using, and that needs the server to answer a plain question for every connection: how did this one arrive?
It is a 10-line function. Here is why each line is there.
What the server can see
All connections end up as a WebSocket on the same port, so the only evidence is the socket's remote address and the request headers:
function routeOf(req) { const h = req.headers || {}; if (h['cf-connecting-ip'] || h['cf-ray']) return 'Cloud'; const ip = String(req.socket?.remoteAddress || '').replace(/^::ffff:/, ''); if (ip === '::1' || ip === '127.0.0.1') return 'Cloud'; // cloudflared connects from localhost if (/^fd7a:115c:a1e0:/i.test(ip)) return 'Tailscale'; const m = ip.match(/^100\.(\d+)\./); if (m && +m[1] >= 64 && +m[1] <= 127) return 'Tailscale'; return 'LAN'; } Line by line
Tunnel headers first. Cloudflare adds cf-connecting-ip and cf-ray. If either is present, the request came through the tunnel, whatever the socket says.
Loopback means the tunnel. The tunnel client (cloudflared) runs on the same PC and connects to the server from 127.0.0.1. So from the server's point of view a cloud connection has a local address. A naive "private address means LAN" test would label every tunnelled phone as LAN. The loopback check is the second guard in case the headers are ever stripped.
Strip the IPv4-mapped prefix. Node on a dual-stack socket reports IPv4 peers as ::ffff:192.168.1.20. Without the replace, none of the dotted-quad tests below would ever match.
Tailscale has two address families. IPv4 addresses are in the CGNAT range 100.64.0.0/10, which is 100.64.x.x through 100.127.x.x. Matching just ^100\. would also catch other 100.x addresses outside that range, so the second octet is range-checked. IPv6 addresses sit under the fd7a:115c:a1e0::/48 prefix.
Everything else is LAN. The fall-through is a deliberate default: if it was not a tunnel and not Tailscale, it reached the PC directly.
Where the answer is stored
The result is saved on the paired-device record as lastRoute, and kept in the in-memory map of live connections as route. A phone that switches from Wi-Fi to mobile data shows up as a new connection with a new route, and the window lists every route a phone currently has open.
A limit worth knowing
This is a label for the user's information, not an access-control decision. Headers can be set by anyone who can reach the port directly, so a client on the LAN could send cf-ray and be labelled Cloud. Authentication is by a per-phone token, checked separately; the route only decides what the window displays. If you ever want to gate behaviour on the route, trust only the socket address.
Dictated text stays out of it
The same module keeps a short history of recent dictations so the window can show them. It is held in memory only (the last 30, gone when the app exits), and the log records lengths, never the text. That rule was set before the history existed, so the history is built around it rather than an exception to it.
Source: src/devices.js in the PC server.
![Industrial Communication — Ch.10: Factory Automation [EN]](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft1hw2kvh64lkchmsurzd.png)

