Skip to content

faizp/OpenRTC

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OpenRTC

OpenRTC is a self-hosted OSS realtime layer for SaaS teams.

Monorepo layout

  • server/: Go core backend module. It builds openrtc-runtime and openrtc-admin.
  • packages/client/: TypeScript WebSocket client for rooms, events, and presence.
  • packages/react/: React hooks for room state, presence, and broadcast events.
  • packages/rich-text/: Yjs binding helpers plus presence adapters for Tiptap, Lexical, and BlockNote selection/cursor state.
  • packages/yjs/: Yjs provider for binary update/snapshot sync plus an awareness bridge over OpenRTC presence.
  • packages/yjs-compactor/: Trusted Yjs update compactor for Redis-backed document retention.
  • reference-app/: production-style reference app (M5).
  • docs/: protocol, contracts, config, release, and engineering docs.

Deployment model

  • One Go image is built from server/.
  • The image runs either openrtc-runtime or openrtc-admin via command/args.
  • openrtc-runtime owns WebSocket traffic, room state, access-grant checks, presence, Yjs sync, limits, and cluster fan-out.
  • openrtc-admin owns room metadata/access grants, storage documents/patches, durable threads/comments, inbox notifications/settings, active-user reads, publish, presence, stats, and admin HTTP endpoints.

Developer commands

  • make lint
  • make typecheck
  • make test
  • make test-integration
  • make check

Client presence integration

@openrtc/client exposes both low-level protocol methods and a Liveblocks-style room handle for app integrations that need ephemeral presence, live cursors, and debuggable broadcast events without adopting durable storage APIs.

import { OpenRTCClient } from "@openrtc/client";

const client = new OpenRTCClient({
  url: "https://openrtc.example.com/ws",
  token: async () => fetch("/api/openrtc-token").then((res) => res.text()),
  lostConnectionTimeout: 5000,
  backgroundKeepAliveTimeout: 15 * 60 * 1000,
  reconnect: { initialDelayMs: 250, maxDelayMs: 5000 },
});

await client.connect();

const { room, leave } = client.enterRoom("tenant-a:canvas-1", {
  initialPresence: {
    cursor: null,
    user: { id: "user-1", name: "Ada", color: "#4fd1b6" },
  },
});

const unsubscribe = room.subscribe("others", (others, event) => {
  console.log(event.type, others);
});
const unsubscribeLostConnection = room.subscribe("lost-connection", (event) => {
  console.log(event);
});

room.setCursor({ x: 120, y: 240, mode: "comment" });
room.broadcastEvent({ type: "CANVAS_PING", at: Date.now() });

unsubscribe();
unsubscribeLostConnection();
leave();

Presence is ephemeral. The client automatically reconnects by default, keeps the latest local presence in memory, replays active rooms after the next HELLO, and only clears stale remote collaborators after lostConnectionTimeout. lostConnectionTimeout is clamped to the Liveblocks-compatible 1000-30000 ms range. In browser environments, backgroundKeepAliveTimeout can close hidden tabs after an inactivity window and reconnect/replay rooms when the tab is focused again. Room handles emit lost, restored, and failed through the lost-connection subscription; call room.reconnect() for an explicit retry after a hard failure. Durable document state remains owned by the admin storage APIs and the Yjs provider.

The React package exposes the same lifecycle through useEnterRoom, usePresence, useOthers, useOthersMapped, useOthersConnectionIds, useCursors, useOtherCursors, useCursorsMapped, useOther, useSelf, useSelfCursor, useCursor, useMyPresence, useMyPresenceSelector, useSetCursor, useBroadcastEvent, useBroadcastEventWithAck, useStatus, useRoomStatus, useRoomEvents, useDiagnostics, useErrorListener, useLostConnectionListener, and useRoomReconnect. It also exports Liveblocks-style Cursors, Cursor, and AvatarStack components for apps that want cursor tracking/rendering and collaborator stacks without building the UI from scratch. Cursor hooks and components return typed cursor peers with resolved user, color, and mode fields, and accept a presenceKey for apps with multiple cursor layers in one room. Broadcast hooks accept the same string or object-shaped events as room handles. Room hooks use shared entry tracking, so multiple components can subscribe to the same room without one cleanup leaving the room for the others. initialPresence is captured once per room entry, so inline initial presence objects do not cause accidental leave/rejoin churn on rerender.

import {
  AvatarStack,
  Cursors,
  useBroadcastEventWithAck,
  useEnterRoom,
  useLostConnectionListener,
} from "@openrtc/react";

export function CanvasPresence() {
  const room = useEnterRoom("tenant-a:canvas-1", {
    initialPresence: { cursor: null, user: { id: "user-1", name: "Ada" } },
  });
  const broadcastWithAck = useBroadcastEventWithAck(room.id);

  useLostConnectionListener(room.id, (event) => {
    console.info("room connection", event);
  });

  return (
    <Cursors
      room={room.id}
      cursorOptions={{ user: { id: "user-1", name: "Ada" }, color: "#4fd1b6" }}
      mode="pointer"
    >
      <AvatarStack room={room.id} max={5} />
      <Canvas onPing={() => broadcastWithAck({ type: "canvas.ping", at: Date.now() })} />
    </Cursors>
  );
}

For server-side product surfaces, OpenRTCAdminClient wraps the admin REST APIs used for rooms, active users, comments, notifications, subscription settings, ephemeral presence, and broadcast.

The reference app Presence Lab includes a fan-out benchmark for production debugging. Spawn lab clients, run the benchmark, and it stamps every synthetic presence update with a run ID, round, sender, and sent timestamp. The UI reports expected versus observed delivery, loss percentage, p99 latency, and duration so integrators can verify multi-client realtime behavior before embedding OpenRTC.

import { OpenRTCAdminClient } from "@openrtc/client";

const admin = new OpenRTCAdminClient({
  url: "https://openrtc.example.com",
  token: process.env.OPENRTC_ADMIN_TOKEN!,
});

await admin.createRoom({
  id: "tenant-a:canvas-1",
  defaultAccesses: ["room:read", "room:presence:write"],
});

await admin.setPresence(
  "tenant-a:canvas-1",
  "agent-1",
  { status: "active", cursor: { x: 120, y: 240 } },
  { ttlSeconds: 60 },
);

const active = await admin.activeUsers("tenant-a:canvas-1");
await admin.createThread("tenant-a:canvas-1", {
  comment: {
    userId: "user-1",
    body: { type: "text", text: "Ready for review" },
  },
});
await admin.triggerInboxNotification({
  userId: "user-1",
  kind: "$custom",
  roomId: "tenant-a:canvas-1",
  activityData: { activeUsers: active.data.length },
});

About

No description, website, or topics provided.

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors