README
ΒΆ
π UniRoute
One unified gateway for every AI model. Route, secure, and manage traffic to any LLMβcloud or localβwith one unified platform. Built-in tunneling for exposing local services to the internet.
Open Source β’ Self-Hostable β’ Managed Service Available
π Overview
UniRoute is a unified gateway platform that routes, secures, and manages traffic to any LLM (cloud or local) with one unified API. A single entry point that intelligently routes requests to the best available model.
Unified API
UniRoute provides a single, consistent API interface for all LLM providers. Instead of learning different APIs for OpenAI, Anthropic, Google, and local models, you use one unified endpoint:
- Single Endpoint:
/v1/chatworks with all providers - Consistent Format: Same request/response format across all providers
- Automatic Routing: UniRoute intelligently routes to the best available model
- Provider Abstraction: Switch providers without changing your code
- Multi-Provider Support: Use multiple providers simultaneously with failover
This unified approach means you can:
- Build applications that work with any LLM provider
- Switch between providers without code changes
- Use local models (Ollama, vLLM) alongside cloud providers
- Implement intelligent routing based on cost, latency, or availability
- Scale across multiple providers automatically
Why UniRoute?
- β Open Source - Full transparency, community-driven
- β Self-Hostable - Deploy anywhere, full control (100% free)
- β Managed Service - UniRoute handles provider keys, unified billing (pay-as-you-go)
- β Local LLM First - Priority support for local models (Ollama, vLLM) - Free & Private
- β Multi-Provider - Support for OpenAI, Anthropic, Google, Local LLMs, and more
- β Shareable - Host and share your gateway with others (built-in tunneling)
- β Intelligent Routing - Load balancing, failover, cost-based routing
- β Enterprise Security - API keys, rate limiting, Zero Trust support
π Quick Start
Prerequisites
- Go 1.21+
- PostgreSQL 15+ (or Supabase free tier)
- Redis 7+ (or Upstash free tier)
- SMTP Server (for email verification and password reset) - Mailtrap (free tier) recommended for development
- Docker (optional, for containerized deployment)
Installation
Option 1: One-Line Install (Easiest) β
# One-line installation (auto-detects OS and architecture)
curl -fsSL https://raw.githubusercontent.com/Kizsoft-Solution-Limited/uniroute/main/scripts/install.sh | bash
# Verify
uniroute --version
# Login to your account
## Email/Password Login
```bash
uniroute auth login
# Or with email flag
uniroute auth login --email user@example.com
Choosing the server (hosted vs local)
When you don't pass --server, --local, or --live, the CLI picks the server in this order: UNIROUTE_API_URL env var β saved server from your last login β hosted (https://app.uniroute.co). New installs default to hosted.
Switch explicitly:
- Hosted:
uniroute auth login --live(oruniroute auth login --server https://app.uniroute.co) - Local:
uniroute auth login --local(oruniroute auth login --server http://localhost:8084)
API Key Login (Recommended for Automation)
API keys provide longer sessions without expiration, making them ideal for automation and CI/CD:
# Login with API key
uniroute auth login --api-key ur_xxxxxxxxxxxxx
# or using short flag
uniroute auth login -k ur_xxxxxxxxxxxxx
# use hosted explicitly
uniroute auth login -k ur_xxx --live
# use local server
uniroute auth login -k ur_xxx --local
Benefits of API Key Login:
- β No expiration (unlike JWT tokens)
- β Perfect for automation and scripts
- β Longer sessions for CI/CD pipelines
- β Same authentication as email/password
Start using UniRoute!
uniroute tunnel --list
**Option 2: Manual Download**
On Mac (and Linux), a downloaded binary is not executable until you run `chmod +x`. Then move it into your PATH.
**If the file is in your Downloads folder** (e.g. `uniroute-darwin-amd64` or `uniroute-darwin-arm64`):
```bash
cd ~/Downloads
chmod +x uniroute-darwin-amd64 # or uniroute-darwin-arm64 for Apple Silicon
sudo mv uniroute-darwin-amd64 /usr/local/bin/uniroute
uniroute --version
Or download via curl:
# macOS (Apple Silicon / M1/M2/M3):
curl -L https://github.com/Kizsoft-Solution-Limited/uniroute/releases/latest/download/uniroute-darwin-arm64 -o uniroute
# macOS (Intel):
# curl -L https://github.com/Kizsoft-Solution-Limited/uniroute/releases/latest/download/uniroute-darwin-amd64 -o uniroute
# Make it executable (required after download)
chmod +x uniroute
# Move to a directory in your PATH (e.g. /usr/local/bin)
sudo mv uniroute /usr/local/bin/
CLI Environment Variables (Recommended for local development):
# Set API server URL (when you don't use --server/--local/--live: env overrides saved config, then default is hosted)
# Use BASE_URL from .env or set explicitly:
export UNIROUTE_API_URL=${BASE_URL:-http://localhost:8084}
# Set tunnel server URL (default: auto-detects local mode or uses tunnel.uniroute.co)
export UNIROUTE_TUNNEL_URL=localhost:8080
# Optional: override default hosted URL (used when no UNIROUTE_API_URL or saved config)
# export UNIROUTE_HOSTED_URL=https://app.uniroute.co
# Enable local development mode
export UNIROUTE_ENV=local
# Then login (will use localhost automatically)
## Email/Password Login
```bash
uniroute auth login
API Key Login (Longer Sessions)
uniroute auth login --api-key ur_xxxxxxxxxxxxx
# or
uniroute auth login -k ur_xxxxxxxxxxxxx
Note: API keys provide longer sessions without expiration, making them ideal for automation.
**Option 2: Build from Source**
```bash
# Clone the repository
git clone https://github.com/Kizsoft-Solution-Limited/uniroute.git
cd uniroute
# Install dependencies
go mod download
# Install OAuth2 package (required for Google/GitHub/X login)
go get golang.org/x/oauth2
# Build binaries
make build
# Set up environment
cp .env.example .env
# Edit .env with your configuration (see Environment Variables below)
# Run database migrations
make migrate
# Start the server
make dev
Environment Variables
Create a .env file in the root directory with the following variables:
# Server Configuration
PORT=8084
ENV=development
FRONTEND_URL=http://localhost:3000
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/uniroute?sslmode=disable
# Redis (for rate limiting)
REDIS_URL=redis://localhost:6379
# Security
API_KEY_SECRET=your-secret-key-min-32-chars
JWT_SECRET=your-jwt-secret-min-32-chars
PROVIDER_KEY_ENCRYPTION_KEY=your-encryption-key-32-chars
# SMTP Configuration (for email verification and password reset)
SMTP_HOST=sandbox.smtp.mailtrap.io
SMTP_PORT=2525
SMTP_USERNAME=your-mailtrap-username
SMTP_PASSWORD=your-mailtrap-password
SMTP_FROM=noreply@uniroute.co
# Optional: Cloud Provider API Keys (server-level, fallback for BYOK)
OPENAI_API_KEY=your-openai-key
ANTHROPIC_API_KEY=your-anthropic-key
GOOGLE_API_KEY=your-google-key
# Optional: OAuth Configuration (for Google, GitHub, and X/Twitter login)
GOOGLE_OAUTH_CLIENT_ID=your-google-oauth-client-id
GOOGLE_OAUTH_CLIENT_SECRET=your-google-oauth-client-secret
GITHUB_OAUTH_CLIENT_ID=your-github-oauth-client-id
GITHUB_OAUTH_CLIENT_SECRET=your-github-oauth-client-secret
X_OAUTH_CLIENT_ID=your-x-oauth-client-id
X_OAUTH_CLIENT_SECRET=your-x-oauth-client-secret
# Optional: IP Whitelist (comma-separated)
IP_WHITELIST=127.0.0.1,::1
# Optional: CORS Origins (comma-separated, overrides defaults)
CORS_ORIGINS=http://localhost:3000,https://app.uniroute.co
# Optional: Tunnel Allowed Origins (comma-separated, overrides defaults)
TUNNEL_ORIGINS=http://localhost,https://tunnel.uniroute.co,.uniroute.co
# Base URL Configuration
# Base URL for the API server (used in documentation and examples)
# For local development: http://localhost:8084
# For production: https://app.uniroute.co (or your domain)
BASE_URL=http://localhost:8084
# Tunnel Server Configuration
# Base domain for tunnel subdomains (e.g., "uniroute.co" or "yourdomain.com")
# If not set, defaults to "uniroute.co"
TUNNEL_BASE_DOMAIN=uniroute.co
# Localhost domain for local development tunnels
# Default: "localhost" (results in subdomain.localhost:port)
TUNNEL_LOCALHOST_DOMAIN=localhost
# Base port for TCP/UDP tunnel allocation
# Default: 20000 (tunnels will use ports starting from this number)
TUNNEL_TCP_PORT_BASE=20000
# Website URL for links in error pages and UI
# Default: https://uniroute.co
WEBSITE_URL=https://uniroute.co
SMTP Configuration
UniRoute requires SMTP configuration for email verification and password reset functionality. For development, we recommend using Mailtrap (free tier):
- Sign up for Mailtrap (free tier available)
- Get your SMTP credentials from the Mailtrap dashboard
- Add to
.envfile:SMTP_HOST=sandbox.smtp.mailtrap.io SMTP_PORT=2525 SMTP_USERNAME=your-mailtrap-username SMTP_PASSWORD=your-mailtrap-password SMTP_FROM=noreply@uniroute.co
For Production:
- Use a production SMTP service (SendGrid, AWS SES, Mailgun, etc.)
- Update
SMTP_HOST,SMTP_PORT,SMTP_USERNAME, andSMTP_PASSWORDaccordingly - Common ports:
587(STARTTLS) or465(TLS)
OAuth Configuration
UniRoute supports OAuth authentication with Google, GitHub, and X (Twitter). To enable:
-
Google OAuth:
- Go to Google Cloud Console
- Create OAuth 2.0 credentials
- Add authorized redirect URI:
{BASE_URL}/auth/google/callback(e.g.,http://localhost:8084/auth/google/callbackfor local dev, orhttps://app.uniroute.co/auth/google/callbackfor production) - Add to
.env:GOOGLE_OAUTH_CLIENT_ID=your-client-id GOOGLE_OAUTH_CLIENT_SECRET=your-client-secret
-
GitHub OAuth:
- Go to GitHub Developer Settings
- Create a new OAuth App
- Set Authorization callback URL:
{BACKEND_URL}/auth/github/callback(e.g.,http://localhost:8084/auth/github/callbackfor local dev) - Add to
.env:GITHUB_OAUTH_CLIENT_ID=your-client-id GITHUB_OAUTH_CLIENT_SECRET=your-client-secret
-
X (Twitter) OAuth:
- Go to Twitter Developer Portal
- Create an OAuth 2.0 app
- Add callback URL:
{BACKEND_URL}/auth/x/callback(e.g.,http://localhost:8084/auth/x/callbackfor local dev) - Add to
.env:X_OAUTH_CLIENT_ID=your-client-id X_OAUTH_CLIENT_SECRET=your-client-secret
Note:
- OAuth providers redirect to your backend server, which then redirects to the frontend with the authentication token
- OAuth users do NOT need email verification - OAuth providers (Google, GitHub, X) already verify user emails, so users are automatically marked as verified upon OAuth login/registration
See CLI Reference for detailed CLI installation and usage instructions, including the interactive UI.
Verify Installation
# Check health endpoint (replace with your BASE_URL if different)
curl ${BASE_URL:-http://localhost:8084}/health
First Request (Local LLM)
# With Ollama running locally
# Replace ${BASE_URL} with your actual base URL (e.g., http://localhost:8084)
curl -X POST ${BASE_URL:-http://localhost:8084}/v1/chat \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "llama2",
"messages": [
{"role": "user", "content": "Hello!"}
]
}'
Expose to Internet
Option 1: Using cloudflared (100% free, no signup) - Recommended
# Replace with your BASE_URL port (default: 8084)
cloudflared tunnel --url ${BASE_URL:-http://localhost:8084}
# Returns: https://random-subdomain.trycloudflare.com
Option 2: Using cloudflared (100% free, no signup)
# Replace 8084 with your actual port from BASE_URL
cloudflared tunnel --url ${BASE_URL:-http://localhost:8084}
# Returns: https://random-subdomain.trycloudflare.com
Option 3: Built-in UniRoute tunnel (requires CLI installation) β Recommended
# Download CLI (see CLI Reference in docs)
# Or build: make build
# Expose your local app (any port, any app)
# Replace 8084 with your actual port from BASE_URL
uniroute tunnel --port 8084
# Returns: http://{subdomain}.${TUNNEL_BASE_DOMAIN:-uniroute.co} -> ${BASE_URL}
# Works with any local application, not just UniRoute!
# Multiple protocols supported
uniroute tunnel --protocol http --port 8080 # HTTP tunnel
uniroute tunnel --protocol tcp --port 3306 # TCP tunnel (MySQL)
uniroute tunnel --protocol tls --port 5432 # TLS tunnel (PostgreSQL)
uniroute tunnel --protocol udp --port 53 # UDP tunnel (DNS)
# Shortcut commands
uniroute http 8080 # HTTP tunnel (shortcut)
uniroute tcp 3306 # TCP tunnel (shortcut)
uniroute tls 5432 # TLS tunnel (shortcut)
uniroute udp 53 # UDP tunnel (shortcut)
# TLS tunnel: connect from anywhere (e.g. PostgreSQL). Start tunnel with local port (e.g. 5432),
# then use the public URL:port with sslmode=require:
# psql "postgresql://user:pass@SUBDOMAIN.tunnel.uniroute.co:PORT/dbname?sslmode=require"
# Custom subdomain support (shortcut syntax)
uniroute http 8080 myapp # Request specific subdomain (myapp.uniroute.co) - shortcut
uniroute http 8080 myapp --new # Create new tunnel with specific subdomain - shortcut
uniroute tcp 3306 mydb # TCP tunnel with specific subdomain - shortcut
uniroute tcp 3306 mydb --new # TCP tunnel with subdomain and force new - shortcut
# Custom subdomain support (flag syntax - also works)
uniroute tunnel --host myapp # Request specific subdomain (myapp.uniroute.co)
uniroute tunnel --host myapp --new # Create new tunnel with specific subdomain
uniroute http 8080 --host myapp # HTTP tunnel with specific subdomain
uniroute http 8080 --host myapp --new # HTTP tunnel with subdomain and force new
# Custom domain support
uniroute domain example.com # Add domain to account (no tunnel assignment)
uniroute domain example.com abc123 # Add domain AND assign to tunnel by subdomain (shortcut)
uniroute domain example.com --subdomain abc123 # Add domain AND assign to tunnel (flag syntax)
uniroute domain example.com --tunnel-id <id> # Add domain AND assign to specific tunnel
# Domain management commands
uniroute domain list # List all your custom domains
uniroute domain show example.com # Show domain details and status
uniroute domain verify example.com # Verify DNS configuration
uniroute domain resume # Resume last used domain assignment
uniroute domain resume abc123 # Resume domain assignment by subdomain
uniroute domain resume example.com # Resume domain assignment by domain
uniroute domain remove example.com # Remove domain from account
# Start multiple tunnels at once
uniroute tunnel --all # Starts all configured tunnels from ~/.uniroute/tunnels.json
# Resume previous tunnel
uniroute tunnel --resume # Automatically resumes last tunnel
uniroute resume abc123 # Resume tunnel by subdomain (shortcut)
Dev server + tunnel (one command or your normal command):
Three ways to run your dev server and get a public URL. Works with Laravel, Vue, React, Django, Rails, and more.
# Option 1: We start your dev server + tunnel (port auto-detected)
uniroute dev
# Option 2: You run your server; we only add the tunnel (two terminals)
# Terminal 1: php artisan serve (or npm run dev, rails s, etc.)
# Terminal 2: uniroute dev --attach
# Option 3: Your exact command; we run it and add the tunnel (port from your command or project)
uniroute run -- php artisan serve
uniroute run -- php artisan serve --port=8080 # tunnel to 8080
uniroute run -- npm run dev
Port is taken from: (1) your command (e.g. --port=8080), (2) our --port flag, or (3) auto-detected from the project. Supported: Node (Vite/Next/React), PHP (Laravel), Python (Django, Flask, FastAPI), Go, Ruby (Rails). Custom domains work with dev/run: assign a domain to the tunnel and traffic to your domain goes to the same HTTP tunnel. See Tunnels β Dev & Run in the Docs UI or run uniroute dev --help and uniroute run --help.
Tunnel Features:
- β HTTP, TCP, TLS, and UDP protocol support
- β Persistent tunnels (survive CLI restarts)
- β Multiple tunnels support
- β Custom subdomains
- β Custom domains (bring your own domain)
- β Domain management (list, show, verify, remove)
- β Domain resume functionality
- β Automatic reconnection
- β Tunnel state management
Custom Domain Management
UniRoute supports using your own custom domains instead of random subdomains. You can manage domains through the CLI or dashboard.
Adding and Assigning Domains:
# Add domain to your account (not assigned to any tunnel yet)
uniroute domain example.com
# Add domain AND assign to tunnel in one command
uniroute domain example.com abc123 # By subdomain (shortcut)
uniroute domain example.com --subdomain abc123 # By subdomain (flag)
uniroute domain example.com --tunnel-id <id> # By tunnel ID
Domain Management Commands:
# List all your domains
uniroute domain list
# Show details for a specific domain
uniroute domain show example.com
# Verify DNS configuration
uniroute domain verify example.com
# Resume domain assignment (restore previous assignment)
uniroute domain resume # Resume last used assignment
uniroute domain resume abc123 # Resume by subdomain
uniroute domain resume example.com # Resume by domain name
# Remove domain from account
uniroute domain remove example.com
DNS Configuration:
After adding a domain, add these records in your DNS provider:
| Type | Host | Value / Target | TTL |
|---|---|---|---|
| A Record | @ |
75.119.141.27 |
Automatic |
| CNAME Record | www |
example.com. |
Automatic |
- A Record: Host
@, IP Address75.119.141.27, TTL Automatic. - CNAME Record: Host
www, Target your apex domain (e.g.example.com.), TTL Automatic.
Confirm tunnel server IP with: dig tunnel.uniroute.co +short
- Verify DNS: Run
uniroute domain verify example.comor use the dashboard athttps://app.uniroute.co/dashboard/domains. Once verified, your domain is ready to use.
Domain Resume Feature:
When you assign a domain to a tunnel, the assignment is automatically saved. You can resume it later:
# First time: assign domain to tunnel
uniroute domain billspot.co abc123
# Later: resume the same assignment
uniroute domain resume abc123
# or
uniroute domain resume billspot.co
The resume feature:
- β Saves domain-to-tunnel assignments automatically
- β Allows resuming by domain name or subdomain
- β Automatically looks up current tunnel ID
- β Works across CLI sessions (persistent storage)
Domain Management in Dashboard:
You can also manage domains through the web dashboard:
- View all domains:
/dashboard/domains - Add new domains
- Verify DNS configuration
- Delete domains
Both CLI and dashboard use the same backend system, so domains created via CLI appear in the dashboard and vice versa.
β¨ Features
Core Features
- Unified API Interface - Single endpoint for all LLM providers
- Intelligent Routing - Model selection based on cost, latency, availability
- Load Balancing - Distribute traffic across multiple instances
- Automatic Failover - Seamless switching when providers fail
- Security & Access Control - API keys, JWT, rate limiting, IP whitelisting
- User Authentication - Registration, login, email verification, password reset
- OAuth Authentication - Login/register with Google, GitHub, and X (Twitter)
- Email Service - SMTP integration for verification and password reset emails
- Monitoring & Analytics - Usage tracking, cost tracking, performance metrics
- Error Logging - Frontend error tracking and admin error log management
- Multi-Provider Support - OpenAI, Anthropic, Google, Local LLMs
- Tunneling - Built-in tunneling for exposing local services
- CLI Tool - Command-line interface for tunnel management and authentication
- Developer Experience - CLI tool, SDKs, OpenAPI docs
Supported Providers
- π Local LLMs (Ollama, vLLM) β Priority - Free, private, self-hosted
- π€ OpenAI (GPT-4, GPT-3.5, etc.)
- π§ Anthropic (Claude)
- π· Google (Gemini)
- π Cohere
- β Custom providers
ποΈ Architecture
System Architecture
UniRoute follows a layered architecture with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client Layer β
β ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ β
β β Web App β β CLI β β SDK β β API β β
β β (Vue.js) β β Tool β β Clients β β Users β β
β ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ β
βββββββββΌββββββββββββββΌββββββββββββββΌββββββββββββββΌβββββββββ
β β β β
βββββββββββββββ¬βββ΄βββββββββββββββ΄ββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Presentation Layer (API Gateway) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β HTTP API (Gin Framework) β β
β β βββ REST Endpoints (/v1/chat, /auth/*, etc.) β β
β β βββ WebSocket (Tunnel Server) β β
β β βββ Middleware (CORS, Auth, Rate Limit, Security) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Business Logic Layer β
β ββββββββββββββββββββ ββββββββββββββββββββ β
β β Gateway Router β β OAuth Service β β
β β βββ Routing β β βββ Google β β
β β βββ Strategy β β βββ X/Twitter β β
β β βββ Failover β ββββββββββββββββββββ β
β ββββββββββββββββββββ ββββββββββββββββββββ β
β ββββββββββββββββββββ β Security Layer β β
β β Cost Calculator β β βββ JWT β β
β β Latency Tracker β β βββ API Keys β β
β ββββββββββββββββββββ β βββ Rate Limit β β
βββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Data Access Layer β
β ββββββββββββββββββββ ββββββββββββββββββββ β
β β Repositories β β Provider Layer β β
β β βββ User β β βββ OpenAI β β
β β βββ API Key β β βββ Anthropic β β
β β βββ Tunnel β β βββ Google β β
β β βββ Request β β βββ Local LLM β β
β ββββββββββββββββββββ ββββββββββββββββββββ β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Infrastructure Layer β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β PostgreSQL β β Redis β β External β β
β β (Database) β β (Cache/RL) β β Services β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Architecture Principles
- Layered Architecture: Clear separation between presentation, business logic, and data access
- Interface-Based Design: Providers, repositories, and services use interfaces for flexibility
- Dependency Injection: Services are injected, not hardcoded
- Repository Pattern: Data access abstracted through repositories
- Strategy Pattern: Routing strategies (cost, latency, balanced, custom)
- Middleware Pattern: Cross-cutting concerns (auth, rate limiting, CORS)
- Configuration via Environment: All settings via environment variables, no hardcoded values
Project Structure
uniroute/
βββ cmd/
β βββ gateway/ # Gateway server entry point
β βββ tunnel-server/ # Tunnel server entry point
β βββ cli/ # CLI tool entry point
βββ internal/
β βββ api/ # Presentation layer
β β βββ handlers/ # HTTP request handlers
β β βββ middleware/ # HTTP middleware (auth, CORS, rate limit)
β β βββ router.go # Route definitions
β βββ gateway/ # Business logic - routing
β β βββ router.go # Main routing logic
β β βββ strategy.go # Routing strategies
β β βββ cost_calculator.go
β β βββ latency_tracker.go
β βββ providers/ # LLM provider implementations
β β βββ interface.go # Provider interface
β β βββ openai.go
β β βββ anthropic.go
β β βββ google.go
β β βββ local.go
β βββ oauth/ # OAuth authentication
β β βββ service.go # Google & X OAuth service
β βββ security/ # Security layer
β β βββ jwt.go # JWT authentication
β β βββ apikey.go # API key management
β β βββ apikey_v2.go # Database-backed API keys
β β βββ ratelimit.go # Rate limiting
β βββ storage/ # Data access layer
β β βββ postgres.go # PostgreSQL client
β β βββ redis.go # Redis client
β β βββ models.go # Data models
β β βββ *_repository.go # Repository implementations
β βββ tunnel/ # Tunnel functionality
β β βββ server.go # Tunnel server
β β βββ client.go # Tunnel client
β β βββ repository.go # Tunnel data access
β βββ email/ # Email service
β βββ config/ # Configuration management
βββ frontend/ # Vue.js frontend application
β βββ src/
β β βββ views/ # Page components
β β βββ components/ # Reusable components
β β βββ services/ # API clients
β β βββ stores/ # State management
βββ migrations/ # Database migrations
βββ tests/ # Test suites
βββ pkg/ # Shared packages
βββ logger/
βββ errors/
βββ version/
Data Flow
-
Request Flow:
Client β API Gateway β Middleware (Auth/Rate Limit) β Handler β Gateway Router β Provider β LLM API β Response β Client -
OAuth Flow (No email verification required - OAuth providers verify):
User clicks "Login with Google/GitHub/X" β Frontend calls /auth/google, /auth/github, or /auth/x β Backend returns auth URL β User authorizes β OAuth provider redirects to /auth/google/callback, /auth/github/callback, or /auth/x/callback β Backend creates/logs in user (email auto-verified) β Redirects to frontend with JWT token β User authenticated -
Tunnel Flow:
CLI connects β Tunnel Server (WebSocket) β Tunnel created β Public URL assigned β Traffic forwarded β Local service responds β Response sent back through tunnel
---
## π» Technology Stack
- **Backend**: Go 1.21+
- **Frontend**: Vue.js 3 + TypeScript + Tailwind CSS
- **API Framework**: Gin
- **Database**: PostgreSQL + Redis
- **Authentication**: JWT + API Keys + OAuth2 (Google, GitHub, X/Twitter)
- **Email Service**: SMTP (Mailtrap, SendGrid, AWS SES, etc.)
- **Tunneling**: Built-in WebSocket-based tunnel server
- **Monitoring**: Prometheus + Grafana
- **Logging**: Structured logging (zerolog)
---
## π Documentation
- **[Interactive Documentation](https://uniroute.co/docs)** - Full documentation with guides, API reference, and examples
- **[CLI Reference](https://uniroute.co/docs/cli)** - π¦ CLI installation and usage guide
- **[Tunnel Documentation](https://uniroute.co/docs/tunnels)** - π Tunnel configuration, protocols, and custom domains
- **[Custom Domains Guide](https://uniroute.co/docs/tunnels/custom-domains)** - π Custom domain setup and management
- **API Documentation**: Interactive Swagger UI available at `${BASE_URL}/swagger` when the server is running (default: `http://localhost:8084/swagger` for local development)
- **Postman Collection**: Import `UniRoute.postman_collection.json` for ready-to-use API requests
---
## π Security
**β οΈ CRITICAL: Security First**
UniRoute implements enterprise-grade security:
- β
Input validation and sanitization
- β
API keys with bcrypt hashing
- β
JWT authentication with strong secrets
- β
Rate limiting (per-key, per-IP)
- β
HTTPS/TLS enforcement
- β
Security headers (CSP, HSTS, etc.)
- β
Parameterized queries (SQL injection prevention)
- β
Encrypted secrets at rest
See the [Security overview](https://uniroute.co/docs/security) in the documentation for the complete checklist.
---
## π³ Deployment
### Docker Compose
```bash
docker-compose up -d
Coolify
- Create new application in Coolify
- Connect GitHub repository
- Set environment variables
- Deploy automatically
For Coolify setup (frontend, backend, tunnel from one repo) and other options, see the Deployment Guide in the docs UI.
π§ͺ Development
Project Structure
uniroute/
βββ cmd/
β βββ gateway/ # Application entry point
βββ internal/
β βββ api/ # HTTP handlers & middleware
β βββ gateway/ # Routing logic
β βββ providers/ # LLM provider implementations
β βββ security/ # Auth & rate limiting
β βββ storage/ # Database clients
β βββ monitoring/ # Metrics & analytics
βββ migrations/ # Database migrations
βββ pkg/ # Shared packages (reusable utilities)
Makefile Commands
make dev # Start development server
make build # Build binary
make test # Run tests
make migrate # Run database migrations
make lint # Run linters
make security # Run security scans
make fmt # Format code
make vet # Run go vet
π§Ή Code Quality & Best Practices
Clean Code Principles
We follow clean code principles to ensure maintainability, readability, and reusability:
1. Single Responsibility Principle (SRP)
Each function, struct, and package should have one clear purpose.
// β
Good: Single responsibility
func ValidateAPIKey(key string) error {
// Only validates API key format
}
// β Bad: Multiple responsibilities
func ProcessRequestAndValidateAndLog(key string, req Request) error {
// Too many responsibilities
}
2. DRY (Don't Repeat Yourself)
Extract common patterns into reusable functions and packages.
// β
Good: Reusable error handling
func HandleProviderError(err error, provider string) error {
return fmt.Errorf("provider %s: %w", provider, err)
}
// β Bad: Repeated error handling
func CallOpenAI() error {
if err != nil {
return fmt.Errorf("provider openai: %v", err)
}
}
func CallAnthropic() error {
if err != nil {
return fmt.Errorf("provider anthropic: %v", err)
}
}
3. Interface-Based Design
Use interfaces for abstraction and testability.
// β
Good: Provider interface for reusability
type Provider interface {
Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error)
HealthCheck(ctx context.Context) error
}
// All providers implement the same interface
type OpenAIProvider struct {}
type AnthropicProvider struct {}
type LocalLLMProvider struct {}
4. Composition Over Inheritance
Use composition and embedding for code reuse.
// β
Good: Composition with base functionality
type BaseProvider struct {
client *http.Client
logger *zerolog.Logger
}
type OpenAIProvider struct {
BaseProvider
apiKey string
}
func (p *OpenAIProvider) Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error) {
// Reuse BaseProvider's client and logger
return p.makeRequest(ctx, req)
}
5. Error Handling
Consistent, informative error handling.
// β
Good: Wrapped errors with context
var (
ErrProviderUnavailable = errors.New("provider unavailable")
ErrRateLimitExceeded = errors.New("rate limit exceeded")
)
func CallProvider(ctx context.Context, req Request) error {
if err := provider.Call(ctx, req); err != nil {
return fmt.Errorf("failed to call provider: %w", err)
}
return nil
}
Reusable Code Patterns
1. Provider Interface Pattern
All LLM providers implement a common interface for easy swapping:
// pkg/providers/interface.go
type Provider interface {
Name() string
Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error)
Stream(ctx context.Context, req ChatRequest) (<-chan StreamChunk, error)
HealthCheck(ctx context.Context) error
GetModels() []string
}
// Easy to add new providers
type CustomProvider struct {
// implements Provider interface
}
2. Middleware Pattern
Reusable middleware for cross-cutting concerns:
// internal/api/middleware/auth.go
func AuthMiddleware(apiKeyService *APIKeyService) gin.HandlerFunc {
return func(c *gin.Context) {
key := extractAPIKey(c)
if err := apiKeyService.Validate(key); err != nil {
c.JSON(401, gin.H{"error": "unauthorized"})
c.Abort()
return
}
c.Next()
}
}
// Reusable across all routes
router.Use(AuthMiddleware(apiKeyService))
router.Use(RateLimitMiddleware(rateLimiter))
router.Use(LoggingMiddleware(logger))
3. Repository Pattern
Abstract data access for testability and reusability:
// internal/storage/repository.go
type APIKeyRepository interface {
FindByKeyHash(ctx context.Context, hash string) (*APIKey, error)
Create(ctx context.Context, key *APIKey) error
Update(ctx context.Context, key *APIKey) error
Delete(ctx context.Context, id string) error
}
// PostgreSQL implementation
type PostgresAPIKeyRepository struct {
db *sql.DB
}
// Redis cache implementation
type CachedAPIKeyRepository struct {
repo APIKeyRepository
cache *redis.Client
}
4. Strategy Pattern
Pluggable algorithms for routing, load balancing, etc.:
// internal/gateway/strategy.go
type RoutingStrategy interface {
SelectProvider(ctx context.Context, req Request, providers []Provider) (Provider, error)
}
// Different strategies
type CostBasedStrategy struct {}
type LatencyBasedStrategy struct {}
type RoundRobinStrategy struct {}
type FailoverStrategy struct {}
// Easy to swap strategies
router := NewRouter(CostBasedStrategy{})
5. Factory Pattern
Centralized creation of providers and services:
// internal/providers/factory.go
type ProviderFactory struct {
config *Config
logger *zerolog.Logger
}
func (f *ProviderFactory) CreateProvider(name string) (Provider, error) {
switch name {
case "openai":
return NewOpenAIProvider(f.config.OpenAI, f.logger)
case "anthropic":
return NewAnthropicProvider(f.config.Anthropic, f.logger)
case "local":
return NewLocalLLMProvider(f.config.Local, f.logger)
default:
return nil, ErrUnknownProvider
}
}
Code Organization Guidelines
Package Structure
pkg/: Reusable packages that can be imported by other projectsinternal/: Private application code, not importable by external projectscmd/: Application entry points
Naming Conventions
// β
Good naming
type APIKeyService struct {} // Clear, descriptive
func ValidateRequest(req Request) // Verb + noun
const MaxRetries = 3 // Constants in PascalCase
// β Bad naming
type Svc struct {} // Abbreviation unclear
func Do(req Request) // Too generic
const max = 3 // Should be exported if used elsewhere
Function Guidelines
- Keep functions small: Max 50 lines, ideally 20-30
- One level of abstraction: Don't mix high-level and low-level logic
- Descriptive names: Function name should describe what it does
- Avoid side effects: Functions should do one thing and do it well
// β
Good: Small, focused function
func CalculateCost(tokens int, model string) (float64, error) {
rate, err := GetModelRate(model)
if err != nil {
return 0, err
}
return float64(tokens) * rate, nil
}
// β Bad: Too many responsibilities
func ProcessEverything(req Request) (*Response, error) {
// 200 lines of mixed logic
}
Testing Best Practices
Unit Tests
- Test one thing at a time
- Use table-driven tests for multiple scenarios
- Mock external dependencies
// β
Good: Table-driven test
func TestCalculateCost(t *testing.T) {
tests := []struct {
name string
tokens int
model string
want float64
wantErr bool
}{
{"gpt-4", 1000, "gpt-4", 0.03, false},
{"invalid model", 1000, "invalid", 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := CalculateCost(tt.tokens, tt.model)
// assertions...
})
}
}
Code Review Checklist
Before submitting code, ensure:
- Single Responsibility: Each function/struct has one clear purpose
- DRY: No code duplication; common logic extracted
- Interfaces: Used for abstraction and testability
- Error Handling: All errors handled appropriately
- Tests: Unit tests for new functionality
- Documentation: Public functions/types documented
- Naming: Clear, descriptive names
- Formatting: Code formatted with
gofmt - Linting: No linter warnings
- Security: No security vulnerabilities introduced
Reusability Checklist
When writing code, ask:
- Can this be reused? - Extract to
pkg/if reusable across projects - Is this abstracted? - Use interfaces for flexibility
- Is this configurable? - Avoid hardcoded values
- Is this testable? - Dependencies injected, not hardcoded
- Is this documented? - Clear documentation for future developers
Example: Reusable Provider Pattern
// internal/providers/base.go - Reusable base provider
type BaseProvider struct {
name string
client *http.Client
logger *zerolog.Logger
metrics *prometheus.CounterVec
}
func (b *BaseProvider) makeRequest(ctx context.Context, url string, body interface{}) (*http.Response, error) {
// Reusable HTTP request logic
// Logging, metrics, retries, etc.
}
// internal/providers/openai.go - Specific implementation
type OpenAIProvider struct {
BaseProvider
apiKey string
baseURL string
}
func (p *OpenAIProvider) Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error) {
// Uses BaseProvider.makeRequest() for HTTP calls
return p.makeRequest(ctx, p.baseURL+"/chat", req)
}
π Code Standards
Go Style Guide
- Follow Effective Go guidelines
- Use
gofmtfor formatting (enforced in CI) - Follow Go Code Review Comments
Required Tools
# Install development tools
go install golang.org/x/tools/cmd/goimports@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/securego/gosec/v2/cmd/gosec@latest
# Run before committing
make fmt # Format code
make lint # Run linters
make vet # Run go vet
make security # Security scan
π API Examples
Chat Completion (Local LLM)
# Using local Ollama instance
# Replace ${BASE_URL} with your actual base URL
curl -X POST ${BASE_URL:-http://localhost:8084}/v1/chat \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "llama2",
"messages": [
{"role": "user", "content": "Explain quantum computing"}
],
"temperature": 0.7,
"max_tokens": 1000
}'
Response
{
"id": "chat-123",
"model": "llama2",
"provider": "local",
"choices": [{
"message": {
"role": "assistant",
"content": "Quantum computing is a type of computation..."
}
}],
"usage": {
"prompt_tokens": 5,
"completion_tokens": 10,
"total_tokens": 15
},
"cost": 0.0,
"latency_ms": 250
}
Share Your Gateway Server
# 1. Start Ollama
ollama serve
# 2. Start UniRoute
make dev
# 3. Expose to internet (using cloudflared - 100% free, no signup)
# Replace with your BASE_URL
cloudflared tunnel --url ${BASE_URL:-http://localhost:8084}
# Share: https://random-subdomain.trycloudflare.com
# 4. Others can now use your gateway!
curl -X POST https://random-subdomain.trycloudflare.com/v1/chat \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "llama2", "messages": [...]}'
π€ Contributing
Contributions are welcome! We prioritize clean, reusable code. Please follow these guidelines:
Contribution Workflow
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write clean, reusable code:
- Follow Single Responsibility Principle
- Extract common patterns into reusable functions
- Use interfaces for abstraction
- Keep functions small and focused
- Write tests for new features (aim for >80% coverage)
- Update documentation (code comments, README, etc.)
- Run quality checks:
make fmt # Format code make lint # Run linters make test # Run tests make security # Security scan - Commit using conventional commits:
feat: add new provider support fix: resolve rate limiting bug refactor: extract common error handling docs: update API documentation - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request with clear description
Code Contribution Guidelines
Before You Start
- Check existing code patterns and follow them
- Look for similar functionality that can be reused
- Discuss major changes in an issue first
While Coding
- Reuse existing code: Check
pkg/andinternal/for reusable utilities - Create reusable components: If you write something that could be reused, put it in
pkg/ - Use interfaces: Abstract dependencies for testability
- Write small functions: Max 50 lines, ideally 20-30
- Document public APIs: All exported functions/types need godoc comments
Code Review Focus
- Reusability: Can this code be reused elsewhere?
- Maintainability: Is the code easy to understand and modify?
- Testability: Can this be easily tested?
- Performance: Are there any obvious performance issues?
- Security: Any security concerns?
Example: Good Contribution
// β
Good: Reusable, well-documented, testable
// Package providers contains reusable provider implementations.
package providers
// Provider defines the interface for all LLM providers.
// This interface allows easy swapping of providers and testing.
type Provider interface {
// Chat sends a chat request to the provider.
Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error)
// HealthCheck verifies the provider is available.
HealthCheck(ctx context.Context) error
}
// NewProviderFactory creates a new provider factory with the given config.
// This factory pattern allows centralized provider creation.
func NewProviderFactory(config *Config) *ProviderFactory {
return &ProviderFactory{config: config}
}
Example: Bad Contribution
// β Bad: Not reusable, no documentation, hard to test
func doStuff(req map[string]interface{}) map[string]interface{} {
// 200 lines of mixed logic
// Hardcoded values
// No error handling
// Can't be reused
}
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π Links
π Donate
UniRoute is an open-source project built with β€οΈ by the community. If you find it useful, please consider supporting the project:
- β Star the repository on GitHub
- π Report bugs and suggest features
- π» Contribute code via pull requests
- β Buy us a coffee - Donate
Your support helps us continue developing and maintaining UniRoute!
π Links
- GitHub: https://github.com/Kizsoft-Solution-Limited/uniroute
- API Documentation:
http://localhost:8084/swagger(when server is running) - Issues: https://github.com/Kizsoft-Solution-Limited/uniroute/issues
β¨ Current Features
- β Unified API - Single endpoint for all LLM providers
- β Multi-Provider Support - OpenAI, Anthropic, Google, Local LLMs (Ollama, vLLM)
- β User Authentication - Registration, login, email verification, password reset
- β OAuth Authentication - Login/register with Google, GitHub, and X (Twitter)
- β Email Service - SMTP integration for verification and password reset emails
- β Security & Rate Limiting - API keys, JWT, progressive rate limiting, IP whitelisting
- β Intelligent Routing - Cost-based, latency-based, and failover routing
- β Custom Routing Rules - User-specific routing strategies and custom rules
- β BYOK (Bring Your Own Keys) - User-provided provider API keys with encryption
- β Monitoring & Analytics - Usage tracking, cost tracking, performance metrics
- β Tunneling - Built-in tunnel server and CLI for exposing local services (HTTP, TCP, TLS)
- β CLI Tool - Full-featured command-line interface for tunnel management and authentication
- β Error Logging - Frontend error tracking with admin dashboard
- β Developer Experience - CLI tool, SDKs, built-in tunneling, OpenAPI docs
β Star History
If you find UniRoute useful, please consider giving it a star β on GitHub!
π Acknowledgments
- Built with modern Go best practices and clean architecture
- Built with β€οΈ by the open-source community
π Quick Reference: Clean Code Checklist
Before Committing Code
# Run all checks
make fmt # Format with gofmt
make lint # Run linters
make test # Run tests
make vet # Run go vet
make security # Security scan
Code Quality Questions
Ask yourself:
- β Single Purpose? Does this function/struct do one thing?
- β Reusable? Can this be extracted and reused?
- β Testable? Can I easily write tests for this?
- β Documented? Is the code self-explanatory or documented?
- β No Duplication? Is this logic already implemented elsewhere?
- β Interface-Based? Am I using interfaces for abstraction?
- β Error Handling? Are all errors handled appropriately?
- β Small Functions? Is each function < 50 lines?
Reusability Patterns
| Pattern | Use Case | Location |
|---|---|---|
| Interface | Provider abstraction | internal/providers/interface.go |
| Middleware | Cross-cutting concerns | internal/api/middleware/ |
| Repository | Data access abstraction | internal/storage/repository.go |
| Strategy | Pluggable algorithms | internal/gateway/strategy.go |
| Factory | Object creation | internal/providers/factory.go |
| Base Struct | Shared functionality | internal/providers/base.go |
Common Reusable Components
- Error Handling:
pkg/errors/- Standardized error types - Logging:
pkg/logger/- Structured logging utilities - HTTP Client:
internal/providers/base.go- Reusable HTTP client - Validation:
internal/api/validators/- Request validation - Metrics:
internal/monitoring/metrics.go- Prometheus metrics
Made with β€οΈ by Kizsoft Solution Limited
Directories
ΒΆ
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
cli
command
|
|
|
gateway
command
|
|
|
tunnel-server
command
|
|
|
examples
|
|
|
basic-chat-go
command
|
|
|
internal
|
|
|
pkg
|
|
|
tests
|
|