5.0 KiB
summary, read_when
| summary | read_when | |||
|---|---|---|---|---|
| How Clawdis presence entries are produced, merged, and displayed |
|
Presence
Clawdis “presence” is a lightweight, best-effort view of:
- The Gateway itself (one per host), and
- The clients connected to the Gateway (mac app, WebChat, CLI, etc.).
Presence is used primarily to render the mac app’s Instances tab and to provide quick operator visibility.
The data model
Presence entries are structured objects with (some) fields:
instanceId(optional but strongly recommended): stable client identity used for dedupehost: a human-readable name (often the machine name)ip: best-effort IP address (may be missing or stale)version: client version stringdeviceFamily(optional): hardware family likeiPad,iPhone,MacmodelIdentifier(optional): hardware model identifier likeiPad16,6orMac16,6mode: e.g.gateway,app,webchat,clilastInputSeconds(optional): “seconds since last user input” for that client machinereason: a short marker likeself,connect,periodic,instances-refreshtext: legacy/debug summary string (kept for backwards compatibility and UI display)ts: last update timestamp (ms since epoch)
Producers (where presence comes from)
Presence entries are produced by multiple sources and then merged.
1) Gateway self entry
The Gateway seeds a “self” entry at startup so UIs always show at least the current gateway host.
Implementation: src/infra/system-presence.ts (initSelfPresence()).
2) WebSocket connect (connection-derived presence)
Every WS client must begin with a connect request. On successful handshake, the Gateway upserts a presence entry for that connection.
This is meant to answer: “Which clients are currently connected?”
Implementation: src/gateway/server.ts (connect handling uses connect.params.client.instanceId when provided; otherwise falls back to connId).
Why one-off CLI commands do not show up
The CLI connects to the Gateway to execute one-off commands (health/status/send/agent/etc.). These are not “nodes” and would spam the Instances list, so the Gateway does not create presence entries for clients with client.mode === "cli".
3) system-event beacons (client-reported presence)
Clients can publish richer periodic beacons via the system-event method. The mac app uses this to report:
- a human-friendly host name
- its best-known IP address
lastInputSeconds
Implementation:
- Gateway:
src/gateway/server.tshandles methodsystem-eventby callingupdateSystemPresence(...). - mac app beaconing:
apps/macos/Sources/Clawdis/PresenceReporter.swift.
Merge + dedupe rules (why instanceId matters)
All producers write into a single in-memory presence map.
Key points:
- Entries are keyed by a “presence key”. If two producers use the same key, they update the same entry.
- The best key is a stable, opaque
instanceIdthat does not change across restarts. - Keys are treated case-insensitively.
Implementation: src/infra/system-presence.ts (normalizePresenceKey()).
mac app identity (stable UUID)
The mac app uses a persisted UUID as instanceId so:
- restarts/reconnects do not create duplicates
- renaming the Mac does not create a new “instance”
- debug/release builds can share the same identity
Implementation: apps/macos/Sources/Clawdis/InstanceIdentity.swift.
displayName (machine name) is used for UI, while instanceId is used for dedupe.
TTL and bounded size (why stale rows disappear)
Presence entries are not permanent:
- TTL: entries older than 5 minutes are pruned
- Max: map is capped at 200 entries (LRU by
ts)
Implementation: src/infra/system-presence.ts (TTL_MS, MAX_ENTRIES, pruning in listSystemPresence()).
Remote/tunnel caveat (loopback IPs)
When a client connects over an SSH tunnel / local port forward, the Gateway may see the remote address as loopback (127.0.0.1).
To avoid degrading an otherwise-correct client beacon IP, the Gateway avoids writing loopback remote addresses into presence entries.
Implementation: src/gateway/server.ts (isLoopbackAddress()).
Consumers (who reads presence)
macOS Instances tab
The mac app’s Instances tab renders the result of system-presence.
Implementation:
- View:
apps/macos/Sources/Clawdis/InstancesSettings.swift - Store:
apps/macos/Sources/Clawdis/InstancesStore.swift
The store refreshes periodically and also applies presence WS events.
Debugging tips
- To see the raw list, call
system-presenceagainst the gateway. - If you see duplicates:
- confirm clients send a stable
instanceIdin the handshake (connect.params.client.instanceId) - confirm beaconing uses the same
instanceId - check whether the connection-derived entry is missing
instanceId(then it will be keyed byconnIdand duplicates are expected on reconnect)
- confirm clients send a stable