A real-time collaborative cloud IDE that enables multiple developers to write, edit, and execute code together in the browser. Built with a focus on engineering fundamentals — authentication, role-based access control, live socket synchronization, and a persistent file system.
- Overview
- Features
- Tech Stack
- System Architecture
- Project Structure
- Getting Started
- Environment Variables
- Email Setup
- API Reference
- Socket Events
- Database Schema
- Roadmap
CodeMesh is a browser-based collaborative development environment. Users can create workspaces called rooms, invite team members, and write code together in real time. Every keystroke is broadcast to collaborators via WebSockets, files persist on the server disk, and code can be executed directly from the browser.
The project was built from scratch with no starter templates. Every layer — from JWT authentication to recursive folder deletion to debounced autosave — was designed and implemented manually.
- Register, login, logout with JWT stored in httpOnly cookies
- Token blacklist on logout using a dedicated model
- Protected routes on both frontend and backend
- Session persistence via getMe endpoint
- Welcome email sent on registration via Gmail OAuth2
- Create and join rooms via unique room codes
- Public and private room visibility
- Shareable invite links
- Room metadata including description, language, and last activity
- Three roles per room: Admin, Editor, Viewer
- Middleware-enforced permissions on every API route
- Admins can manage participants and delete rooms
- Viewers have read-only access to the editor
- Live code synchronization via Socket.IO
- Debounced autosave triggers a disk write after 2 seconds of idle typing
- File explorer syncs across all participants instantly on create, rename, and delete
- Presence system shows which users are online and which file they are editing
- Live chat inside each room with message broadcast
- VS Code-style file explorer with nested folder support
- Files stored on server disk, metadata stored in MongoDB
- Create, rename, and delete files and folders
- Recursive folder deletion cleans up all children
- Language inference from file extension for Monaco syntax highlighting
- Monaco Editor integration with syntax highlighting for JavaScript, TypeScript, Python, C++, Java, HTML, CSS, and JSON
- Remote change injection using imperative setValue to prevent echo loops
- Cursor position preserved across file switches
- Execute JavaScript, Python, C++, and Java from the browser
- Output streamed to an in-browser console panel
- Timeout and error handling per execution
- Node.js with Express
- MongoDB with Mongoose
- Socket.IO for real-time communication
- JSON Web Tokens for authentication
- bcrypt for password hashing
- Zod for request validation
- Nodemailer with Gmail OAuth2 for transactional email
- express-async-handler for error propagation
- React with Vite
- Tailwind CSS for styling
- Monaco Editor via @monaco-editor/react
- Socket.IO client
- Axios for HTTP requests
- React Router for navigation
- React Context for global auth state
Browser (React)
|
|--- HTTP (Axios) -----------> Express REST API
| |
|--- WebSocket (Socket.IO) --------> Socket Server
|
MongoDB (metadata)
Disk (file content)
User A types in Monaco Editor
|
v
handleCodeChange()
|
|--- socket.emit('code-change') --> Server
| |
|--- debounce 2s |--- socket.to(room).emit('receive-change')
| |
v v
socket.emit('save-file') User B Monaco
| setValue() called
v imperatively
Server writes to disk
Server updates lastEditedBy
codemesh/
|
|-- backend/
| |-- src/
| | |-- controllers/
| | | |-- auth.controller.js
| | | |-- room.controller.js
| | | |-- project.controller.js
| | |
| | |-- services/
| | | |-- project.service.js
| | | |-- workspace.service.js
| | |
| | |-- models/
| | | |-- user.model.js
| | | |-- room.model.js
| | | |-- participant.model.js
| | | |-- projectItem.model.js
| | | |-- blacklist.model.js
| | |
| | |-- routes/
| | | |-- auth.routes.js
| | | |-- room.routes.js
| | | |-- project.routes.js
| | |
| | |-- sockets/
| | | |-- room.socket.js
| | |
| | |-- middlewares/
| | | |-- auth.middleware.js
| | | |-- validate.middleware.js
| | | |-- role.middleware.js
| | | |-- room.middleware.js
| | |
| | |-- validations/
| | | |-- auth.validation.js
| | | |-- room.validation.js
| | | |-- project.validation.js
| | |
| | |-- utils/
| | | |-- buildPath.js
| | | |-- project.helper.js
| | | |-- socket.helper.js
| | | |-- getFileLanguage.js
| | | |-- email.js
| | |
| | |-- app.js
| | |-- server.js
| |
| |-- workspace/ <- real files live here, one folder per room
| |-- .env
| |-- package.json
|
|-- frontend/
| |-- src/
| | |-- pages/
| | | |-- auth/
| | | | |-- Login.jsx
| | | | |-- Register.jsx
| | | |-- dashboard/
| | | | |-- Dashboard.jsx
| | | |-- room/
| | | |-- Room.jsx
| | |
| | |-- components/
| | | |-- ui/
| | | | |-- Input.jsx
| | | | |-- Button.jsx
| | | | |-- Loading.jsx
| | | |-- room/
| | | | |-- RoomHeader.jsx
| | | | |-- FileExplorer.jsx
| | | | |-- EditorTabs.jsx
| | | | |-- Collaborators.jsx
| | | | |-- ChatPanel.jsx
| | | | |-- ConsolePanel.jsx
| | | |-- editor/
| | | |-- CodeEditor.jsx
| | |
| | |-- layouts/
| | | |-- AuthLayout.jsx
| | |
| | |-- services/
| | | |-- auth.service.js
| | | |-- room.service.js
| | | |-- project.service.js
| | | |-- execution.service.js
| | |
| | |-- context/
| | | |-- AuthContext.jsx
| | |
| | |-- lib/
| | | |-- axios.js
| | | |-- server.js <- socket client
| | |
| | |-- utils/
| | | |-- helpers.js
| | |
| | |-- App.jsx
| | |-- main.jsx
| |
| |-- index.html
| |-- tailwind.config.js
| |-- vite.config.js
| |-- package.json
|
|-- README.md
- Node.js 18 or higher
- MongoDB running locally or a MongoDB Atlas URI
- npm
cd backend
npm installCreate a .env file in the backend root (see Environment Variables section below).
npm run devThe backend starts on http://localhost:3000.
cd frontend
npm install
npm run devThe frontend starts on http://localhost:5173.
Create a .env file in the backend/ directory with the following:
PORT=3000
MONGO_URI=mongodb://localhost:27017/codemesh
JWT_SECRET=your_jwt_secret_key_here
NODE_ENV=development
EMAIL_USER=[email protected]
CLIENT_ID=your_google_oauth_client_id
CLIENT_SECRET=your_google_oauth_client_secret
REFRESH_TOKEN=your_refresh_tokenCodeMesh sends a welcome email on registration using Gmail OAuth2 via Nodemailer.
- Go to https://console.cloud.google.com and create a new project
- Navigate to APIs and Services and enable the Gmail API
- Go to Credentials and create an OAuth2 client ID
- Select Desktop app as the application type
- Copy the Client ID and Client Secret
- Open https://developers.google.com/oauthplayground
- Click the settings icon in the top right
- Check "Use your own OAuth credentials"
- Enter your Client ID and Client Secret
- In the scope input on the left, enter
https://mail.google.com - Click Authorize APIs and sign in with your Gmail account
- Click Exchange authorization code for tokens
- Copy the Refresh Token from the response
Add all four values to your .env file:
EMAIL_USER=[email protected]
CLIENT_ID=your_google_oauth_client_id
CLIENT_SECRET=your_google_oauth_client_secret
REFRESH_TOKEN=your_refresh_tokenImport the named export in your controller:
const { sendRegisterEmail } = require('../utils/email.js')
// call after user is created — non-blocking
sendRegisterEmail(user.email, user.name).catch(err =>
console.error('Welcome email failed:', err)
)The email is sent asynchronously and does not block the registration response. If the email service is unavailable, the user is still registered successfully.
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/auth/register | Register a new user | No |
| POST | /api/auth/login | Login and receive JWT cookie | No |
| GET | /api/auth/me | Get current authenticated user | Yes |
| POST | /api/auth/logout | Logout and blacklist token | Yes |
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/room/create | Create a new room | Yes |
| GET | /api/room | Get all rooms for current user | Yes |
| GET | /api/room/:roomCode | Get a single room | Yes |
| POST | /api/room/join/:roomCode | Join a room | Yes |
| GET | /api/room/:roomCode/participants | Get room participants | Yes |
| PATCH | /api/room/leave/:roomCode | Leave a room | Yes |
| DELETE | /api/room/:roomCode/delete | Delete a room (creator only) | Yes |
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /api/project/:roomCode | Get full file tree | Yes |
| POST | /api/project/:roomCode/file | Create a file | Yes |
| POST | /api/project/:roomCode/folder | Create a folder | Yes |
| GET | /api/project/file/:fileId | Open and read a file | Yes |
| PATCH | /api/project/file/:fileId | Update file content | Yes |
| DELETE | /api/project/file/:fileId | Delete a file | Yes |
| DELETE | /api/project/folder/:folderId | Delete a folder recursively | Yes |
| Event | Payload | Description |
|---|---|---|
| join-room | { roomCode } | Join a collaboration room |
| leave-room | { roomCode } | Leave a room |
| code-change | { roomCode, fileId, content } | Broadcast live code change |
| save-file | { roomCode, fileId, content } | Trigger autosave to disk |
| file-created | { roomCode, file } | Notify room of new file |
| file-deleted | { roomCode, fileId } | Notify room of deleted file |
| file-renamed | { roomCode, fileId, newName } | Notify room of rename |
| folder-created | { roomCode, folder } | Notify room of new folder |
| folder-deleted | { roomCode, folderId } | Notify room of deleted folder |
| editing-file | { roomCode, fileId, fileName } | Broadcast which file user is editing |
| send-message | { roomCode, user, message, time } | Send a chat message |
| Event | Payload | Description |
|---|---|---|
| presence-update | [{ socketId, userId, name, email, status }] | Full online users list |
| user-joined | { socketId, userId, name, email } | A user joined the room |
| user-left | { socketId, userId, name } | A user left the room |
| receive-change | { fileId, content } | Live code update from collaborator |
| file-saved | { fileId } | A collaborator saved a file |
| receive-file-created | { file } | New file created by collaborator |
| receive-file-deleted | { fileId } | File deleted by collaborator |
| receive-file-renamed | { fileId, newName } | File renamed by collaborator |
| receive-folder-created | { folder } | New folder created by collaborator |
| receive-folder-deleted | { folderId } | Folder deleted by collaborator |
| editing-status | { userId, name, fileId, fileName } | Who is editing what |
| receive-message | { user, message, time, userId } | Incoming chat message |
_id, name, email, password (hashed), avatar, createdAt, updatedAt
_id, roomName, roomCode (unique), description, creator (ref User),
language, visibility, isActive, lastActivity, createdAt, updatedAt
_id, room (ref Room), user (ref User), role (admin|editor|viewer),
joinedAt, isActive, lastSeen, createdAt, updatedAt
index: { room, user } unique
_id, room (ref Room), name, type (file|folder), parent (ref ProjectItem),
language, order, createdBy (ref User), lastEditedBy (ref User),
createdAt, updatedAt
index: { room, parent, name } unique
_id, token, createdAt (TTL index for auto cleanup)
- JWT authentication with httpOnly cookies and token blacklist
- Welcome email on registration via Gmail OAuth2
- Room creation, joining, and participant management
- Role-based access control with Admin, Editor, and Viewer roles
- VS Code-style file explorer with nested folder support
- Monaco Editor with syntax highlighting for 8 languages
- Real-time code synchronization via Socket.IO
- Debounced autosave to server disk
- File explorer sync across all room participants
- Presence system showing online status and active file
- In-room live chat
- JavaScript and Python code execution with console output
- Cursor presence with colored indicators per user
- Version history with restore capability
- Yjs CRDT integration for conflict-free collaborative editing
- Rename file and folder from the explorer
- Docker sandbox for isolated code execution
- GitHub repository import
- AI code assistant for explanation, bug detection, and autocomplete
- Terminal emulator inside the room
- Package manager support (npm, pip)
- Live deployment with preview URL
- Analytics dashboard per user