A terminal chat app in Rust. One server relays messages to many TUI clients over plain TCP. No accounts, no setup beyond cargo.
Cleartext by design. Messages travel unencrypted on the wire. Don't use it for anything sensitive — it's a learning project, not a privacy tool.
┌──────────────────────────────┐┌──────────────┐
│ alice: hey, anyone here? ││ alice │
│ bob: yo ││ bob │
│ SYSTEM: carol joined the chat││ carol │
│ carol: hi! ││ │
│ ││ │
│ ││ │
└──────────────────────────────┘└──────────────┘
┌──────────────────────────────┐
│ Input as [you] _ │
└──────────────────────────────┘
The left pane is the chat history. The right pane is the list of currently connected users. The bottom is the input box. Esc to quit.
- Rust toolchain (1.85+ required; project uses
edition = "2024") - A terminal that supports ANSI escape sequences (basically anything modern)
No cmake, no OpenSSL, no system libraries — all dependencies are pure Rust and pulled in by cargo.
From the repo root:
cargo build --releaseThe two binaries land at ./target/release/server and ./target/release/client.
Open two terminals (or more) from the repo root.
Terminal 1 — start the server:
./target/release/server
# → Chat server listening on 0.0.0.0:8080Terminal 2 (and 3, 4, …) — start a client:
./target/release/client
# Enter your username: aliceType a message and press Enter to send. Every connected client sees it. New joiners and leavers are announced as SYSTEM: ….
| Key | Action |
|---|---|
| Any character | Append to input |
Backspace |
Delete last character |
Enter |
Send the current input (ignored if empty) |
Esc |
Disconnect and quit |
The client's username is chosen once at startup. To chat as a different name, restart the client.
- Listens on
0.0.0.0:8080. - Accepts any number of clients concurrently.
- Forwards every chat message to every other connected client.
- Maintains a live user list and broadcasts it on every join and leave.
- Logs connections, disconnections, and codec errors to stderr.
The server has no admin commands, no message history, no persistence — restart it and the chat is empty.
client A ──► server ──► client B
│
└──────► client C
│
└──────► client D
All clients see all messages. There are no private channels, no DMs, no rooms.
Address already in use (server)
Port 8080 is taken. Either stop the other process, or change the bind address in src/bin/server.rs:31 and the connect target in src/bin/client.rs:42 to match.
Client disconnects immediately The server isn't running, or it's bound only to a different interface. Check the server log.
Garbled output in the TUI
Make sure your terminal supports ANSI escapes. Inside tmux or screen, the app works as long as the parent terminal does. Resize the terminal to redraw.
Panic in the client leaves the terminal in raw mode
The client installs a panic hook that restores the terminal before printing the error, so this shouldn't happen. If it does, run stty sane or open a new shell.
- No encryption, no authentication, no rate limiting — anyone who can reach the port can read and write.
- No message history; late joiners don't see prior messages.
- No protocol versioning; client and server must be the same build.
- Username
SYSTEMis reserved by the server for join/leave notices. Picking it will collide visually with system messages. - Hard-coded port and address; no config file, no flags.
Released under the MIT License.