Integrate a host app
dotwire stores room membership and messages. Your host application owns users, profiles, authentication, and the RS256 private key. This guide connects the two.
Choose your SDKs
| SDK | Runs in | Use it for |
|---|---|---|
Dotwire.Host | .NET host backend | Mint user tokens, grant roles and room membership, send host messages, read as a user, administer audit and retention |
@dotwire/host | Node.js host backend | The same host operations from JavaScript or TypeScript |
@dotwire/client | Browser or JavaScript client | Subscribe to rooms, send messages, load history and participants, show typing and presence |
@dotwire/react | React client | Provider and hooks for messages, typing, presence, and connection state |
Pick one host SDK for your backend's language. A browser uses @dotwire/client or @dotwire/react. There is no .NET member client SDK. The host SDKs call dotwire's HTTP API and don't need database access.
1. Run dotwire
Run dotwire with Postgres and NATS, and configure it with your host's public signing key, issuer, and audience. Keep the private key in your host. See the Quickstart for a local stack and Deployment for more.
2. Grant roles and room membership
Give each user the member role and add them to rooms. dotwire checks both in its own tables; a token alone doesn't grant a room.
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"]);
These are admin operations, signed as host-admin by default. See Rooms and membership.
3. Expose a token endpoint
Add an authenticated endpoint that mints a dotwire token for the current user. Take the user id from your session, never from the request body.
@dotwire/client calls this endpoint with GET and credentials: 'include', and expects a JSON body of { token: string }.
Node.js (@dotwire/host), as a Next.js route handler
import { NextResponse } from 'next/server';
import { DotwireHostClient } from '@dotwire/host';
const host = new DotwireHostClient({
baseUrl: process.env.DOTWIRE_BASE_URL!,
privateKeyPem: process.env.DOTWIRE_PRIVATE_KEY_PEM!,
keyId: 'prod-key',
issuer: 'my-app'
});
export async function GET(req: Request) {
// Verify session in your existing auth system (NextAuth, Supabase, Clerk, etc.)
const sessionUser = await getSessionUser(req);
if (!sessionUser) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const token = await host.mintToken(sessionUser.id, {
role: sessionUser.role ?? 'member',
displayName: sessionUser.name,
ttl: '1h'
});
return NextResponse.json({ token });
}
getSessionUser stands for your own session lookup.
.NET (Dotwire.Host)
string token = client.MintToken("user-42", role: "member");
Return it from your endpoint as { "token": "..." }.
Keep TTLs short. See Token lifetime.
4. Connect the client
Point the client SDK at dotwire and at your token endpoint, then subscribe to a room:
const client = new DotwireClient({
baseUrl: 'https://chat.example.com',
tokenUrl: '/api/dotwire/token'
});
const room = client.room(roomId);
await room.subscribe({ history: 50 });
const userIds = await room.getParticipants();
const ack = await room.sendMessage('Hello');
ack.seq is the message's ordering token. An ack means JetStream accepted the message. See What "sent" means.
In React, use @dotwire/react instead.
5. Resolve participant profiles
getParticipants() returns member user ids only. Resolve usernames and avatars through a batch lookup in your host, apply your own visibility rules there, and cache profiles in the frontend as your policy allows. dotwire doesn't know whether one user may see another's profile.
Keep the participant list and presence apart: participants are current room membership, while presence is live connection activity.
From the host backend, you can read participants as a specific member:
Node.js (@dotwire/host)
const userIds = await host.getRoomParticipants(roomId, 'user-42');
.NET (Dotwire.Host)
IReadOnlyList<string> userIds = await client.GetRoomParticipantsAsync(roomId, "user-42");
getRoomMembers(roomId) and GetRoomMembersAsync(roomId) are admin operations for host management. The participant methods use the named user's room access, so pass the authenticated user's id when you serve a user request.
Optional: route sends through your host
By default, the browser sends messages straight to dotwire. To run your own checks on every send, set the client's sendUrl to a host route and handle it with handleClientSend (Node) or HandleClientSendAsync (.NET). See Moderation webhooks.
Checklist
- Keep the signing private key and the host SDK on the backend.
- Never treat the
dw:roleclaim as the only grant. dotwire cross-checks it againstuser_rolesand checksroom_membersfor room access. - Use the admin membership methods to change membership, and the participant methods for member-facing lists.
- Refresh the participant list after you change membership.