An intentionally opinionated, anonymous (but not private) chat platform built with Rust (Axum) and Next.js. RChat makes unconventional design choices that prioritize user freedom and casual communication over traditional security practices.
RChat deliberately deviates from industry standards in several ways. These are not oversights - they are intentional design decisions that reflect a specific philosophy about online communication.
Industry Standard: Minimum 8 characters, mixed case, numbers, special characters RChat: Single character passwords ("a") are valid
Rationale: Password complexity requirements create friction for casual, anonymous (not private) communication. Users should be free to choose trivial passwords if they want disposable, low-stakes accounts. This platform is designed for ephemeral conversation, not sensitive data storage.
Industry Standard: Encrypt data at rest and in transit (beyond TLS)
RChat: A single SQLite .db file stores absolutely everything in plaintext (except site config env vars).
Rationale: RChat explicitly rejects the notion of privacy. This is a public-by-default platform where users should assume everything they write is visible. The database file is the site; portability and transparency are prioritized over secrecy. No encrypted storage means no false sense of security. The transparency is the point.
Industry Standard: Randomly generated server IDs, hidden/private servers, invite-only links RChat: Server names are unique identifiers and join codes. All servers are effectively public if you know the name.
Rationale: Simplifies discovery and sharing. If you can name it, you can join it. This reinforces the public-square nature of the platform.
Industry Standard: Login wall for all content, or separate public view RChat: The entire site is accessible in a read-only "Guest Mode" by default. Accounts are only required to write (send messages, create servers).
Rationale: Information should be free to access. The barrier to entry (account creation) is only strictly enforced when a user wants to contribute content, not when they just want to observe. Guest state (joined servers) is stored locally in the browser.
Industry Standard: Lock accounts after 3-5 failed attempts RChat: Unlimited attempts, 3-second gap between tries, lock only after 1000 attempts (for 24 hours)
Rationale: Account security is the user's responsibility, not the platform's. If someone chooses "a" as their password, they've made a choice. The 3-second throttling prevents automated brute force while still allowing humans to try repeatedly. The 1000-attempt threshold exists solely to prevent DOS attacks, not to protect users.
Industry Standard: Persistent storage, user-controlled deletion RChat: All files (up to 25MB) auto-delete after exactly 24 hours
Rationale: Ephemeral by default. Conversations should be temporary. This prevents the platform from becoming a file hosting service and enforces the philosophy of transient communication.
Industry Standard: End-to-end encryption for private messages RChat: Server can read all DMs, stored in plaintext
Rationale: Transparency extends to direct messages. If you want truly private communication, use Signal. RChat is for casual chats where perfect privacy isn't the goal.
Industry Standard: User-controlled content filters RChat: Server-side mandatory profanity censoring in public servers (disabled in DMs)
Rationale: Public servers maintain a baseline level of civility through automatic filtering. DMs have filtering disabled to preserve freedom in private conversations.
Industry Standard: Separate user IDs and display names RChat: Username = unique identifier = login credential
Rationale: Simplicity. No hidden user IDs. What you see is what you get. Reduces cognitive overhead.
While RChat rejects user-protective security theater, it implements genuine anti-abuse measures:
- Site-wide rate limiting: 100 requests/second per IP (burst: 20)
- Login throttling: 3-second gap between attempts
- Account locking: 1000 failed logins = 24-hour lock
- DOS protection: tower-governor rate limiting layer
- SQL injection protection: Parameterized queries via SQLx
- Site admin moderation: Ban users (Site Ban), kick from servers (Server Ban), delete content
Security exists to protect the platform, not to protect users from themselves.
- Servers & Channels: Discord-like server/channel structure. Server names are case-insensitive IDs.
- Direct Messages: Private 1-on-1 conversations (including self-DM).
- Real-time Updates: WebSocket connections for live message delivery.
- File Uploads: Seamless drag-and-drop, images, documents (25MB max, auto-delete after 24h).
- Guest Mode: View any server without an account. LocalStorage saves your server list.
- Profile Pictures: Identicon or solid-color person icons (no uploads).
- Text passwords: Any length (including single character).
- Word-based passwords: Choose 7 words from a deterministic 20-word sequence.
- Same username always gets same word set (SHA256-based).
- Case-insensitive username normalization.
- Server Admins: Creator of the server. Can Server Ban (persistent kick), delete messages, manage channels.
- Site Admins: First registered user becomes site admin.
- Can Site Ban users (deletes all messages + account site-wide).
- Can moderate any server.
- Profanity Filter: rustrict library for automatic censoring.
- Applied to: usernames, server names, channel names, public messages.
- Not applied to: DMs, passwords.
- Axum: Web framework
- SQLx: Database interactions
- SQLite: Data storage (.db file)
- tower-governor: Rate limiting
- WebSocket: Real-time communication
- rustrict: Profanity filtering
- React 19: UI framework
- Material-UI: Component library (Google style)
- WebSocket: Real-time updates
- LocalStorage: Guest mode server preferences
Two-server setup where Rust is the main entry point:
- Rust server handles
/apiroutes + WebSocket - Next.js server handles UI rendering
- Rust proxies non-API requests to Next.js
- Rust (latest stable)
- Node.js 18+
- Just (task runner)
Create .env.local:
# Rust Server (Main Entry Point)
SERVER_PORT=3000
SERVER_HOST=127.0.0.1 # Use 0.0.0.0 for remote access
# Next.js Dev Server (Development Only)
PORT=3001
HOSTNAME=localhost # Use 0.0.0.0 for remote access
# Security
SECRET_KEY=your-secret-key-here # REQUIRED for JWT
# Logging
RUST_LOG=warn # Recommended for production to reduce noisejust src devRuns both servers:
- Rust: http://localhost:3000 (main entry point)
- Next.js dev: http://localhost:3001 (proxied)
just src prodRuns optimized build:
- Rust serves static files from
.next/standalone - No Next.js dev server
The easiest way to deploy RChat:
# Build and run with docker-compose
docker-compose up -d
# Or build manually
docker build -t rchat .
docker run -d \
-p 3000:3000 \
-e SECRET_KEY=your-secret-key \
-v rchat-data:/app/data \
-v rchat-uploads:/app/uploads \
rchatAccess at http://localhost:3000
Environment variables for Docker:
| Variable | Default | Description |
|---|---|---|
SECRET_KEY |
(required) | JWT signing secret |
SERVER_PORT |
3000 |
Port to expose |
RUST_LOG |
info |
Log level |
Volumes:
/app/data- SQLite database/app/uploads- Uploaded files (auto-deleted after 24h)
rchat/
├── src/
│ ├── api/ # Axum route handlers
│ ├── models/ # Data structures
│ ├── services/ # Business logic
│ ├── middleware/ # Auth, rate limiting
│ ├── websocket/ # Real-time messaging
│ └── bin/server.rs # Main entry point
├── app/
│ ├── components/ # React components
│ ├── lib/ # API clients, utilities
│ └── types/ # TypeScript types
├── migrations/ # SQL schema
└── public/ # Static assets
Single SQLite file contains:
users- Accounts (plaintext passwords via argon2 hash)servers- Chat serverschannels- Server channelsmessages- All messages (server + DM)direct_messages- DM metadatafiles- Upload metadataserver_members- Membership + rolesserver_bans- Server-specific banslogin_attempts- Rate limiting data
GET /api/auth/word-sequence?username=X- Get word listPOST /api/auth/register- Create accountPOST /api/auth/login- AuthenticateGET /api/public/servers- List all serversPOST /api/public/servers/lookup- Lookup server by nameGET /api/public/servers/:id/channels- Get channelsGET /api/public/channels/:id/messages- Get messagesGET /api/public/servers/:id/members- Get membersGET /api/downloads/:file_id- Public file download
POST /api/servers- Create serverDELETE /api/servers/:id- Delete serverPOST /api/servers/:id/channels- Create channelDELETE /api/servers/:id/channels/:cid- Delete channelPATCH /api/servers/:id/channels/:cid- Rename channelDELETE /api/servers/:id/members/:username- Server Ban userPATCH /api/servers/:id/members/:username- Grant adminPOST /api/dms- Create/get DMPOST /api/messages- Send message (Server or DM)DELETE /api/channels/:cid/messages/:mid- Delete channel messageDELETE /api/dms/:did/messages/:mid- Delete DM messagePOST /api/files- Upload filePOST /api/users/:username/ban- Site Ban user (site admin only)
ws://localhost:3000/api/ws?token=JWT- Real-time updates
Each username has a deterministic set of 20 words generated via:
SHA256(lowercase_username + iteration_index) → word_indexProperties:
- Same username always gets same words
- Case-insensitive (TestUser = testuser)
- No duplicates
- One-to-one mapping
Users select 7 words in order as their password.
- Set
SERVER_HOST=0.0.0.0in.env.local - In dev mode: Set
HOSTNAME=0.0.0.0for Next.js - Access via http://your-host:3000
- Optional: Use reverse proxy (nginx/Caddy) for HTTPS
Rust server uses AllowOrigin::mirror_request() - dynamically mirrors incoming Origin header. Works with any origin without manual configuration.
- Set strong
SECRET_KEYin production - Configure
RUST_LOGappropriately - Run
just src prodfor optimized build - Set up reverse proxy for HTTPS
- Ensure
.dbfile has proper permissions - Monitor rate limiting effectiveness
- User profile pictures (file uploads)
- Message editing
- Message reactions
- Voice/video chat
- Email verification
- Password reset (no email system)
- Account recovery
- Better mobile responsive design
- Notification system
- Search functionality
- Message history pagination
- Server categories
This is an educational/demonstration project showcasing intentionally unconventional design choices. Contributions should maintain the project's philosophy of:
- User freedom over protection
- Simplicity over features
- Transparency over privacy
MIT
RChat is not suitable for:
- Confidential communication
- Business use
- Storing sensitive information
- Compliance-required environments (GDPR, HIPAA, etc.)
Use RChat for:
- Casual group chats
- Ephemeral coordination
- Learning about trade-offs in system design
- Environments where transparency is valued over privacy
By using RChat, you acknowledge that:
- All data is stored in plaintext
- Messages may be read by server operators
- Files are automatically deleted after 24 hours
- Accounts can be created with trivial passwords
- No privacy guarantees exist
When in doubt, use Signal.