Back to docs

Strong privacy guide

Encrypt sensitive realtime payloads.

Use strong privacy mode when specific realtime content should stay unreadable outside the intended participants. Start with protected channels, then encrypt the sensitive JSON payload before it is sent.

Architecture

How this guide should be used.

Step 01

Start with protected channels

Private and presence channels still come first. Your trusted server must verify who can join before any encrypted payload is delivered.

Step 02

Encrypt only the sensitive payload

Strong privacy mode protects the JSON content inside the event. Channel names, event names, timing, connection details, and payload size are still routing metadata.

Step 03

Use the right privacy target

Good targets are private chat bodies, call signaling offer/answer/ICE payloads, sensitive live location, sensitive delivery notes, and custom private events.

Step 04

Plan key lifecycle before launch

Key generation, exchange, backup, rotation, recovery, and lost-device behavior must be clear before production rollout.

Examples

Focused implementation notes.

Encrypt private chat JSON

Use the chat helper when message content should only be readable by room participants.

Encrypt private chat JSON

import {
  decryptChatJson,
  encryptChatJson,
  generateAesGcmKey,
} from "@domvia/realtime-e2ee";

const roomKey = await generateAesGcmKey();

const encrypted = await encryptChatJson(
  {
    body: "Private message",
    sender_id: "user-1",
  },
  roomKey,
  {
    roomId: "room-42",
    messageId: "msg-1",
    keyId: "room-42-v1",
    keyVersion: 1,
  },
);

const message = await decryptChatJson(encrypted, roomKey, {
  roomId: "room-42",
  messageId: "msg-1",
});

Encrypt call signaling JSON

Use this when offer, answer, ICE, or media state payloads need stronger privacy. WebRTC still carries the actual audio and video media.

Encrypt call signaling JSON

import {
  decryptCallSignalJson,
  encryptCallSignalJson,
} from "@domvia/realtime-e2ee";

const encryptedOffer = await encryptCallSignalJson(
  {
    sdp_type: "offer",
    sdp: localDescription.sdp,
  },
  callKey,
  {
    callId: "call-9001",
    eventName: "call.offer",
    keyVersion: 1,
  },
);

const offer = await decryptCallSignalJson(encryptedOffer, callKey, {
  callId: "call-9001",
  eventName: "call.offer",
});

Encrypt custom realtime JSON

Use the universal helper for sensitive location, delivery, account, or custom private events.

Encrypt custom realtime JSON

import {
  decryptRealtimeJson,
  encryptRealtimeJson,
} from "@domvia/realtime-e2ee";

const encrypted = await encryptRealtimeJson(
  {
    note: "Sensitive delivery instruction",
  },
  eventKey,
  {
    scope: "delivery",
    scopeId: "order-1001",
    eventName: "delivery.private_note",
    keyVersion: 1,
  },
);

const payload = await decryptRealtimeJson(encrypted, eventKey, {
  scope: "delivery",
  scopeId: "order-1001",
  eventName: "delivery.private_note",
});

Safety

Rules before production traffic.

Use private or presence channels before encrypted events.

Use strong privacy for sensitive payload fields, not every routine event.

Do not store encryption keys in public client configuration.

Do not treat E2EE as a replacement for authorization, trusted origins, WSS, usage limits, or abuse controls.

Make recovery and key-loss behavior clear before production rollout.

Continue

Keep reading without crowding the main docs page.