OPEN SOURCE

CrossChat: Real-Time Multi-Room Chat on FastAPI WebSockets + SQLite

OCT 13, 2026·8 MIN

TL;DR: CrossChat is a real multi-room chat server: FastAPI + WebSockets, message history persisted in SQLite with replay for newcomers, room isolation, a small REST API, a minimal single-page frontend, a pytest suite that covers the real-time behavior, and Docker + Render deploy configs. Zero external services — no Firebase, no vendor lock-in, everything runs from the repo. github.com/Shivay00001/crosschat.

The problem: chat demos are either toys or traps

Real-time chat is one of those things that looks trivial and isn't. Most open-source examples fall into two camps: toy echo servers with no persistence (refresh the page and history is gone, one room, no tests), or full Firebase/Supabase builds that trade a weekend of setup for permanent vendor lock-in. What's missing is the middle: a self-hostable, real multi-room chat with persisted history, a clean protocol, and tests that prove the real-time behavior — small enough to read in an afternoon.

CrossChat is that middle. One Python backend, one SQLite file, one HTML frontend, and a test suite that actually connects WebSocket clients and asserts on message delivery.

What it does

EndpointBehavior
GET /Single-page chat UI — pick a username, join or create rooms, live messages
WS /ws/{room}/{username}Join a room; newcomers get history replay, then live join / message / leave events
GET /api/roomsRooms with message counts
GET /api/rooms/{room}/history?limit=50Persisted history (limit capped at 200)
GET /healthHealth check for deploy platforms

How it works: the WebSocket lifecycle

The core of the repo is main.py (~2.9 KB — genuinely readable end to end). A client connecting to /ws/{room}/{username} goes through this exact sequence:

  1. Validate. Room and username must match ^[A-Za-z0-9_-]{1,40}$; anything else gets a WebSocket close with code 1008 (policy violation). No anonymous rooms, no path-traversal room names, no 10 KB usernames.
  2. Accept and register. The socket is accepted and added to the room's set under an asyncio.Lock.
  3. History replay. Before anything live happens, the newcomer receives {"type": "history", "messages": [...]} — the room's persisted messages, oldest first. This is the detail toy chat servers skip, and it's what makes the app feel real.
  4. Join broadcast. Everyone in the room (including the newcomer) gets a join event.
  5. Message loop. Each received text is stripped; empties are skipped; messages are capped at 2,000 characters, saved to SQLite via db.save_message(), and broadcast to the room as {"type": "message", ...} including the database id and timestamp.
  6. Disconnect. On WebSocketDisconnect, the socket is removed under the lock and a leave event is broadcast.

Broadcast without leaks

broadcast() deserves a look: it snapshots the room's socket set under the lock, sends to each outside the lock (so one slow client can't block the others), collects dead sockets, and discards them under the lock afterward. That three-step pattern — snapshot, send, sweep — is the correct way to fan out over mutable connection sets, and it's easy to get wrong.

Persistence: one table, no ORM

db.py is refreshingly honest about its scope — its docstring is literally "SQLite message history. One table, no ORM." The schema is a single messages table (id, room, username, text, ts) with an index on (room, id) so history queries stay fast. init_db() runs CREATE TABLE IF NOT EXISTS at startup; the database path is overridable with the CROSSCHAT_DB environment variable (which is also how the tests point at a throwaway database).

The test suite proves the real-time behavior

tests/test_chat.py is the strongest signal that this repo is real rather than a sketch. Using FastAPI's TestClient, it:

Run it with python -m pytest tests/ -q. Testing WebSocket fan-out, replay, and isolation — not just the REST layer — is what separates a demo from a component you could actually build on.

How to run it

git clone https://github.com/Shivay00001/crosschat.git
cd crosschat
pip install -r requirements.txt
uvicorn main:app --port 8005
# open http://localhost:8005 in two browser windows and chat

Deployment is covered three ways: a Dockerfile + docker-compose.yml for containers, a render.yaml for one-click Render deploys (free plan, Python 3.12.3, health check on /health, CROSSCHAT_DB env var), and GitHub Actions workflows for CI and hosting deploys.

One honest note on naming: the repo's GitHub description says "powered by Firebase Data Connect," but the tree implements FastAPI + WebSockets + SQLite — and the README states it explicitly: "No external services, no Firebase — everything runs from this repo." The firebase-hosting workflows are deploy targets, not dependencies. The code is the source of truth, and the code is Firebase-free.

Honest status and roadmap

Status: working. The server boots, the frontend chats, history persists and replays, rooms are isolated, and the test suite covers the behaviors that matter. This is a real, usable chat component — suitable for internal tools, prototypes, classrooms, and as a reference implementation.

What's not here is also clear: there's no authentication (anyone can claim any username), no message editing or deletion, no typing indicators, no pagination cursors for very long histories, and a single process holds all connections in memory — fine for teams and demos, not for thousands of concurrent rooms.

Roadmap (sensible next steps):

Who should look at this repo

The frontend: small but complete

static/index.html (~5.9 KB) is a single-page app with no framework and no build step, and it covers the full chat UX: a login box for picking a username, a sidebar listing rooms with a "new room" input, a header showing the current room plus a live/offline status indicator, a message pane, and a composer. The JavaScript is careful in the ways that matter:

Protocol reference

The WebSocket protocol is small enough to document completely — a genuine virtue:

DirectionMessageShape
Server → clientHistory replay{"type": "history", "messages": [{id, room, username, text, ts}, …]}
Server → clientJoin / leave{"type": "join"|"leave", "username": …, "text": "… joined …"}
Server → clientChat message{"type": "message", "id": …, "room": …, "username": …, "text": …, "ts": …}
Client → serverChat messagePlain text (stripped; empty ignored; capped at 2,000 chars)

Every server message is JSON; every client message is raw text. Timestamps are Unix floats from the server, ids are SQLite row ids — so clients can deduplicate and order deterministically. If you wanted to write a native mobile client or a bot, this table plus the REST endpoints are the entire integration surface.

Finally, note the dependency footprint: FastAPI, uvicorn, and the standard library. No ORM, no migration framework, no message broker, no external database. That minimalism is a deliberate architectural choice — every dependency you don't take is a failure mode you don't inherit — and it's what makes the repo deployable to a free Render instance in minutes.

Frequently asked questions

Does CrossChat need Firebase?
No. It runs entirely on FastAPI, WebSockets, and SQLite. The firebase-hosting workflows are deploy targets, not runtime dependencies — the README states "no external services, no Firebase."

How many users can it handle?
It's a single-process asyncio server: plenty for teams, classrooms, and prototypes. Multi-worker scale via Redis pub/sub is on the roadmap.

Where are messages stored?
In a SQLite file (crosschat.db by default), one table, no ORM. Override the path with the CROSSCHAT_DB environment variable.

Is there authentication?
Not yet — usernames are asserted by the client. Token-based auth is the first item on the roadmap.

The bottom line

CrossChat is what a reference implementation should be: small enough to read completely, correct enough to trust the patterns, and tested on the behaviors that actually matter for real-time systems — delivery, replay, isolation. If you need chat without a cloud bill, clone it; if you're learning WebSockets, read main.py first and the tests second.

References & further reading

  1. github.com/Shivay00001/crosschat

Cite this article: Shivam Kumar (2026). CrossChat: Real-Time Multi-Room Chat on FastAPI WebSockets + SQLite. VisionQuantech. https://visionquantech.com/blog/showcase-crosschat.html

Want this for your business?

VisionQuantech builds AI receptionists, chatbots & automation for clinics, home services, real estate & more.

Book a Free Demo
SHARE𝕏in⧉
Chat with us Email us