Skip to content

Peetah002/concord

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Concord

My friend group and I had grown tired of Discord's quirks, and as we say in Italy chi fa da sé fa per tre. So I built a full-stack Discord clone: text/voice servers, DMs, P2P video and screen sharing, roles, presence — a single project to tackle the typical problems of a real-time multi-user app.

The backend is NestJS + Prisma + PostgreSQL with Socket.IO for chat, presence and WebRTC signaling; the frontend is Next.js 15 + React 19 + Zustand. Voice and video go peer-to-peer over a WebRTC mesh, auth is JWT access token + httpOnly refresh cookie with rotation, and permissions are a bitmask checked in O(1).

The result is a working Discord-like app with optimistic UI, cursor-paginated messages, an LRU client cache, magic-byte upload validation, and reconnect-safe sockets. The only open limitation is the in-memory StateService: scaling past one instance requires moving it to Redis.

Run with npm install && npm run dev (client :3000, server :3001).


Features

  • Servers and channels: server creation, text and voice channels, ordering, icons.
  • Messages: send, edit, delete, reply threading, emoji reactions, attachments with preview.
  • DMs: persistent 1-to-1 conversations with the same UX as channels.
  • Voice and video: join/leave voice channel, mute, speaking indicator, screen sharing, mesh P2P.
  • Presence: ONLINE / IDLE / DND / OFFLINE states, unread badges for both channels and DMs.
  • Permissions: bitmask roles (SEND=1, MANAGE_MSG=2, MANAGE_CH=4, MANAGE_ROLES=8, KICK=16, BAN=32, MANAGE_SERVER=64, ADMIN=128); the server owner bypasses every check.
  • Invites: reusable codes with usage/expiry limits, per-server ban list.
  • Auth: JWT access token (header) + refresh token in httpOnly cookie, rotation on refresh.

Case study: what was interesting

  1. Presence/voice cleanup on disconnect — handled in PresenceGateway.handleDisconnect. This is the spot where it is easiest to leave "ghost" users online.
  2. Reconnect without losing roomsactiveRooms tracked on the socket layer avoids having to reload state on every network glitch.
  3. Bitmask permissions — replace N joins: a single Int on Role, checked with & in O(1).
  4. Client-side LRU cache — the naive "remember every message I've ever seen" cache grows forever; 15 channels is the sweet spot found between UX and RAM.
  5. WebRTC mesh — trivial to implement but dies past ~6 users: an accepted trade-off because the use case is "small groups".
  6. Optimistic UI with clientId reconciliation — the server re-emits the original clientId, so the client can swap the optimistic message in O(1) without flicker.
  7. Magic-byte upload validation — the client Content-Type is not trustworthy; file-type reads the actual leading bytes of the file.
  8. Non-scalable in-memory state — known and documented limitation: going beyond a single instance requires moving StateService onto Redis.

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages