A modern, privacy‑first, whiteboard with realtime collab, E2EE, and integrated video chat.
Three modes
Solo — just open and draw. End‑to‑end encrypted (E2EE); nothing leaves your device unencrypted.
apps/DrawDeck (frontend), apps/ws (WebSocket), apps/rtc (RTC signaling)..
├─ apps/
│ ├─ DrawDeck/ # Next.js frontend (client + minimal server routes)
│ ├─ ws/ # WebSocket server (rooms, shapes broadcast, rate limit)
│ └─ rtc/ # RTC signaling server (for Duo video)
├─ packages/ # Shared packages
├─ docker/
│ ├─ Dockerfile.frontend
│ ├─ Dockerfile.websocket
│ └─ Dockerfile.rtc
├─ turbo.json # Turborepo pipelines
├─ pnpm-workspace.yaml
└─ .github/workflows/ # CI/CD
Prereqs
pnpm install
pnpm dev
By default this will run:
You can also start services individually:
bash pnpm --filter @app/drawdeck dev # or: cd apps/DrawDeck && pnpm dev pnpm --filter @app/ws dev # or: cd apps/ws && pnpm start pnpm --filter @app/rtc dev # or: cd apps/rtc && pnpm start
Environment variables are provided via .env.local in each app (never commit secrets). Keep a public template at .env.example.
apps/DrawDeck)NEXT_PUBLIC_WS_URL=
NEXT_PUBLIC_RTC_URL=
NEXT_PUBLIC_SITE_URL=
# NextAuth (optional locally)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
NEXTAUTH_URL=
NEXTAUTH_SECRET=
apps/ws)PORT=
# rate limiting, secrets, etc. (implementation-specific)
apps/rtc)PORT=
┌────────────┐ WebRTC (P2P media) ┌────────────┐
│ Client A │◀──────────────────────────▶│ Client B │
│ (Duo) │ │ (Duo) │
└─────┬──────┘ Signaling (HTTP/WS) └─────┬──────┘
│ │
│ Shapes/events (WS, encrypted) │
▼ ▼
┌───────────────────────────────────┐
│ WS Server (apps/ws) │
│ - Rooms & membership │
│ - Broadcast shapes/events │
│ - Rate limit & IP cache │
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ RTC Signaling │
│ (apps/rtc) │
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ Next.js Frontend │
│ (apps/DrawDeck) │
└───────────────────────────────────┘
Next.js 15 Frontend-First — runs both client UI and lightweight server routes. Backend services (ws, rtc) are kept minimal.
No Mandatory Auth — anyone can instantly start drawing in Solo mode. Auth (Google OAuth via NextAuth) is only needed for collaboration or org setups.
Local-Only Solo Mode — drawings are stored locally in the browser and never touch a server.
Peer-to-Peer Collaboration — Duo sessions use WebRTC for direct audio/video and E2EE drawing sync.
Group Mode with Secure Broadcast — multiple participants sync shapes over a WebSocket layer; payloads are encrypted with a room key.
Ephemeral by Design — no canvas history is persisted unless you extend it. All sessions are temporary.
Encrypted Everywhere — all drawing data is encrypted client-side with room keys; servers only relay ciphertext.
Hook-based WebSocket Client — a clean React abstraction for connecting, syncing shapes, and handling backpressure.
Rate Limiting + Queueing — IP-aware rate limiting server-side; client has a message queue with retry/backoff.
1..0 map to tools (hand/select/shape…)rate_limit_exceeded with retryAfter.Build images (from repo root):
# Frontend
docker build -f docker/Dockerfile.frontend -t acidop/drawdeck-frontend:dev .
# WebSocket
docker build -f docker/Dockerfile.websocket -t acidop/drawdeck-ws:dev .
# RTC
docker build -f docker/Dockerfile.rtc -t acidop/drawdeck-rtc:dev .
Run containers:
# Frontend (port 3000)
docker run -d --name drawdeck-frontend -p port-machine:port-locally \
-e NEXT_PUBLIC_WS_URL=ws://your-host:port-ws \
-e NEXT_PUBLIC_RTC_URL=http://your-host:port-rtc \
-e NEXT_PUBLIC_SITE_URL=http://your-host:port \
-e GOOGLE_CLIENT_ID=... -e GOOGLE_CLIENT_SECRET=... \
-e NEXTAUTH_URL=http://your-host:port -e NEXTAUTH_SECRET=... \
acidop/drawdeck-frontend:dev
# WebSocket (port )
docker run -d --name drawdeck-ws -p machine-port:port-ws acidop/drawdeck-ws:dev
# RTC (port 8081)
docker run -d --name drawdeck-rtc -p machine-port:port-rtc acidop/drawdeck-rtc:dev
frontend, ws, rtc).latest).Common root scripts:
pnpm build — Turborepo build for all appspnpm dev — start dev serverspnpm lint — lint all packagespnpm check-types — typecheckpnpm start:client — start frontend (production)pnpm start:ws — start WebSocket serverpnpm start:rtc — start RTC signaling serverPRs and issues welcome! Please:
main.pnpm install and run pnpm dev to reproduce.MIT. See LICENSE.
$ claude mcp add DrawDeck \
-- python -m otcore.mcp_server <graph>