Disclosure: I'm the developer of the app described here, and this post was drafted and published by an AI agent from the project's source code.
Bolo (search Bolo AI on Google Play) turns an Android phone into a microphone for a computer. On the same Wi-Fi the phone finds the PC directly. Away from it, the PC's Node.js helper publishes a small "I'm here, reach me at this URL" record that the phone looks up. This post covers src/presence.js, the 146-line module behind that record, and four decisions in it.
1. Sign the record, don't trust the transport
The helper generates an EC key pair (prime256v1) once and keeps it in its settings file. The server ID is the SHA-256 of the public key, so identity is the key, not a name anyone can claim.
Each record is signed over a canonical, newline-joined payload of fixed fields in a fixed order (server ID, URL, display name, version, boot ID, reported-at, expires-at, lookup-token hash):
record.signature = crypto.sign( 'sha256', Buffer.from(canonicalPresencePayload(record), 'utf8'), identity.privateKey ).toString('base64'); Signing a canonical string instead of JSON.stringify(record) matters: key order and whitespace in JSON are not guaranteed to survive a round trip, and a signature check that depends on them fails for no good reason.
The phone looks the PC up by a random lookup token, but the record stores only its hash, so the lookup service holds nothing that lets it replay the lookup.
2. Short TTL, steady heartbeat
Records expire after 150 s and the helper re-sends every 45 s: three chances to refresh before a record lapses. A PC that is switched off just stops sending and falls out of the lookup on its own, with no "goodbye" message to lose. The interval timer is unref()ed so the heartbeat never keeps the process alive by itself.
3. Publish only while the route works
This is the fix that taught me the most. A record for a tunnel URL that no longer reached the PC made the phone show the PC as online, and then every tap failed. The refresh now asks a health check first:
async function publishPresence() { if (!currentTunnelUrl || !currentVersion) return false; if (!routeHealthy()) return false; ... } routeHealthy is injected (setRouteHealthCheck) and defaults to "yes", which keeps the module testable. When the probe says the URL does not reach this machine, the helper stays silent, the old record expires within the TTL, and the phone sees "offline", which is true. Absence of a record is more honest than a stale one.
4. Say who said no
A rejected refresh now logs who rejected it. The service answers with JSON; an edge bot-challenge answers with HTML. So the log line is either proxy: {...} or edge (cf-mitigated=..., ray=...). On one CI runner on a datacenter network, the edge had answered a bare https.request with a bot challenge while fetch from the same machine got a 200. Home networks passed both. The module now uses fetch with a 10 s AbortSignal.timeout, and the log makes the next such difference diagnosable in one line instead of a guessing game.
Takeaways
- Make identity a key; sign a canonical string, not serialized JSON.
- Short TTL plus a faster heartbeat beats explicit "I'm gone" messages.
- Gate publishing on a real reachability check; a missing record beats a stale one.
- Log which layer rejected you.
- Inject the health check so the module stays testable.
Bolo is on Google Play (search Bolo AI): https://play.google.com/store/apps/details?id=com.ashwar.bolo. The presence record carries only a server ID, a URL, a display name and version info, signed by the PC's own key; it does not carry dictated text.