Skip to content

Repository files navigation

CodeQuorum

A real-time collaborative code editor — share a link and code together, live.

CI License: MIT Node Tests


CodeQuorum editor

About

CodeQuorum is a browser-based, multi-file collaborative IDE. Multiple developers share a room and edit the same workspace — code files and Jupyter notebooks — at the same time. Every keystroke syncs in real time, remote cursors and selections appear inline with each collaborator's name, and edits merge conflict-free even when people type in the same spot simultaneously.

It's built on a CRDT (Yjs) rather than naive last-write-wins broadcasting, so concurrent edits never clobber each other or corrupt the document — the same approach used by production collaborative editors.

Features

Real-time collaboration

  • Conflict-free editing — Yjs CRDTs; concurrent edits always converge.
  • Live presence & cursors — see who's in the room, where they're typing, and which file they're in, color-coded per user.
  • Follow mode — click a collaborator to follow their cursor and file switches live (à la VS Code Live Share).
  • Spotlight / Present — pull everyone in the room to follow you (à la Figma).
  • Inline comments — comment on a selection; threaded, resolvable, and CRDT-anchored so they stick to the code as it moves (à la Google Docs).
  • Shareable rooms — create a room, send the link, collaborate. Works anonymously.
  • Collaborative undo/redo — your undo reverts only your changes.

Accounts & access control

  • Optional accounts — email/password (scrypt) or GitHub OAuth; sign up for a dashboard of your rooms. Anonymous link-sharing still works.
  • Public / read-only / private rooms — owners control who can view and who can edit.
  • Server-enforced authorization — private rooms reject non-members and read-only is enforced on the server, never just hidden in the UI.
  • Invite members by email with editor or viewer roles.

IDE productivity

  • Project-wide search (Ctrl/⌘+Shift+F) — search across every file, grouped results, jump-to-line.
  • Version history — automatic + manual snapshots of a room; restore any past version into a new room.
  • Command palette, find & replace, tabbed editor, folder tree, fork, templates (see below).

Multi-file workspace ("full codebases")

  • File explorer with folders — organize files into a tree using / paths; create, rename, delete.
  • Tabbed editor — open multiple files at once; your open tabs persist across reloads.
  • 14 languages, syntax-highlighted — JS, TS, JSX, Python, HTML, CSS, JSON, Markdown, C/C++, Java, Rust, PHP, SQL, XML — auto-detected from the file extension.
  • Upload & export — drag files into a room, download a single file, or export the entire workspace as a .zip.

Collaborative Jupyter notebooks (.ipynb)

  • Real notebook UI — code and markdown cells; add, move, and delete cells.
  • Executable cells, JS or Python — switch the kernel per notebook; run cells (▶ / Shift+Enter) with output inline. Python runs via a self-hosted Pyodide runtime with a persistent shared kernel (variables carry across cells, like Jupyter).
  • Live markdown rendering — rendered with sanitized HTML (XSS-safe for shared input).
  • Import & export real .ipynb — interoperates with Jupyter / nbviewer; outputs are preserved on round-trip.
  • Every cell is collaborative — multiple people edit different cells of the same notebook at once.

Run, preview & productivity

  • Run JavaScript — executes in a sandboxed Web Worker with captured console output and an infinite-loop timeout (Ctrl/⌘+Enter).
  • Live HTML preview with a console — renders in a sandboxed, locked-down iframe (its own CSP), inlining sibling CSS/JS from the workspace, and captures the page's console/errors into a console pane (à la CodeSandbox).
  • Markdown preview — rendered side-by-side for .md files.
  • Starter templates — scaffold a room as a web page, JS script, or notebook (à la CodePen).
  • Fork a workspace — one-click duplicate any room into your own copy (à la Replit).
  • Command palette (Ctrl/⌘+P) — fuzzy go-to-file and run commands.
  • Find & replace (Ctrl/⌘+F) — full CodeMirror search.
  • In-room chat — talk to collaborators without leaving the editor; messages persist with the room.

Platform

  • Persistent rooms — workspaces and chat are saved to disk and restored after a server restart.
  • Resilient connections — automatic reconnection with a live status indicator.
  • Polished, framework-free UI — toasts and modal dialogs (no native alert/prompt), keyboard-driven, responsive on desktop and mobile.

Tech stack

Layer Technology
Editor CodeMirror 6
Realtime sync Yjs (CRDT) + y-codemirror.next
Transport Socket.IO (WebSocket, with polling fallback)
Server Express on Node.js 22+
Database node:sqlite (built-in, parameterized queries, versioned migrations)
Auth scrypt password hashing + JWT cookies, GitHub OAuth; zod validation
Code execution sandboxed Web Worker (JS) · self-hosted Pyodide (Python, shared kernel)
Security helmet (CSP/HSTS/…), express-rate-limit
Notebooks marked + DOMPurify; JSZip for export
Build / DX esbuild bundler · TypeScript typed protocol · ESLint · node:test · GitHub Actions CI · Docker

The client is bundled into one self-contained file and the database is embedded; the Python runtime is self-hosted too — so no third-party CDN is required.

Getting started

Prerequisites

  • Node.js ≥ 22 (uses the built-in node:sqlite)

Installation

git clone https://github.com/nishantkluhera/CodeQuorum.git
cd CodeQuorum
npm install
cp .env.example .env   # then set JWT_SECRET
npm start

npm start bundles the client and launches the server. Open http://localhost:3000 — create a room as a guest, or sign up for a dashboard with private/read-only rooms.

To collaborate, open the room URL in a second browser/tab (or send it to a friend) and start typing — changes appear instantly in both.

Configuration

