Skip to content

Client Presence

ConvergeDB automatically tracks connected clients via a built-in system kind called convergedb_connections. Each connected source gets an entity in this kind, asserted on connect and retracted when the client disconnects (after its liveness deadline expires). You can subscribe to this kind like any other to observe who is online.

When a client connects, the server automatically:

  1. Asserts an entity in convergedb_connections using the client’s own source ID. The entity contains the client’s Name (from ConvergenceOptions) and a LastSeenAt timestamp.
  2. Updates the LastSeenAt timestamp on every heartbeat (default: every 15 seconds).
  3. Retracts the entity via the standard liveness mechanism when the client disconnects and its liveness deadline expires.

Because this uses the same convergence model as user-defined kinds, brief disconnects within the liveness deadline window do not cause the entity to disappear. The client will reconnect and re-assert before the deadline fires.

Set the Name property on ConvergenceOptions to give your client a human-readable identity:

var client = await ConvergenceClient.ConnectAsync(new ConvergenceOptions
{
Host = "127.0.0.1",
Port = 3727,
SourceId = 1,
Name = "player-service",
});

If Name is null or omitted, the entity is still created but the Name field will be empty.

The Connections property on ConvergenceClient is a pre-resolved KindHandle<ConvergeDbConnection> that is ready to use immediately after connecting. No registration or resolution step is needed.

await foreach (var change in client.Connections.SubscribeAsync(bootstrap: true))
{
switch (change.Type)
{
case NotificationType.Bootstrap:
case NotificationType.Created:
Console.WriteLine($"Online: {change.Entity?.Name} (last seen {change.Entity?.LastSeenAt})");
break;
case NotificationType.Deleted:
Console.WriteLine($"Offline: source disconnected");
break;
case NotificationType.Updated:
// LastSeenAt was refreshed by a heartbeat
break;
}
}

With bootstrap: true, the subscription first delivers a snapshot of all currently connected clients, then transitions to live notifications. This gives you a complete view of who is online from the start.

ConvergeDbConnection? conn = await client.Connections.QueryAsync(entityId);

The entity ID for a given source is deterministic: byte 0 is the source ID, bytes 1-31 are zero. You can construct it directly if you know the source ID:

byte[] id = new byte[32];
id[0] = sourceId;
var entityId = new ReadOnlyMemory<byte>(id);

To get a one-shot list of all connected clients without maintaining a subscription:

IReadOnlyList<ConvergeDbConnection> online = await client.Connections.BootstrapSnapshotAsync();

ConvergeDbConnection is a built-in entity type included in the SDK. It implements IConvergenceEntity<ConvergeDbConnection> and does not require a source generator.

PropertyTypeDescription
EntityIdReadOnlyMemory<byte>32-byte entity ID (byte 0 = source ID).
NamestringHuman-readable name from ConvergenceOptions.Name.
LastSeenAtDateTimeOffsetUTC timestamp of the most recent heartbeat or connect.

convergedb_connections is the first system kind in ConvergeDB. System kinds are entity kinds managed by the server itself, not by user code. They share the same convergence semantics, subscription model, and wire protocol as user-defined kinds.

System kinds use the reserved convergedb_ name prefix. Attempting to create a user-defined kind with this prefix will fail with an error.

System kind entities are excluded from source epoch reconciliation. When you call EpochAsync, the server will not retract your connection presence entity, even if you do not re-assert it during the epoch. This is handled automatically.