Lightweight chat server supporting TCP, gRPC and WebSocket transports, with optional TLS for both (TLS over TCP, gRPC and WSS). Includes configurable rate limiting, message size limits, optional password gate, and Docker/Compose deployment.
- Multi-transport: TCP, gRPC, WebSocket
- Secure by choice: TLS 1.2+ for TCP, gRPC and WSS
- Chat essentials: broadcast + private messages
- Fair usage: per-client rate limit and max message length
- Access control: optional password gate
- Container-ready: Dockerfile + Compose
- Setup
- Configuration
- Generate TLS certificates
- Run (TCP / WebSocket / gRPC)
- Use the server (commands & examples)
- TLS usage (TCP + WSS + gRPC)
- Docker and Docker Compose
- gRPC mode
- Logs and files
- Troubleshooting
Prerequisites:
- Go 1.24+
- OpenSSL (for local TLS cert generation)
- Optional: Docker 24+ and Docker Compose v2+
Clone and build:
git clone https://github.com/abdorrahmani/chat-server.git
cd chat-server
go build -o bin/chat ./cmd/main.goRun with default config (non-TLS TCP on port 8080):
./bin/chatThe server reads config.yml from the current working directory.
All settings live in config.yml and are loaded via Viper at startup.
server:
host: "0.0.0.0" # (currently informational; the server binds to :port)
port: 8080 # listening port for both TCP and WebSocket
type: "tcp" # "tcp" or "websocket" or gRPC
maxClients: 100
readTimeout: 5 # seconds (not all timeouts may be enforced in handlers)
writeTimeout: 5 # seconds
security:
requirePassword: false
password: "1234"
hashMessage: true # reserved; message hashing helper exists in utils
hashAlgorithm: "hmac-sha256"
hashKey: "supersecret"
rateLimit:
messagePerSecond: 5 # token bucket rate per client
burst: 5 # not all fields may be used directly
message:
maxLength: 1000 # maximum allowed message length
log:
enableLogging: false
file: "chat.log"
tls:
tlsRequire: false # enable TLS for TCP or WebSocket or gRPC
certFile: "tls/server.crt"
keyFile: "tls/server.key"
minVersion: "TLS12" # TLS12 or TLS13keynotes:
- Set
server.typetotcpfor raw TCP, orwebsocketfor WS/WSS, orgRPCfor RPC. - Set
tls.tlsRequire: trueto enable TLS (affects both TCP, gRPC and WebSocket depending onserver.type). - Files referenced in
tlsmust exist and be readable by the process.
Self-signed certificates for local development are supported. Scripts are provided:
-
Windows PowerShell / cmd:
.\generate-certs.bat -
Linux/macOS:
bash generate-certs.sh
These create:
tls/server.key(private key)tls/server.crt(self-signed cert)
Update config.yml:
tls:
tlsRequire: true
certFile: "tls/server.crt"
keyFile: "tls/server.key"
minVersion: "TLS12"sed -i.bak 's/type: ".*"/type: "tcp"/' config.yml # or edit manually
sed -i.bak 's/tlsRequire: .*/tlsRequire: false/' config.yml
./bin/chatsed -i.bak 's/type: ".*"/type: "tcp"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: true/' config.yml
./bin/chatsed -i.bak 's/type: ".*"/type: "websocket"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: false/' config.yml
./bin/chatListens on ws://localhost:<port>/ws.
sed -i.bak 's/type: ".*"/type: "websocket"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: true/' config.yml
./bin/chatListens on wss://localhost:<port>/ws.
sed -i.bak 's/type: ".*"/type: "gRPC"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: false/' config.yml
./bin/chatListens for gRPC on localhost:<port>.
sed -i.bak 's/type: ".*"/type: "gRPC"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: true/' config.yml
./bin/chatUses your tls/server.crt and tls/server.key.
When a client connects, the server prompts:
Enter your username:- If
security.requirePasswordis true, prompts:Enter password:(must matchsecurity.password).
Commands and behavior:
/quit: leave the chat/pm <username> <message>: send a private message- Any other text: broadcast to all other users
- Echo: the server sends
ME: <your message>back to the sender - Rate limit: if you send too quickly, you’ll receive a slowdown message
- Max length: messages exceeding
message.maxLengthare rejected
-
Plaintext TCP:
nc 127.0.0.1 8080 # or on Windows (Git Bash/WSL): ncat 127.0.0.1 8080 -
TLS over TCP (using OpenSSL):
openssl s_client -connect 127.0.0.1:8080 -quiet # for self-signed certs add: -verify 0
-
WS using
wscat:npx wscat@latest -c ws://localhost:8080/ws
-
WSS using
wscat(self-signed):npx wscat@latest -c wss://localhost:8080/ws --no-check
-
Unary (SendMessage) without TLS:
grpcurl -plaintext localhost:8080 chat.ChatService.SendMessage \ -d '{"user":"alice","text":"hello from grpc"}' -
Unary (SendMessage) with TLS (self-signed for dev):
grpcurl -insecure localhost:8080 chat.ChatService.SendMessage \ -d '{"user":"alice","text":"secure hello"}' -
Streaming (Chat) tip:
- Use a small Go client for bidirectional streaming; interactive streaming via grpcurl/Postman is limited.
- On connect, first send a Join payload with username (and password if required), then send Text messages (supports
/pm <user> <msg>and/quit).
The server enables TLS when tls.tlsRequire: true.
- TCP + TLS: wraps the TCP listener with
crypto/tlsusingtls/server.crtandtls/server.key. - WebSocket + TLS: uses
http.ListenAndServeTLSto serve WSS at/ws. - gRPC + TLS: uses gRPC transport credentials from your
tls.server.crt/tls.server.key. - Minimum TLS version is controlled by
tls.minVersion(TLS12orTLS13). - For self-signed certs, clients must disable verification or trust the cert.
Security considerations:
- Self-signed certs are for development only. Use proper CA-issued certificates in production.
- Keep private keys secure and set file permissions appropriately.
- Ensure
server.type: "tcp"andtls.tlsRequire: trueinconfig.yml. - Start the container (Docker or Compose).
- Run inside the container:
Notes:
docker exec -it chat-server-chat-server-1 openssl s_client -connect localhost:8080 -quiet -verify 0localhost:8080targets the service from inside the container.-verify 0skips verification for self-signed certs (dev only).- If you run from the host instead, use
127.0.0.1:8080(or the mapped port) and ensure the port is published.
docker build -t chat-server:local .
docker run --rm -p 8080:8080 \
-v %cd%/config.yml:/root/config.yml \
-v %cd%/tls:/root/tls \
chat-server:localNotes:
- The image copies
config.ymlandtls/at build time, but the command above also mounts your localconfig.ymlandtls/to override at runtime. Adjust paths for your shell/OS.
docker-compose.yml already exposes port 8080 and mounts config.yml and tls/:
docker compose up --buildEdit config.yml to switch between tcp/websocket and to enable/disable TLS. Recreate the container after changes.
Set in config.yml:
server:
type: "gRPC"
port: 8080
tls:
tlsRequire: false # or true with certsServer behavior:
- Exposes unary RPC
chat.ChatService/SendMessagewith request{ user, text }and response{ status }. - Bridges to existing broadcast system: calling
SendMessage(user, text)broadcasts to all other clients.
Example call with grpcurl (no TLS):
grpcurl -plaintext localhost:8080 chat.ChatService.SendMessage \
-d '{"user":"alice","text":"hello from grpc"}'TLS example (self-signed):
grpcurl -insecure -d '{"user":"alice","text":"secure hello"}' \
-authority localhost \
localhost:8080 chat.ChatService.SendMessageNotes:
- Current RPC is unary for simplicity; you can extend the proto to streaming later.
- For production, replace self-signed certs with CA-issued certs and remove
-insecure.
- TLS assets:
tls/server.crt,tls/server.key - Configuration:
config.yml - Binary (local build):
bin/chat - Optional logs (if enabled):
logs/or file specified inlog.file
- Port already in use: change
server.portor stop the conflicting service. - Self-signed cert errors: for testing clients, disable verification (
--no-checkin wscat,-verify 0inopenssl s_client) or trust the cert. - Cannot connect over WSS: confirm
tls.tlsRequire: true, cert/key paths exist in the container/host process, and the port is reachable. - Messages dropped or slow: you may be hitting the per-client rate limit; adjust
rateLimit.messagePerSecond. - Username in use: each username must be unique per server instance.
If you want!
This project is licensed under the MIT License - see the LICENSE file for details.
- GitHub: @abdorrahmani