Variable Default Purpose
PORT 3000 HTTP port
JWT_SECRET dev fallback (required in prod) signs session cookies
NODE_ENV development production enables Secure cookies + requires JWT_SECRET
DB_PATH ./data/codequorum.db SQLite database file

How it works

 Browser A ──┐                              ┌── Browser B
 CodeMirror  │   Yjs update (binary)        │  CodeMirror
   │ yCollab │ ───────────────────────────► │  yCollab │
 Yjs doc ────┤        Socket.IO             ├──── Yjs doc
 awareness ──┘  ◄─────────────────────────  └── awareness
                         │
                    ┌────▼──────────┐
                    │    Server     │  • REST: auth + room management
                    │ Express + IO  │  • authoritative Yjs doc per room
                    │               │  • authz: private/read-only enforced here
                    └────┬──────────┘  • debounced persistence
                         │
                    SQLite (node:sqlite)
                    users · rooms · members · documents(blob)

Each client holds a local Yjs document and connects its active editor to it via yCollab. Local edits are encoded as compact binary updates and sent over Socket.IO. The server keeps an authoritative Yjs document per room: it merges every incoming update, rebroadcasts it, and hands the full current state to late joiners. Cursor/name info travels over the Yjs awareness protocol on the same channel. Document state is persisted to the SQLite documents table (debounced) so rooms survive restarts; idle empty rooms are evicted from memory.

Authorization is enforced server-side. On join, the server resolves the caller's role from the room's visibility and membership; private rooms reject non-members, and a viewer's edits are dropped on the server — the client UI is never trusted to gate access.

The whole workspace lives in that one CRDT doc, so multi-file and notebooks need no special server support — the server just relays the doc:

  • a files Y.Map holds file metadata (id → {name, kind, language}),
  • each code file's body is a Y.Text (content:<id>),
  • each notebook is a Y.Array of cells (cells:<id>), where each cell is a Y.Map with its own Y.Text source.

Switching files re-binds a single editor to that file's shared type; notebook cells each bind their own editor. See src/server.js for the wire protocol and client/workspace.js for the model.

Project structure

CodeQuorum/
├── build.js              # esbuild bundler for the browser client
├── src/
│   ├── server.js         # Express + Socket.IO; serves API + realtime + authz
│   ├── api.js            # REST API: auth + room management (zod-validated)
│   ├── auth.js           # password hashing, JWT cookies, middleware
│   ├── db.js             # node:sqlite connection + versioned migrations
│   ├── repo.js           # data access (parameterized queries) + role logic
│   └── rooms.js          # per-room Yjs docs, DB persistence, eviction
├── client/               # browser ES modules (bundled by esbuild)
│   ├── main.js           # app entry: doc/socket/awareness, tabs, permissions
│   ├── workspace.js · codeview.js · notebook.js · ipynb.js
│   ├── runner.js         # sandboxed JS run + HTML/markdown preview
│   ├── pyrun.js          # Python execution via the self-hosted Pyodide worker
│   ├── templates.js      # starter scaffolds (web / node / notebook)
│   ├── chat.js · palette.js · ui.js · editor.js · icons.js
├── public/
│   ├── index.html · landing.js   # auth-aware landing + dashboard
│   ├── room.html · styles.css    # the IDE
│   ├── pyworker.js               # Pyodide Web Worker (persistent kernel)
│   └── dist/bundle.js            # generated client bundle (git-ignored)
├── shared/
│   └── protocol.ts       # typed wire-protocol + domain contract (type-checked)
├── test/
│   ├── rooms.test.js     # room/CRDT + DB persistence
│   ├── auth.test.js      # password hashing + room access-control roles
│   └── ipynb.test.mjs    # .ipynb parse/serialize round-trip
├── scripts/smoke-collab.js       # end-to-end two-client collaboration test
├── Dockerfile · docker-compose.yml · .github/workflows/ci.yml
└── SECURITY.md           # threat model

Scripts

Command Description
npm start Build the client and start the server
npm run dev Rebuild on change + run the server
npm test Run the unit/integration test suite (node:test)
npm run typecheck Type-check the shared protocol (tsc --noEmit)
npm run lint Lint with ESLint

End-to-end check

With the server running (PORT=3010 npm start), in another shell:

URL=http://localhost:3010 node scripts/smoke-collab.js

Two clients join one room (real multi-file model) and assert that a late joiner sees existing files and contents, edits propagate both ways, concurrent file creation converges, chat syncs, and the workspaces end up identical.

Security

CodeQuorum runs user-supplied code and renders user-supplied HTML/Markdown, so isolation is a first-class concern. Highlights: server-side authorization, scrypt+JWT auth, parameterized SQL, zod validation, rate limiting, a tailored helmet CSP, JS execution in a timed Web Worker, HTML preview in a sandboxed iframe, and DOMPurify-sanitized markdown. Full threat model: SECURITY.md.

Deployment

Containerized — node:sqlite means no external database to provision.

echo "JWT_SECRET=$(node -e "console.log(require('crypto').randomBytes(48).toString('hex'))")" > .env
docker compose up --build       # → http://localhost:3000, data persisted in a volume

CI (GitHub Actions) runs lint, type-check, tests, a build, and a production npm audit on every push. The Docker image deploys as-is to Fly.io, Render, Railway, or any container host.

Possible extensions

  • Python/notebook execution via Pyodide (JS execution & HTML preview already ship)
  • Horizontal scale: Socket.IO Redis adapter + Postgres (swap the repo/rooms layer)
  • OAuth sign-in, email verification, password reset
  • Completing the TypeScript migration from shared/protocol.ts outward

License

Distributed under the MIT License. See LICENSE for details.

About

A real-time collaborative code editor currently under development

Resources

Security policy

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages