A modern, high-performance random video chat application built with Go, WebRTC, and Redis. Connect with strangers worldwide through HD video, audio, and text chat with seamless matchmaking.
Get started in 30 seconds with our pre-built Docker image:
docker run -d \
--name omiro \
-p 8080:8080 \
-e REDIS_HOST=redis \
-e REDIS_PORT=6379 \
--network omiro-net \
ghcr.io/r0ld3x/omiro:latestOr use Docker Compose (Recommended):
# Create docker-compose.yml
curl -O https://raw.githubusercontent.com/r0ld3x/omiro/main/docker-compose.yml
# Start everything
docker-compose up -d
# Access at http://localhost:8080That's it! No need to install Go, Redis, or build anything. π
Configure Omiro with these environment variables:
| Variable | Description | Default | Required |
|---|---|---|---|
REDIS_HOST |
Redis server hostname | localhost |
β |
REDIS_PORT |
Redis server port | 6379 |
β |
REDIS_PASSWORD |
Redis password (if protected) | - | β |
PORT |
Application HTTP port | 8080 |
β |
Example with all options:
docker run -d \
--name omiro \
-p 8080:8080 \
-e REDIS_HOST=my-redis-server \
-e REDIS_PORT=6379 \
-e REDIS_PASSWORD=mypassword \
-e PORT=8080 \
ghcr.io/r0ld3x/omiro:latest- Quick Start - Docker
- Features
- Architecture
- Manual Installation
- Configuration
- API Documentation
- Project Structure
- Deployment
- Security
- Performance
- Troubleshooting
- Contributing
- π₯ HD Video & Audio Chat - WebRTC P2P connections with automatic quality adaptation
- π¬ Real-time Text Messaging - Instant chat with message history
- π Smart Matchmaking - Queue-based random matching system
- βοΈ Next Person - Skip to next match seamlessly (Omegle-style)
- π Auto-reconnect - Automatic queue rejoining on partner disconnect
- π Session Management - HMAC-signed session tokens
- π« Rate Limiting - Per-IP WebSocket connection limits
- π IP Ban System - Redis-backed IP banning with TTL
- π Multi-Server Support - Redis pub/sub for horizontal scaling
- π― Smart WebRTC Negotiation - Deterministic caller/callee assignment
- π NAT Traversal - STUN/TURN server support
- β‘ High Performance - Goroutine-based concurrent handling
- π¨ Modern UI - Beautiful animated gradient design
- π« Smooth Animations - Fade-ins, pulses, and interactive effects
- π± Fully Responsive - Mobile, tablet, and desktop support
- π Glassmorphism - Modern backdrop blur effects
- β¨ Interactive Elements - Ripple effects and hover animations
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Frontend (Browser) β
β ββββββββββββββββββ WebRTC P2P ββββββββββββββββββ β
β β Client A β ββββββββββββΊ β Client B β β
β β (Video/Audio) β β (Video/Audio) β β
β ββββββββββ¬ββββββββ ββββββββββ¬ββββββββ β
β β β β
βββββββββββββΌββββββββββββββββββββββββββββββββΌβββββββββββββββββββ
β WebSocket + Session Token β
βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββ
β Go Backend (Echo Framework) β
β β
β βββββββββββββββ ββββββββββββββββ β
β β WebSocket β β HTTP API β β
β β Handler β β /session β β
β ββββββββ¬βββββββ ββββββββββββββββ β
β β β
β ββββββββΌβββββββββββββββββββββββββββββββ β
β β Matchmaking Engine β β
β β (Queue-based Algorithm) β β
β ββββββββ¬βββββββββββββββββββββββββββββββ β
βββββββββββΌβββββββββββββββββββββββββββββββββββββ
β Redis Pub/Sub + Data Operations
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β Redis Database β
β β
β β’ Session Tokens β
β β’ Matchmaking Queue β
β β’ Active Connections β
β β’ Rate Limiting Counters β
β β’ IP Ban List β
β β’ Server Registry (for scaling) β
β β’ Pub/Sub Channels β
βββββββββββββββββββββββββββββββββββββββββββ
| Component | Technology | Purpose |
|---|---|---|
| Frontend | Vanilla JavaScript + WebRTC | Video chat UI and P2P connections |
| Backend | Go + Echo Framework | WebSocket server and API |
| Database | Redis | Session management, queuing, pub/sub |
| Signaling | WebSocket | WebRTC negotiation (SDP/ICE) |
| Media | WebRTC | Peer-to-peer video/audio streams |
Ensure you have the following installed:
- Go 1.24+ (Download)
- Redis 7.0+ (Install Guide)
- Modern web browser (Chrome, Firefox, Safari, Edge)
-
Clone the repository
git clone https://github.com/r0ld3x/omiro.git cd omiro -
Install dependencies
go mod download
-
Start Redis server
# Linux/Mac redis-server # Windows (if installed via MSI) redis-server.exe # Docker docker run -d -p 6379:6379 redis:7-alpine
-
Run the application
go run . -
Open in browser
http://localhost:8080 -
Test with multiple tabs
- Open 2+ browser tabs/windows
- Click "Connect" β "Start Video" β "Find Match" in each
- The tabs will match and start video calling!
Edit main.go to configure Redis connection:
redis.Init(redis.Config{
Host: "localhost", // Redis host
Port: "6379", // Redis port
Password: "", // Redis password (if any)
DB: 0, // Redis database number
})Adjust WebSocket settings in main.go:
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
ReadBufferSize: 1024,
WriteBufferSize: 1024,
HandshakeTimeout: 10 * time.Second,
EnableCompression: true,
}Change the default port (8080) in main.go:
e.Start(":8080") // Change to your desired portGenerate a new session token for WebSocket authentication.
Response:
{
"token": "550e8400-e29b-41d4-a716-446655440000:1700672400:a3f5e7..."
}Token Format: uuid:timestamp:hmac_signature
Serves the main HTML application.
Connect: ws://localhost:8080/ws?token={session_token}
All messages follow this format:
{
"op": "operation_name",
"data": {
/* optional payload */
}
}| Operation | Description | Payload |
|---|---|---|
join_queue |
Join matchmaking queue | None |
next |
Skip to next partner | None |
chat |
Send text message | {"message": "text"} |
webrtc_offer |
Send WebRTC offer | {"sdp": "..."} |
webrtc_answer |
Send WebRTC answer | {"sdp": "..."} |
ice_candidate |
Send ICE candidate | {"candidate": {...}} |
disconnect |
Fully disconnect | None |
| Operation | Description | Payload |
|---|---|---|
match_found |
Match found | {"partner": "uuid", "should_call": bool} |
partner_disconnected |
Partner left | None |
chat |
Receive message | {"message": "text"} |
webrtc_offer |
Receive offer | {"sdp": "...", "from": "uuid"} |
webrtc_answer |
Receive answer | {"sdp": "...", "from": "uuid"} |
ice_candidate |
Receive ICE candidate | {"candidate": {...}, "from": "uuid"} |
// 1. Connect with session token
const token = await fetch("/session/new").then((r) => r.json());
const ws = new WebSocket(`ws://localhost:8080/ws?token=${token.token}`);
// 2. Join queue
ws.send(JSON.stringify({ op: "join_queue" }));
// 3. Receive match
// Server sends: {"op":"match_found","partner":"...", "should_call":true}
// 4. If should_call=true, create and send offer
ws.send(
JSON.stringify({
op: "webrtc_offer",
data: { sdp: offer.sdp },
})
);
// 5. Send chat message
ws.send(
JSON.stringify({
op: "chat",
data: { message: "Hello!" },
})
);omiro/
βββ main.go # Application entry point
βββ client.go # Client struct and methods
βββ chat.go # Chat message handling
βββ handle_websocket.go # WebSocket upgrade and connection
βββ handle_webrtc.go # WebRTC signaling (offer/answer/ICE)
βββ incoming.go # Message routing and readPump
βββ join_queue.go # Matchmaking queue logic
β
βββ middleware/
β βββ is_allowed.go # Rate limiting and IP banning
β βββ session_token.go # Token generation and validation
β
βββ redis/
β βββ client.go # Redis connection initialization
β βββ operations.go # Core Redis operations (queue, stats)
β βββ chat.go # Chat message storage
β βββ ips.go # IP ban management
β
βββ helper/
β βββ helper.go # Utility functions (GetRealIP, etc.)
β
βββ index.html # Frontend application (WebRTC client)
βββ go.mod # Go module dependencies
βββ go.sum # Dependency checksums
βββ Dockerfile # Docker build configuration
βββ docker-compose.yml # Multi-container setup
βββ README.md # This file
| File | Purpose |
|---|---|
main.go |
Initializes Redis, starts matchmaker, sets up Echo routes |
handle_websocket.go |
Upgrades HTTP to WebSocket, validates session tokens |
incoming.go |
Routes WebSocket messages to appropriate handlers |
join_queue.go |
Manages matchmaking queue and partner assignment |
handle_webrtc.go |
Forwards WebRTC signaling between peers |
chat.go |
Handles text chat between matched partners |
Build and run:
docker-compose up -dStop:
docker-compose downThe docker-compose.yml automatically sets up:
- Go application on port 8080
- Redis on port 6379
- Persistent Redis volume
Build image:
docker build -t omiro:latest .Run container:
docker run -d \
-p 8080:8080 \
-e REDIS_HOST=host.docker.internal \
-e REDIS_PORT=6379 \
omiro:latestNginx Configuration:
server {
listen 80;
server_name omiro.yourdomain.com;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}# Start Omiro with Docker
docker run -d -p 8080:8080 ghcr.io/r0ld3x/omiro:latest
# In another terminal
ngrok http 8080# Start Omiro with Docker
docker run -d -p 8080:8080 \
-e REDIS_HOST=redis \
ghcr.io/r0ld3x/omiro:latest
# Set up Cloudflare tunnel
cloudflared tunnel create omiro
cloudflared tunnel route dns omiro omiro.yourdomain.com
cloudflared tunnel run omiro- Use HTTPS/WSS (required for camera/microphone)
- Set up reverse proxy (Nginx/Caddy)
- Enable Redis persistence (
appendonly yes) - Configure firewall (allow ports 80, 443)
- Set up monitoring (Prometheus/Grafana)
- Enable log rotation
- Use dedicated TURN servers
- Set up automatic backups
- Configure SSL certificates (Let's Encrypt)
β Session Token Authentication
- HMAC-SHA256 signed tokens
- Timestamp-based expiration
- Prevents token forgery
β Rate Limiting
- Per-IP connection limits
- Configurable time windows
- Redis-backed counters
β IP Banning
- Persistent ban storage in Redis
- TTL-based automatic unbanning
- Admin API for ban management
β Real IP Detection
- Cloudflare header support (
CF-Connecting-IP) - X-Forwarded-For parsing
- Proxy-aware IP extraction
β Input Validation
- JSON payload validation
- Message length limits
- XSS protection in chat
β Origin Checking
- WebSocket origin validation
- CORS configuration
- Cross-site request protection
Rate Limiting (in redis/operations.go):
allowed, _ := redis.CheckRateLimit(ip, 10, 1*time.Minute)
// Allows 10 connections per minute per IPSession Token (in middleware/session_token.go):
token := fmt.Sprintf("%s:%d:%s", sessionID, timestamp, signature)
// Format: uuid:timestamp:hmac- Goroutines - Concurrent message handling per client
- Redis Pub/Sub - Efficient multi-server communication
- WebSocket Compression - Reduced bandwidth usage
- Connection Pooling - Redis connection reuse
- Channel Buffering - 256-message buffer per client
- Lazy Peer Connection - Created only when needed
Horizontal Scaling:
Load Balancer (Nginx/HAProxy)
βββ Go Server 1 ββ
βββ Go Server 2 ββΌββΊ Redis (Central coordination)
βββ Go Server 3 ββ
Each server:
- Registers with Redis on startup
- Subscribes to its own channel
- Uses pub/sub for cross-server messaging
| Metric | Value |
|---|---|
| Concurrent Connections | 10,000+ |
| Messages/sec | 50,000+ |
| Latency (avg) | <10ms |
| Memory/client | ~100KB |
Problem: Browser can't access media devices
Solutions:
- Use HTTPS (browsers require secure context)
- Check browser permissions
- Verify device is not in use by another app
- Test in browser console:
navigator.mediaDevices.getUserMedia({video: true, audio: true})
Problem: Peer connection fails, video doesn't show
Solutions:
- Check STUN/TURN server configuration in
index.html - Verify firewall allows WebRTC traffic (UDP ports)
- Check browser console for ICE connection errors
- Test with both users on same network first
Problem: Can't establish WebSocket connection
Solutions:
# Check Redis is running
redis-cli ping # Should return PONG
# Check server is running
curl http://localhost:8080/session/new
# Check WebSocket endpoint
wscat -c ws://localhost:8080/ws?token=YOUR_TOKENProblem: WebRTC signaling error in logs
Solution: This has been fixed in the current version. Update incoming.go to use direct handler functions instead of forwardWebRTC.
Problem: Neither client initiates WebRTC offer
Solution: Server now sends should_call: true/false in match_found message. First client in queue becomes caller.
We welcome contributions! Here's how to get started:
-
Fork the repository
-
Create a feature branch:
git checkout -b feature/amazing-feature
-
Make your changes and test
-
Commit with meaningful messages:
git commit -m "feat: add amazing feature" -
Push to your fork:
git push origin feature/amazing-feature
-
Open a Pull Request
- Follow Go best practices and idioms
- Run
gofmtbefore committing - Add comments for complex logic
- Write tests for new features
- Update documentation
type(scope): subject
body (optional)
footer (optional)
Types: feat, fix, docs, style, refactor, test, chore
// Core dependencies
github.com/gorilla/websocket v1.5.3 // WebSocket protocol
github.com/labstack/echo/v4 v4.13.4 // HTTP framework
github.com/redis/go-redis/v9 v9.17.0 // Redis client
github.com/google/uuid v1.6.0 // UUID generation
golang.org/x/crypto v0.38.0 // Cryptographic functionsThis project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2024 Omiro Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software...
- WebRTC - Real-time communication standard
- Gorilla WebSocket - Go WebSocket implementation
- Redis - In-memory data structure store
- Echo Framework - High-performance Go web framework
- Open Relay - Free TURN servers
- GitHub Issues: Report a bug
- Discussions: Join the conversation
- Pull Requests: Contribute code
If you find this project useful, please consider:
- β Starring the repository
- π Reporting bugs
- π‘ Suggesting features
- π Contributing code
- π’ Sharing with others
Built with β€οΈ using Go, WebRTC, and Redis
Making real-time communication accessible to everyone