Rooms and membership
A room is a UUID. dotwire has no create-room call: a room exists as soon as you add members to it. A room id that isn't a UUID can't exist, so room-scoped routes answer it with 404.
Membership
The room_members table is the authoritative record of who belongs to which room. The token never grants a room. You manage membership from your host backend through the admin API or a host SDK.
Node.js (@dotwire/host)
await host.setUserRole('user-42', 'member');
await host.addRoomMembers(roomId, ['user-42', 'user-57']);
.NET (Dotwire.Host)
Guid roomId = Guid.Parse("a0000000-0000-0000-0000-000000000001");
await client.SetUserRoleAsync("user-42", "member");
await client.AddRoomMembersAsync(roomId, ["user-42", "user-57"]);
A member also needs a role in user_roles, or the role cross-check denies every request. Adding members can grant the member role at the same time, without overwriting an existing auditor or admin role:
- The HTTP API does this only when the request sets
ensureMemberRole: true. The default isfalse. - Both host SDKs set it by default. To add membership only, pass
{ ensureMemberRole: false }as the options argument in Node, or the named argumentensureMemberRole: falsein .NET.
Deleting a user's role also removes all of that user's room memberships.
Who can read a room
Reading a room follows membership, not role. History, live delivery, SSE, and the participant list all require the caller to be in room_members. An auditor or admin who wants to read a room's messages must be a member like anyone else.
Participants and presence
These answer different questions:
- Participants are the room's members:
GET /rooms/{roomId}/participantsreturns their user ids, sorted. It returns ids only. Resolve names and avatars through your own user directory. - Presence is live connection activity. The server sends coalesced deltas (
joined,left) as members connect and disconnect. There is no presence snapshot endpoint, so a client sees only changes that happen after it subscribes.
Refresh the participant list after your host changes membership, or when a user opens the room.
Revocation
When you remove a member, or delete a user's role, dotwire acts on live connections immediately:
- Every hub connection that user holds in the room receives
MembershipRevoked, is removed from the room, and a presence leave is queued. - Every SSE stream that user holds for the room receives a
revokedevent and closes. - The revocation travels over NATS core, so it reaches every node, not just the node that handled the admin request.
The SDKs surface it directly: onMembershipRevoked in @dotwire/client, which also unsubscribes the room, status: 'revoked' in @dotwire/react, and a revoked event in both host SDKs' SSE streams.
System messages
An admin can inject a message into any room as an asserted sender, without being a member. This is how hosts post bot replies, notifications, and moderation notices.
- The sender id defaults to
system. dotwire doesn't check that the sender id is a real member. - Every injection writes a
message.injectedaudit event. If the event can't be written, dotwire erases the message before anyone receives it and returns503 audit_unavailable, so you can retry without creating a duplicate. - Injecting into a room doesn't grant read access to it.
- Presend and postsend webhooks skip injected messages unless you turn on
IncludeAdminSends.
Node.js (@dotwire/host)
const room = host.room('a0000000-0000-0000-0000-000000000001');
await room.sendSystemMessage('Server restart scheduled in 5 minutes.');
.NET (Dotwire.Host)
var ack = await client.SendSystemMessageAsync(roomId, "system announcement");
var botAck = await client.SendSystemMessageAsync(roomId, "build finished", senderId: "bot:ci");
@dotwire/client routes messages whose sender id is system, or starts with system: or bot:, to its onSystemMessage listeners.
Limits
- User ids and sender ids are 1 to 256 characters, with no control characters.
- Message content must be non-empty and at most
Dotwire:MaxContentBytesUTF-8 bytes, 65,536 by default.
Related
- Auth: roles and the authorization pipeline.
- HTTP API: membership and role endpoints.
- Integrate a host app