uniroute

module
v1.0.77 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 12, 2026 License: MIT

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.

Go Version Status

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/chat works 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 (or uniroute auth login --server https://app.uniroute.co)
  • Local: uniroute auth login --local (or uniroute auth login --server http://localhost:8084)

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

  1. Sign up for Mailtrap (free tier available)
  2. Get your SMTP credentials from the Mailtrap dashboard
  3. Add to .env file:
    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, and SMTP_PASSWORD accordingly
  • Common ports: 587 (STARTTLS) or 465 (TLS)
OAuth Configuration

UniRoute supports OAuth authentication with Google, GitHub, and X (Twitter). To enable:

  1. 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/callback for local dev, or https://app.uniroute.co/auth/google/callback for production)
    • Add to .env:
      GOOGLE_OAUTH_CLIENT_ID=your-client-id
      GOOGLE_OAUTH_CLIENT_SECRET=your-client-secret
      
  2. 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/callback for local dev)
    • Add to .env:
      GITHUB_OAUTH_CLIENT_ID=your-client-id
      GITHUB_OAUTH_CLIENT_SECRET=your-client-secret
      
  3. 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/callback for 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 Address 75.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.com or use the dashboard at https://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
  1. Layered Architecture: Clear separation between presentation, business logic, and data access
  2. Interface-Based Design: Providers, repositories, and services use interfaces for flexibility
  3. Dependency Injection: Services are injected, not hardcoded
  4. Repository Pattern: Data access abstracted through repositories
  5. Strategy Pattern: Routing strategies (cost, latency, balanced, custom)
  6. Middleware Pattern: Cross-cutting concerns (auth, rate limiting, CORS)
  7. 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
  1. Request Flow:

    Client β†’ API Gateway β†’ Middleware (Auth/Rate Limit) β†’ Handler β†’ 
    Gateway Router β†’ Provider β†’ LLM API β†’ Response β†’ Client
    
  2. 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
    
  3. 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
  1. Create new application in Coolify
  2. Connect GitHub repository
  3. Set environment variables
  4. 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 projects
  • internal/: Private application code, not importable by external projects
  • cmd/: 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:

  1. Can this be reused? - Extract to pkg/ if reusable across projects
  2. Is this abstracted? - Use interfaces for flexibility
  3. Is this configurable? - Avoid hardcoded values
  4. Is this testable? - Dependencies injected, not hardcoded
  5. 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
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
  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Write clean, reusable code:
    • Follow Single Responsibility Principle
    • Extract common patterns into reusable functions
    • Use interfaces for abstraction
    • Keep functions small and focused
  4. Write tests for new features (aim for >80% coverage)
  5. Update documentation (code comments, README, etc.)
  6. Run quality checks:
    make fmt      # Format code
    make lint     # Run linters
    make test     # Run tests
    make security # Security scan
    
  7. Commit using conventional commits:
    feat: add new provider support
    fix: resolve rate limiting bug
    refactor: extract common error handling
    docs: update API documentation
    
  8. Push to the branch (git push origin feature/amazing-feature)
  9. 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/ and internal/ 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.


πŸ’ 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!



✨ 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
api
mcp
pkg
tests

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL