Real-time messaging system built in Rust with a focus on authentication, secure communication, connection management, and low-latency.
Tech: Rust • Yew • WebSockets • JWT • Redis • Nginx • YugabyteDB
The app is split into a Yew frontend, a Rocket-based API, and a YugabyteDB-backed data layer. The current setup also includes helper crates for local startup and test startup, so the same services can be brought up in different ways without duplicating the core app logic.
- The Yew client talks to the API for normal requests and uses WebSockets for real-time message delivery
- Authentication is handled with JWTs during request and connection setup
- The server validates token claims, expiration, and signature before granting access
- Messages are routed through active connection handlers rather than polling loops
- Connection and session state is kept intentionally simple so it can later move toward shared infrastructure
The system separates authentication, connection handling, message routing, and persistence so each concern can evolve independently.
- User authenticates and receives a JWT
- The token is attached to subsequent authenticated requests and WebSocket initialization
- The server checks expiration, signature, and claims before accepting the session
- Valid sessions are tied to the user identity that was encoded in the token
- Expired or invalid tokens are rejected early so unauthorized connections never reach the chat flow
Token lifecycle handling includes expiration checks and session validation to ensure secure communication channels.
- WebSocket-based persistent connections for low-latency delivery
- Connection lifecycle management for connect, message, and disconnect events
- Structured message handling so messages can be validated and routed consistently
- A client/server split that keeps the browser UI light and the server responsible for shared state
The system prioritizes efficient message routing, connection stability, and a UI that can stay responsive while the server handles the real-time work.
- Designed to minimize connection overhead and avoid unnecessary polling
- Built to handle multiple concurrent WebSocket sessions efficiently
- Planned Redis integration for caching, fan-out, and shared session state
- Separation of state makes it easier to scale the backend horizontally later
- Redis will be implemented for pub/sub messaging and shared session state across nodes (Required to support horizontal scaling of WebSocket connections)
- Support horizontal scaling of WebSocket servers when traffic grows
- Decouple the messaging layer for a more event-driven architecture later
The current implementation has connection state currently maintained in-memory per node, which simplifies development but limits horizontal scalability, planned implementation of Redis for shared state infrastructure will address this limitation.
The repository is already structured for containerized development. The backend and frontend each have their own Dockerfile, and the root compose file ties them together with YugabyteDB so the production-like stack can be reproduced locally.
- The server image uses a multi-stage Rust build so the compiled API binary can be copied into a smaller runtime image.
- The runtime server container exposes port
8000and listens on0.0.0.0so other containers can reach it. - The client image builds the Yew app with Trunk and serves the generated static files through Nginx.
- The client container exposes port
80, which becomes the browser entry point in Compose. - Nginx proxies
/apirequests to the server container so the frontend and API behave like a single site from the browser's point of view.
This setup keeps build tooling inside the image, keeps runtime images focused on serving the app, and makes the same stack easier to run in a deployment pipeline or on another machine.
The compose file wires the three services together: yugabyte, server, and client. Each service has a narrow responsibility so the stack is easy to reason about and the startup order stays predictable.
yugabyteprovides the PostgreSQL-compatible database port used by Diesel and the Rust server.serverwaits for YugabyteDB to become ready, resets the schema, runs migrations, and then launches the API.clientis built from the frontend Dockerfile and served by Nginx on host port80.- The server attaches to both the frontend and backend networks so it can reach the database and accept requests from the Nginx container.
- The database uses a bind-mounted data directory so local state persists across container restarts.
The compose layout is intentionally simple: one service for persistence, one for the API, and one for the browser-facing frontend. That makes startup order, port mapping, and debugging much easier while still matching the shape of the app in production.
There are 2 ways to run the project:
- *local environment*: starting each service individually for local development
- *Docker environment*: starting the full stack through Docker Compose in a production-like environment
Each approach has its own use case and setup requirements.
These setup steps are shared by both workflows.
- Clone the repository.
- Open the workspace root so the helper crates can be run from a single terminal context.
- Install Docker (and Docker Compose if you want to run the compose stack).
- Create the local server environment settings from .env templates:
server/.env.templateandserver/.env.prod.templateand fill in according to template instructions.
git clone https://github.com/GreenJ84/Rustic_Mesaging.git #1
cd Rustic_Mesaging #2
curl -fsSL https://get.docker.com | sh #3
mv server/.env.template server/.env #4
mv server/.env.prod.template server/.env.prod #4This path uses a custom package to bring up the database, reset the schema, launch the API, and serve the frontend.
- Install Rust and Cargo with rustup; if not already installed
- Install Trunk and the Diesel CLI; if not already installed
- Make sure Docker is running, because the startup package creates a YugabyteDB container before running tests.
- From the workspace root, start the local development helper package,
app_startup. - Wait for YugabyteDB to start, the schema to reset, and the API to launch.
- Open the API at
http://localhost:8000. - Open the frontend at
http://localhost:3000.
rustup toolchain install stable #1
rustup default stable #1
cargo install trunk #2
cargo install diesel_cli --no-default-features --features postgres #2
docker info #3 - Should not have an error connecting to the Docker daemon
cargo run -p app_startup #4This path starts the database, API server, and Nginx-served frontend together as one stack.
- From the repository root, build and start the compose stack.
- Wait for YugabyteDB, the API container, and the Nginx client container to start.
- Open
http://localhostin your browser. - Use
docker compose downwhen you want to stop the stack.
docker compose up --build
docker compose downThis project is licensed under the MIT License - see the LICENSE.md file for details.
The MIT License is a permissive license that allows users to use, copy, modify, merge, publish, distribute, and sublicense the software, provided that they include the original copyright notice and disclaimer. It also provides an implied warranty of fitness for a particular purpose and limits the liability of the software's authors and contributors.
By using or contributing to this project, you agree to be bound by the terms and conditions of the MIT License.
If you have any questions about the license or would like to use this software under a different license, please contact the project maintainers.
Contributions are welcome!
Please refer to my profile Code of Conduct before contributing to this project.
My Contribution Guide has more details on how to get started contributing.
Feel free to open an issue or submit a pull request if you have a way to improve this project.
Make sure your request is meaningful, thought out, and that you have tested the app locally before submitting a pull request.
💙 If you like this project, give it a ⭐ and share it with friends!
Made with Rust, Yew, Rocket, Redis, YugabyteDB, and ❤️🔥