Skip to content

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.

License

Notifications You must be signed in to change notification settings

abdorrahmani/chat-server

Repository files navigation

Anophel Chat Server (Go)

Go License: MIT Release GitHub stars GitHub issues Docker PRs Welcome Made with Love Website

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.

✨Features

  • 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

📚Table of Contents

  • 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

🧰Setup

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.go

Run with default config (non-TLS TCP on port 8080):

./bin/chat

The server reads config.yml from the current working directory.


⚙️Configuration

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 TLS13

keynotes:

  • Set server.type to tcp for raw TCP, or websocket for WS/WSS, or gRPC for RPC.
  • Set tls.tlsRequire: true to enable TLS (affects both TCP, gRPC and WebSocket depending on server.type).
  • Files referenced in tls must exist and be readable by the process.

🔏Generate TLS certificates

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"

▶️Run

TCP mode (plaintext)

sed -i.bak 's/type: ".*"/type: "tcp"/' config.yml   # or edit manually
sed -i.bak 's/tlsRequire: .*/tlsRequire: false/' config.yml
./bin/chat

TCP mode (TLS)

sed -i.bak 's/type: ".*"/type: "tcp"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: true/' config.yml
./bin/chat

WebSocket mode (WS)

sed -i.bak 's/type: ".*"/type: "websocket"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: false/' config.yml
./bin/chat

Listens on ws://localhost:<port>/ws.

WebSocket mode (WSS)

sed -i.bak 's/type: ".*"/type: "websocket"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: true/' config.yml
./bin/chat

Listens on wss://localhost:<port>/ws.

gRPC mode (plaintext)

sed -i.bak 's/type: ".*"/type: "gRPC"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: false/' config.yml
./bin/chat

Listens for gRPC on localhost:<port>.

gRPC mode (TLS)

sed -i.bak 's/type: ".*"/type: "gRPC"/' config.yml
sed -i.bak 's/tlsRequire: .*/tlsRequire: true/' config.yml
./bin/chat

Uses your tls/server.crt and tls/server.key.


🧑‍💻Use the server

When a client connects, the server prompts:

  1. Enter your username:
  2. If security.requirePassword is true, prompts: Enter password: (must match security.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.maxLength are rejected

Example clients

Raw TCP

  • 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

WebSocket

  • 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

gRPC

  • 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).

🔐TLS usage details

The server enables TLS when tls.tlsRequire: true.

  • TCP + TLS: wraps the TCP listener with crypto/tls using tls/server.crt and tls/server.key.
  • WebSocket + TLS: uses http.ListenAndServeTLS to 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 (TLS12 or TLS13).
  • 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.

Test TLS over TCP inside Docker container (Windows dev mode example)

  • Ensure server.type: "tcp" and tls.tlsRequire: true in config.yml.
  • Start the container (Docker or Compose).
  • Run inside the container:
    docker exec -it chat-server-chat-server-1 openssl s_client -connect localhost:8080 -quiet -verify 0
    Notes:
    • localhost:8080 targets the service from inside the container.
    • -verify 0 skips 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 and run (Dockerfile)

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:local

Notes:

  • The image copies config.yml and tls/ at build time, but the command above also mounts your local config.yml and tls/ to override at runtime. Adjust paths for your shell/OS.

Docker Compose

docker-compose.yml already exposes port 8080 and mounts config.yml and tls/:

docker compose up --build

Edit config.yml to switch between tcp/websocket and to enable/disable TLS. Recreate the container after changes.


📡gRPC mode

Set in config.yml:

server:
  type: "gRPC"
  port: 8080
tls:
  tlsRequire: false # or true with certs

Server behavior:

  • Exposes unary RPC chat.ChatService/SendMessage with 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.SendMessage

Notes:

  • 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.

🗂️Logs and files

  • TLS assets: tls/server.crt, tls/server.key
  • Configuration: config.yml
  • Binary (local build): bin/chat
  • Optional logs (if enabled): logs/ or file specified in log.file

🛠️Troubleshooting

  • Port already in use: change server.port or stop the conflicting service.
  • Self-signed cert errors: for testing clients, disable verification (--no-check in wscat, -verify 0 in openssl 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.

🤑 One more thing

If you want!

Buy me a latte

📄License

This project is licensed under the MIT License - see the LICENSE file for details.

🙌Contact

About

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.

Topics

Resources

License

Stars

Watchers

Forks