Presence
Fased presence is a lightweight, best-effort view of:- the Gateway itself, and
- clients connected to the Gateway (mac app, WebChat, CLI, etc.)
Presence fields (what shows up)
Presence entries are structured objects with fields like:instanceId: stable client identity when the client provides onedeviceId: signed device identity when device auth is presenthost: human-friendly host nameip: best-effort IP addressversion: client version stringplatform: platform stringdeviceFamily/modelIdentifier: hardware hintsmode:ui,webchat,cli,backend,probe,test,node, …lastInputSeconds: seconds since last user input, if knownreason:self,connect,node-connected,periodic, …roles/scopes: Gateway role and scope hints from the connectiontext: original presence text or generated fallback textts: last update timestamp (ms since epoch)
Producers (where presence comes from)
Presence entries are produced by multiple sources and merged.1) Gateway self entry
The Gateway always seeds aself entry at startup so UIs show the gateway host
even before any clients connect.
2) WebSocket connect
Every WebSocket client begins with aconnect request. On successful handshake
the Gateway upserts a presence entry for that connection.
Why one-off CLI commands don’t show up
The CLI often connects for short, one-off commands. To avoid spamming the Instances list,client.mode === "cli" is not turned into a presence entry.
3) system-event beacons
Clients can send richer periodic beacons via the system-event method. The mac
app uses this to report host name, IP, platform details, and
lastInputSeconds.
Non-node system-event text is also added to the system event feed. Node:
presence lines are deduped so only meaningful context changes create a system
event.
4) Node connects (role: node)
When a node connects over the Gateway WebSocket withrole: node, the Gateway
upserts a presence entry with roles: ["node"] and the granted scopes.
Merge + dedupe rules (why instanceId matters)
Presence entries are stored in a single in-memory map:
- Entries are keyed by a presence key.
- Keys are case-insensitive.
- WebSocket connect entries prefer
device.id, thenclient.instanceId, then the connection id. system-evententries preferdeviceId, theninstanceId, then parsed host/IP/text fallback data.
TTL and bounded size
Presence is intentionally ephemeral:- TTL: entries older than 5 minutes are pruned
- Max entries: 200 (oldest dropped first)
disconnect; they do not immediately
delete the row. The TTL removes stale rows.
Remote/tunnel caveat (loopback IPs)
When a client connects over an SSH tunnel or local port forward, the Gateway may see the remote address as127.0.0.1. To avoid overwriting a good client-reported
IP, loopback remote addresses are ignored.
Consumers
Advanced and native debug views
Advanced/native debug views render the output ofsystem-presence and apply a
small status indicator (Active/Idle/Stale) based on the age of the last update.
Debugging tips
- To see the raw list, call
system-presenceagainst the Gateway. - If you see duplicates:
- confirm clients send a stable
client.instanceIdor signeddevice.idin the handshake - confirm periodic beacons use the same
deviceIdorinstanceId - check whether the connection-derived entry fell back to a connection id
- confirm clients send a stable