CaoIM: Self-Hosted Chat on a Single Flutter Codebase
Developers who want to run their own chat get a deployable system with feature parity across desktop, mobile, and browser from one Flutter codebase. But putting it into production means changing the default admin password and JWT secret, and most public-network voice/video calls will require adding a TURN server to the provided STUN-only setup.
CaoIM ships a complete self-hosted chat system. The server side splits into im-server on port 8080 (WebSocket, presence, history, read receipts, call signaling) and app-server on port 8081 (registration, login, profiles, file upload), with an optional push-server for offline notifications and a Vue 3 admin console for user and message moderation. The client is a single Flutter codebase that builds for Android, iOS, Windows, macOS, and Web; the browser gets the same interface instead of a separate web app.
Messaging covers text, emoji, images, camera capture, voice, files, and short video, with quoting, forwarding, recalling, and favoriting. One-on-one voice and video calls exist, but group calls don't. Media stays peer-to-peer; the server only forwards signaling. The default setup uses public STUN, which handles LAN, but public networks will often need a TURN server. After login, clients hold two tokens: token for app-server, imToken for im-server. Text goes over the long connection; files upload to app-server and messages carry the URL, stored locally or in S3-compatible object storage.
Deployment requires JDK 17, Maven 3.6, MySQL 5.7 with utf8mb4, Flutter 3, Dart 3.11+, and Node.js 18 for the admin page. The default admin account is admin/admin123, and both that password and the JWT secret need changing before going live. The project is free for personal use; commercial use needs authorization.
Reusing the same Flutter code for the browser instead of building a dedicated web client gives perfect feature parity, but it also commits every platform to Flutter Web's rendering and performance characteristics.
Separating auth into token and imToken lets the API and the realtime channel be managed independently, so one can be rotated or revoked without dropping the other.