FunChat is a production-quality, peer-to-peer, end-to-end encrypted chat application built with Flutter. It uses the Signal Protocol (X3DH + Double Ratchet) for encryption, WebRTC DataChannel for P2P transport, and a lightweight Cloudflare Worker for signaling.
- âś… End-to-end encryption using Signal Protocol (libsignal)
- âś… Peer-to-peer communication via WebRTC DataChannel
- âś… Local SQLite storage with Drift
- âś… No server-side message storage
- âś… Offline message queue with automatic retry
- âś… Dark/Light theme support
- âś… WhatsApp-like UI/UX
- âś… Message status indicators (pending/sent/delivered)
- âś… Search conversations
- âś… Swipe to delete chats
- âś… Identity key verification
- State Management: Riverpod
- Database: Drift (SQLite)
- Encryption: libsignal_protocol_dart
- P2P Transport: flutter_webrtc
- UI: Material Design 3
- Framework: HonoJS (TypeScript)
- Purpose: Signaling only (no message storage)
- Endpoints: Registration, lookup, offer/answer, ICE exchange, presence
- Flutter SDK (3.9.2 or higher)
- Dart SDK
- Bun (for backend) - Install Bun
- Android Studio / Xcode (for mobile development)
cd backend
# Install dependencies using bun
bun install
# Run development server
bun run dev
# The server will start on http://localhost:8787For production deployment to Cloudflare Workers:
# Login to Cloudflare
wrangler login
# Deploy
bun run deploy# Install Flutter dependencies
flutter pub get
# Generate Drift database code
flutter pub run build_runner build --delete-conflicting-outputs
# Run the app
flutter runThe app uses --dart-define for configuration. Default values are set in lib/config/environment.dart.
To run with custom configuration:
flutter run \
--dart-define=SIGNALING_URL=ws://localhost:8787 \
--dart-define=BUILD_MODE=dev \
--dart-define=LOG_LEVEL=debugAvailable environment variables:
SIGNALING_URL- WebSocket URL for signaling server (default: ws://localhost:8787)BUILD_MODE- dev/production (default: dev)APP_NAME- Application name (default: FunChat)APP_DOMAIN- App domain (default: funchat.local)MAX_CHUNK_SIZE_BYTES- File chunk size (default: 65536)LOG_LEVEL- Logging level (default: debug)ICE_SERVERS- JSON array of ICE serversTURN_USERNAME- TURN server usernameTURN_CREDENTIAL- TURN server credential
funchat/
├── lib/
│ ├── config/
│ │ └── environment.dart # Environment configuration
│ ├── core/
│ │ ├── crypto/
│ │ │ └── signal_protocol_manager.dart # Signal Protocol implementation
│ │ ├── webrtc/
│ │ │ └── webrtc_manager.dart # WebRTC P2P manager
│ │ └── signaling/
│ │ └── signaling_client.dart # Signaling client
│ ├── data/
│ │ ├── database/
│ │ │ └── database.dart # Drift database schema
│ │ └── repositories/
│ │ └── chat_repository.dart # Chat business logic
│ ├── presentation/
│ │ ├── screens/
│ │ │ ├── chat_list_screen.dart
│ │ │ ├── chat_screen.dart
│ │ │ ├── add_contact_screen.dart
│ │ │ └── settings_screen.dart
│ │ └── theme/
│ │ └── app_theme.dart
│ ├── providers/
│ │ └── app_providers.dart # Riverpod providers
│ └── main.dart
├── backend/
│ ├── src/
│ │ └── index.ts # Signaling server
│ ├── package.json
│ ├── wrangler.toml
│ └── .env
└── README.md
- On first run, the app generates a Signal Protocol identity keypair
- Pre-keys and signed pre-keys are generated and published to the signaling server
- Private keys are stored securely using flutter_secure_storage
- User enters contact's User ID
- App fetches contact's public identity and pre-key bundle from signaling server
- X3DH handshake establishes initial session
- Contact is saved locally
- Sender creates WebRTC offer and sends via signaling server
- Recipient receives offer and creates answer
- ICE candidates are exchanged
- Direct P2P DataChannel connection is established
- Message is encrypted using Signal Protocol (Double Ratchet)
- Encrypted message is sent over WebRTC DataChannel
- Message is stored locally as ciphertext
- If recipient is offline, message is queued locally
- Encrypted message arrives via DataChannel
- Message is decrypted using Signal Protocol
- Message is stored locally and displayed
- Delivery acknowledgment is sent
- Messages sent while recipient is offline are queued locally
- When connection is re-established, queued messages are automatically sent
- Fresh session keys are derived on reconnection
- End-to-End Encryption: All messages encrypted with Signal Protocol
- Perfect Forward Secrecy: Double Ratchet ensures past messages remain secure
- No Server Storage: Messages never stored on server
- Secure Key Storage: Private keys stored in device secure enclave
- Identity Verification: Users can verify contact fingerprints
- Encrypted Session State: Signal session state encrypted at rest
flutter testflutter test integration_test/To test P2P functionality:
- Run backend:
cd backend && bun run dev - Run app on device 1:
flutter run -d device1 - Run app on device 2:
flutter run -d device2 - Add each other as contacts using User IDs
- Start chatting!
flutter build apk --release \
--dart-define=SIGNALING_URL=wss://your-worker.workers.dev \
--dart-define=BUILD_MODE=productionflutter build ios --release \
--dart-define=SIGNALING_URL=wss://your-worker.workers.dev \
--dart-define=BUILD_MODE=production- Ensure Bun is installed:
bun --version - Check port 8787 is not in use
- Verify wrangler.toml configuration
- Check SIGNALING_URL in environment.dart
- Ensure backend is running
- Check firewall settings
- Verify both devices are connected to signaling server
- Check WebRTC connection state
- Ensure ICE candidates are being exchanged
- Run:
flutter pub run build_runner build --delete-conflicting-outputs - Clear app data and restart
- Messages are paginated for smooth scrolling
- Large files are chunked for efficient transfer
- Encryption operations run off main thread
- Database queries are optimized with indexes
- Group chats (in progress)
- Voice/video calls
- File attachments with resume support
- Message reactions
- Push notifications
- Desktop apps (Windows, macOS, Linux)
- Message search
- Chat backup/restore
- Copy
.env.exampleto.env:
cp .env.example .env- Update values in
.env:
SIGNALING_URL=ws://localhost:8787
BUILD_MODE=dev
API_KEY=funchat_secure_key_2024- Run with environment variables:
flutter run \
--dart-define=SIGNALING_URL=ws://localhost:8787 \
--dart-define=API_KEY=funchat_secure_key_2024- Copy
.env.exampleto.env:
cd backend
cp .env.example .env- Update values in
backend/.env:
PORT=8787
ENVIRONMENT=development
API_KEY=funchat_secure_key_2025The app uses assets/images/logo.png for all platform icons.
-
Place your logo at
assets/images/logo.png(1024x1024 recommended) -
Generate icons for all platforms:
flutter pub get
dart run flutter_launcher_iconsThis will generate icons for:
- Android (all densities)
- iOS (all sizes)
- Web (favicon, manifest icons)
- Windows (ico file)
- macOS (icns file)
- Linux (png files)
This project is licensed under the Apache License, Version 2.0. See the LICENSE file for details.
Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
Muhammad Fiaz
- Email: contact@muhammadfiaz.com
- GitHub: @muhammad-fiaz
- Repository: muhammad-fiaz/funchat
For issues and questions:
- GitHub Issues: Create an issue
- Email: contact@muhammadfiaz.com
- Signal Protocol by Open Whisper Systems
- Flutter team for the amazing framework
- Cloudflare for Workers platform
- HonoJS for lightweight web framework