Framework-neutral SDK
A single createDendriStore() works in React, Vue, Svelte, and
vanilla JS. Wrap with framework helpers, never rewrite the room.
Self-hosted by default
Dendri pairs a framework-neutral TypeScript SDK with a Rust signaling server so you can ship chat, presence, multiplayer state, Yjs documents, and relay-backed WebRTC without renting somebody else’s realtime cloud.
// Install the framework-neutral client
// $ npm i @afterrealism/dendri-client
import { createDendriStore } from "@afterrealism/dendri-client";
const store = createDendriStore({
host: "127.0.0.1",
port: 9876,
secure: false,
path: "/",
});
store.join("project-room");
store.setPresence({ name: "Ari" });
store.broadcast({ type: "chat", text: "hello team" });
What you get
Dendri ships the building blocks you would otherwise stitch together from signaling vendors, presence services, CRDT relays, and TURN providers.
Features
One SDK and one server. No proprietary cloud, no per-message billing, no lock-in.
A single createDendriStore() works in React, Vue, Svelte, and
vanilla JS. Wrap with framework helpers, never rewrite the room.
No hidden hosted defaults. Your app always points at a signaling server you operate — container, VM, or bare metal.
Direct P2P first, then STUN, TURN, and a TLS relay through your server when carriers and NATs get in the way.
Subscribe to a room snapshot of peers, presence maps, and connection state with end-to-end TypeScript types.
Request/response semantics with message acknowledgement. Build commands, not hopeful broadcasts.
@afterrealism/dendri-client-y ships a provider that pipes Yjs
updates over Dendri topics — collaborative docs in a few lines.
Send Uint8Array chunks for assets, audio frames, or CRDT updates
with no JSON overhead. Optional msgpack serializer.
Rooms survive host churn. Relay-mode payloads stay end-to-end encrypted so your server never sees plaintext.
Issue scoped room tokens. Forward connect, disconnect, and lifecycle events to your backend over signed webhooks.
Configuration as code
No dashboards, no consoles. Versioned with your repo, reviewed in PRs, deployed with the rest of your app.
import { createDendriStore } from "@afterrealism/dendri-client";
import { DendriYjsProvider } from "@afterrealism/dendri-client-y";
import * as Y from "yjs";
export const store = createDendriStore({
// Point at any signaling server you operate
host: "signaling.your-domain.dev",
port: 443,
secure: true,
path: "/",
// Issue scoped JWT tokens from your backend
auth: { token: await fetchRoomToken() },
// 4-tier fallback: P2P → STUN → TURN → TLS relay
transport: {
iceServers: [{ urls: "stun:stun.your-domain.dev:3478" }],
relay: { e2e: true },
},
// Optional: enable msgpack for binary throughput
codec: "msgpack",
});
// Bridge a Yjs document over the same room
const doc = new Y.Doc();
export const provider = new DendriYjsProvider({ room: store, doc });
store.join("workspace-42");
Localhost in dev, your VM in staging, a Quadlet behind Traefik in prod. Same client, different host.
Mint short-lived JWTs from your existing backend. Dendri verifies scoped room access, never owns your user table.
Swap STUN servers, force relay, opt into msgpack for tight binary loops. Defaults stay sane out of the box.
The same store backs chat, presence, RPC, and Yjs documents.
No separate services to wire up.
Framework integrations
The same createDendriStore() snapshot model, four idiomatic
bindings.
Vanilla JS integration
Examples
Every demo lives in dendri-examples/ and uses the framework-neutral
packages directly.
Topic-routed messages with presence-aware sender identity.
PresenceSnapshot-based presence list with typed metadata per peer.
CursorsSmooth cursor sync over high-frequency presence updates.
WhiteboardShared canvas backed by Yjs CRDT with persistence hooks.
DocsRich text editing using the DendriYjsProvider bridge.
Geospatial layers synced live across all peers in a room.
GameAuthoritative-host game loop with binary state diffs.
FilesChunked binary transport with backpressure-aware delivery.
VideoMedia tracks negotiated over Dendri-managed peer connections.
Local setup
cd dendri-server
cargo run -- \
--host 127.0.0.1 \
--port 9876 \
--enable_relay \
--allow_discovery
npm install @afterrealism/dendri-client @afterrealism/dendri-client-y
import { createDendriStore }
from "@afterrealism/dendri-client";
const store = createDendriStore({
host: "127.0.0.1",
port: 9876,
secure: false,
path: "/",
});
store.join("project-room");
Task recipes
Publish and subscribe by topic for clean message routing.
const unsubChat = store.subscribe("chat", (data, peerId) => {
console.log("chat", peerId, data);
});
store.broadcast(
{ type: "chat", text: "hello team" },
{ topic: "chat" },
);
Sync user metadata and read all peers from snapshot state.
store.setPresence({
name: "Ari",
cursor: [314, 128],
color: "#67e8f9",
});
store.subscribe(() => {
const { presences } = store.getSnapshot();
console.log("presence map", presences);
});
Bridge Yjs updates over Dendri topics using
@afterrealism/dendri-client-y.
import { DendriYjsProvider } from "@afterrealism/dendri-client-y";
import * as Y from "yjs";
const doc = new Y.Doc();
const provider = new DendriYjsProvider({ room: store, doc });
store.join("shared-doc");
// cleanup: provider.destroy(); doc.destroy(); store.destroy();
Send chunks for assets or CRDT updates without JSON overhead.
const bytes = new Uint8Array([1, 2, 3, 4]);
store.broadcastBinary(bytes, { topic: "asset-chunk" });
store.subscribe("asset-chunk", (payload) => {
if (payload instanceof Uint8Array) {
console.log("binary bytes:", payload.byteLength);
}
});
Validation and deployment
cd e2e
npm install
npm run test:examples-local
Drives Playwright against Svelte, React, and Vue example apps plus the local signaling server.
cd dendri-infrastructure/deploy
# Quadlet units + bootstrap.sh
# for VM rollout
Use the deployment folder when you need a packaged service topology.
cd dendri-infrastructure/terraform
# Alibaba + Cloudflare
# Terraform modules
Start from Terraform modules if you need managed infrastructure provisioning.
Open source
Every package in the Dendri stack is MIT-licensed and lives under one GitHub organization. Read it, fork it, contribute back.
Framework-neutral SDK and Yjs provider. The two npm packages every Dendri app installs.
The signaling, relay, and webhook server. Single binary, container, or Quadlet unit.
Chat, presence, cursors, video, whiteboard, collaborative editor, and more — all runnable.
Infrastructure modules, deployment recipes, and the broader realtime tooling family.
Pair a tiny SDK with a Rust server and ship collaboration features without betting your roadmap on someone else’s pricing page.