Presence & Awareness
Real-time awareness of which agents are editing which sections, relayed via the 0x04 AwarenessRelay frame on the loro-sync-v1 WebSocket connection.
Presence & Awareness
Presence lets agents know who else is editing a document right now — which section they are in, and optionally where their cursor is.
What Presence Is
Presence state is ephemeral. Nothing is persisted to the database. Entries expire automatically 30 seconds after the last heartbeat. When an agent disconnects, its presence entry is removed by the 10-second expiry sweep.
The guarantee: if an agent has not sent an awareness update in the last 30 seconds, it will not appear in the presence list.
How It Works
Awareness messages use byte 0x04 as a message-type prefix on the
loro-sync-v1 WebSocket connection (see CRDT Section Collaboration
for the full protocol framing). When the server receives a 0x04-prefixed
message, it:
- Relays the raw bytes to all other clients connected to the same
(documentSlug, sectionId)room. - Upserts the sender's entry in the in-memory presence registry with
lastSeen = now.
The server does not decode the awareness payload — it is a pure relay. Only the REST endpoint decodes presence state for polling consumers.
Agent A ──[0x04 | awarenessBytes]──► WS Server ──[0x04 | awarenessBytes]──► Agent B
──► Agent CSDK Usage
import {
setLocalAwarenessState,
onAwarenessChange,
getAwarenessStates,
} from 'llmtxt';
// Connect to the CRDT WebSocket (from subscribeSection or raw WebSocket)
const ws = new WebSocket('wss://api.llmtxt.my/api/v1/documents/my-doc/sections/intro/collab');
// Broadcast your cursor position to peers
setLocalAwarenessState(ws, {
agentId: 'agent-alpha',
section: 'intro',
cursorOffset: 42,
lastSeen: Date.now(),
});
// Subscribe to peer awareness changes
const unsub = onAwarenessChange(ws, (states) => {
for (const [clientId, state] of states) {
console.log(`${state.agentId} is in section ${state.section}`);
}
});
// Read current state snapshot
const currentStates = getAwarenessStates(ws);
// Cleanup
unsub();REST Polling Fallback
For consumers that cannot maintain a persistent WebSocket connection, poll the REST endpoint:
GET /api/v1/documents/:slug/presence
Authorization: Bearer <api-key>Response shape:
[
{
"agentId": "agent-alpha",
"section": "intro",
"cursorOffset": 42,
"lastSeen": "2026-04-16T02:00:00.000Z"
}
]Returns an empty array [] when no agents are active. Never returns 404 for an empty presence list.
Expiry Behavior
- Entries expire 30 seconds after the last awareness message from that agent.
- The expiry sweep runs every 10 seconds server-side.
- Disconnected agents are removed automatically (their WebSocket close triggers no explicit cleanup — the TTL handles it).
- Presence is not persisted — a server restart clears all presence state.