Files
openclaw/docs/concepts/presence.md
Vincent Koc 7f71e84248 docs(concepts): typography hygiene + Related CardGroups across 3 pages
docs/concepts/presence.md: replaced 8 curly quote and non-breaking
hyphen characters (U+201C/U+201D/U+2019/U+2011) with ASCII equivalents
so grep, copy-paste, and Mintlify search hit the right tokens.
Converted the 2-bullet Related list into a CardGroup adding cross-links
to gateway architecture and gateway protocol since presence is produced
by both surfaces.

docs/concepts/markdown-formatting.md: replaced 5 typography characters
(en-dash and curly quotes) with ASCII equivalents and converted the
2-bullet Related list into a CardGroup pointing at streaming/chunking
and system prompt.

docs/concepts/typing-indicators.md: replaced 4 typography characters
with ASCII equivalents and converted the 2-bullet Related list into a
CardGroup with the same Presence and Streaming cross-links.

Verified /concepts/streaming, /concepts/system-prompt,
/concepts/architecture, and /gateway/protocol targets all exist.
2026-05-05 18:30:39 -07:00

118 lines
4.1 KiB
Markdown

---
summary: "How OpenClaw presence entries are produced, merged, and displayed"
read_when:
- Debugging the Instances tab
- Investigating duplicate or stale instance rows
- Changing gateway WS connect or system-event beacons
title: "Presence"
---
OpenClaw "presence" is a lightweight, best-effort view of:
- the **Gateway** itself, and
- **clients connected to the Gateway** (mac app, WebChat, CLI, etc.)
Presence is used primarily to render the macOS app's **Instances** tab and to
provide quick operator visibility.
## Presence fields (what shows up)
Presence entries are structured objects with fields like:
- `instanceId` (optional but strongly recommended): stable client identity (usually `connect.client.instanceId`)
- `host`: human-friendly host name
- `ip`: best-effort IP address
- `version`: client version string
- `deviceFamily` / `modelIdentifier`: hardware hints
- `mode`: `ui`, `webchat`, `cli`, `backend`, `probe`, `test`, `node`, ...
- `lastInputSeconds`: "seconds since last user input" (if known)
- `reason`: `self`, `connect`, `node-connected`, `periodic`, ...
- `ts`: 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 a "self" entry at startup so UIs show the gateway host
even before any clients connect.
### 2) WebSocket connect
Every WS client begins with a `connect` request. On successful handshake the
Gateway upserts a presence entry for that connection.
#### Why one-off CLI commands do not 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, and `lastInputSeconds`.
### 4) Node connects (role: node)
When a node connects over the Gateway WebSocket with `role: node`, the Gateway
upserts a presence entry for that node (same flow as other WS clients).
## Merge + dedupe rules (why `instanceId` matters)
Presence entries are stored in a single in-memory map:
- Entries are keyed by a **presence key**.
- The best key is a stable `instanceId` (from `connect.client.instanceId`) that survives restarts.
- Keys are case-insensitive.
If a client reconnects without a stable `instanceId`, it may show up as a
**duplicate** row.
## TTL and bounded size
Presence is intentionally ephemeral:
- **TTL:** entries older than 5 minutes are pruned
- **Max entries:** 200 (oldest dropped first)
This keeps the list fresh and avoids unbounded memory growth.
## Remote/tunnel caveat (loopback IPs)
When a client connects over an SSH tunnel / local port forward, the Gateway may
see the remote address as `127.0.0.1`. To avoid overwriting a good client-reported
IP, loopback remote addresses are ignored.
## Consumers
### macOS Instances tab
The macOS app renders the output of `system-presence` and applies a small status
indicator (Active/Idle/Stale) based on the age of the last update.
## Debugging tips
- To see the raw list, call `system-presence` against the Gateway.
- If you see duplicates:
- confirm clients send a stable `client.instanceId` in the handshake
- confirm periodic beacons use the same `instanceId`
- check whether the connection-derived entry is missing `instanceId` (duplicates are expected)
## Related
<CardGroup cols={2}>
<Card title="Typing indicators" href="/concepts/typing-indicators" icon="ellipsis">
When typing indicators are sent and how to tune them.
</Card>
<Card title="Streaming and chunking" href="/concepts/streaming" icon="bars-staggered">
Outbound streaming, chunking, and per-channel formatting.
</Card>
<Card title="Gateway architecture" href="/concepts/architecture" icon="diagram-project">
Gateway components and the WebSocket protocol that drives presence updates.
</Card>
<Card title="Gateway protocol" href="/gateway/protocol" icon="plug">
The wire protocol for `connect`, `system-event`, and `system-presence`.
</Card>
</CardGroup>