← Back to docs

Chat SDK

Build private realtime chat rooms.

Use DomVia Realtime chat patterns for one-to-one rooms, group conversations, typing state, read receipts, presence, and optional private-message encryption.

Package

@domvia/realtime-chat

install

npm install @domvia/realtime @domvia/realtime-chat @domvia/realtime-server @domvia/realtime-e2ee

@domvia/realtime-chat

Built for focused realtime use cases.

Keep the SDK modular, documented, and safe to extend as new products are added.

Architecture

How this guide should be used.

Step 01

Use one protected room channel per conversation

Keep each conversation isolated. A good channel shape is private-chat.room.{roomId}. Your trusted server decides who can join before the client receives events.

Step 02

Keep the core client small

Use @domvia/realtime for the connection, then add @domvia/realtime-chat only where a product needs messaging helpers.

Step 03

Persist messages on your server

Realtime events should notify clients that something changed. Store durable message records, attachments, moderation flags, and audit history in your product database.

Step 04

Add E2EE only for private message content

Private chat content is the first E2EE target. Typing state, read receipts, and delivery status usually stay as normal protected events.

Examples

Focused implementation notes.

Client chat room

Connect with the public key, join a private chat room, listen for messages, and send throttled client typing signals when enabled.

Client chat room

import { DomViaRealtime } from "@domvia/realtime";
import {
  buildChatChannelName,
  createChatRoom,
} from "@domvia/realtime-chat";

const realtime = DomViaRealtime.create({
  key: "pk_live_your_public_key",
  host: "ws.domvia.net",
  authEndpoint: "/realtime/auth",
  clientEvents: true,
});

const room = createChatRoom(realtime, {
  roomId: "room-42",
  channelName: buildChatChannelName("room-42"),
  encrypted: true,
});

await room.waitUntilReady();

room.onMessage((event) => {
  console.log("New message:", event.message_id);
});

room.onTyping((event) => {
  console.log("Typing:", event.user_id, event.is_typing);
});

room.sendTyping({
  user_id: "user-1",
});

Trusted server authorization

The browser never signs private channel access directly. Your server verifies the signed-in user and returns a signed authorization response.

Trusted server authorization

import { authorizePrivateChannel } from "@domvia/realtime-server";

app.post("/realtime/auth", async (request, response) => {
  const user = await requireSignedInUser(request);
  const channelName = request.body.channel_name;
  const socketId = request.body.socket_id;

  const roomId = channelName.replace("private-chat.room.", "");

  if (!channelName.startsWith("private-chat.room.")) {
    return response.status(403).json({ message: "Forbidden" });
  }

  if (!await userCanAccessChatRoom(user.id, roomId)) {
    return response.status(403).json({ message: "Forbidden" });
  }

  return response.json(
    await authorizePrivateChannel({
      key: process.env.DOMVIA_REALTIME_KEY,
      secret: process.env.DOMVIA_REALTIME_SECRET,
      socketId,
      channelName,
    }),
  );
});

Safety

Rules before production traffic.

Keep chat room membership checks on your trusted server.

Never trust room IDs sent by the client without verifying membership.

Use E2EE for private message content when the product requires participant-only readability.

Do not put app secrets or room encryption keys in public client config.

Keep attachments and durable message records in your product database.

Continue

Keep reading without crowding the main docs page.