016ca2c59bf2c678c676c5df5b22baef23db8310
The socialStore WS-driven handlers (addFriendFromAccepted, addIncomingRequest, removeFriendLocally, removeRequestById, updateFriendPresence) used instance-local id:origin composite keys for deduplication. When the client is connected to multiple instances, both fire WS events for the same federated user with different local IDs, bypassing the dedup and creating duplicate entries. Switch all handlers to use homeUserId??id (canonical identity), matching the pattern loadFriends/loadRequests already use. Also replace the loadRequests() re-fetch in updateFriendRequest with optimistic canonical removal to avoid racing S2S relay propagation.
Backspace
Open-source, self-hosted Discord alternative built with TypeScript.
Features
- Real-time text messaging with WebSocket
- Servers, channels, and role-based permission management
- Voice and video chat via LiveKit
- Screen sharing with configurable quality (VP9)
- Direct messages (1-on-1 and group DMs up to 10)
- DM voice/video calls with ringing
- Friend system with requests
- File uploads and image sharing
- Markdown message formatting with syntax highlighting
- Message reactions, replies, and editing
- Typing indicators, presence status, and read states
- Invite system with shareable codes
- Instance-level admin panel (streaming limits)
- Desktop app (Electron)
- Mobile-responsive web UI
- Docker deployment
Tech Stack
| Layer | Technology |
|---|---|
| Backend | Fastify + TypeScript |
| Database | SQLite (better-sqlite3) + Drizzle ORM |
| Auth | JWT + bcrypt |
| Real-time | WebSocket (ws) |
| Frontend | React 18 + Tailwind CSS + Zustand |
| Voice/Video | LiveKit |
| Desktop | Electron |
| Build | Vite + pnpm workspaces |
Quick Start with Docker
# Clone the repository
git clone https://github.com/your-username/backspace.git
cd backspace
# Create environment file
cp .env.example .env
# Generate a JWT secret
echo "JWT_SECRET=$(openssl rand -hex 32)" >> .env
# Start Backspace
docker compose up -d
Open http://localhost:3000 in your browser. A default server "Backspace" is created automatically.
Default admin account: admin / admin123 (change this after first login).
Development Setup
Prerequisites
- Node.js 20+
- pnpm 8+
Install
pnpm install
Configure
cp .env.example .env
# Edit .env with your settings (generate a JWT_SECRET)
Run
# Start both server and web dev server
pnpm dev
# Or start individually
pnpm dev:server # API server on :3005
pnpm dev:web # Vite dev server on :5173
Build
pnpm build
This builds the shared types, server, and web frontend. The server serves the built frontend in production mode.
Project Structure
Backspace/
├── packages/
│ ├── shared/ # Shared TypeScript types & permissions
│ ├── server/ # Fastify API + WebSocket server
│ ├── web/ # React frontend (Vite + Tailwind)
│ └── desktop/ # Electron desktop app
├── data/ # SQLite DB + uploads (created at runtime)
├── Dockerfile
├── docker-compose.yml
└── .env.example
Voice & Video
Voice and video requires a LiveKit server. Set these in your .env:
LIVEKIT_URL=wss://your-livekit-server
LIVEKIT_API_KEY=your-api-key
LIVEKIT_API_SECRET=your-api-secret
Without LiveKit configured, text chat works fully but voice/video channels will not connect.
API
The server exposes a REST API and WebSocket endpoint:
- REST API:
http://localhost:3000/api/* - WebSocket:
ws://localhost:3000/ws - Health check:
GET /api/health
See CLAUDE.md for the full API reference.
Desktop App
The Electron desktop app wraps the web UI and adds system tray, notifications, and native window controls.
cd packages/desktop
pnpm build:ts # Compile TypeScript
pnpm dev # Run in development
pnpm build # Package for distribution
License
MIT
Languages
TypeScript
96.8%
HTML
1.3%
Shell
1.1%
JavaScript
0.4%
CSS
0.3%