Sub2API

Go Vue PostgreSQL Redis Docker

AI API Gateway Platform for Subscription Quota Distribution

English | 中文 | 日本語

⚠️ Important Notice

Please read the following carefully before using this project:

  • 🚨 Terms of Service Risk: Using this project may violate the terms of service of Anthropic and other upstream providers. Please review the relevant providers' user agreements before use; all risks arising from such use are borne solely by the user.
  • ⚖️ Compliant Use: Use this project only in compliance with the laws and regulations of your country or region. Any unlawful use is strictly prohibited.
  • 📖 Disclaimer: This project is provided for technical learning and research purposes only. The authors assume no liability for account bans, service interruptions, data loss, or any other direct or indirect damages resulting from the use of this project.
  • 🚫 No Commercial Authorization: The developers of this project have never authorized any individual or organization to conduct any form of commercial operation based on this project. Any commercial activity conducted in the name of or based on this project is unrelated to this project and its developers, and all resulting disputes, losses, and legal liabilities shall be borne solely by the party conducting such activity.

❤️ Sponsors

Want to appear here?

🚀 The platform offers a variety of channels to choose from: an ultra-low-price 0.02x OpenAI promotional group (limited time), groups as low as 0.25x OpenAI, 0.7x Claude with 95% fixed cache, and a 1.2x Claude Max channel. It also provides a public status page showing real-time availability, latency, and operating status of each group for transparent and reliable service, plus 7×24 human technical support (not bots) with fast responses to developer needs.

Overview

Sub2API is an AI API gateway platform designed to distribute and manage API quotas from AI product subscriptions. Users can access upstream AI services through platform-generated API Keys, while the platform handles authentication, billing, load balancing, and request forwarding.

Features

  • Multi-Account Management - Support multiple upstream account types (OAuth, API Key)
  • API Key Distribution - Generate and manage API Keys for users
  • Precise Billing - Token-level usage tracking and cost calculation
  • Smart Scheduling - Intelligent account selection with sticky sessions
  • Concurrency Control - Per-user and per-account concurrency limits
  • Rate Limiting - Configurable request and token rate limits
  • Built-in Payment System - Supports EasyPay, Alipay, WeChat Pay, and Stripe for user self-service top-up, no separate payment service needed (Configuration Guide)
  • Admin Dashboard - Web interface for monitoring and management
  • Composite Groups - Admin routing layer that resolves requested models to concrete providers for multi-provider groups (Operator Guide)
  • External System Integration - Embed external systems (e.g. ticketing) via iframe to extend the admin dashboard

Ecosystem

Community projects that extend or integrate with Sub2API:

Project Description Features
Sub2ApiPay Self-service payment system Now Built-in — Payment is now integrated into Sub2API, no separate deployment needed. See Payment Configuration Guide
sub2api-mobile Mobile admin console Cross-platform app (iOS/Android/Web) for user management, account management, monitoring dashboard, and multi-backend switching; built with Expo + React Native

Tech Stack

Component Technology
Backend Go 1.25.7, Gin, Ent
Frontend Vue 3.4+, Vite 5+, TailwindCSS
Database PostgreSQL 15+
Cache/Queue Redis 7+

Nginx Reverse Proxy Note

When using Nginx as a reverse proxy for Sub2API (or CRS) with Codex CLI, add the following to the http block in your Nginx configuration:

underscores_in_headers on;

Nginx drops headers containing underscores by default (e.g. session_id), which breaks sticky session routing in multi-account setups.


Deployment

Method 1: Script Installation (Recommended)

One-click installation script that downloads pre-built binaries from GitHub Releases.

Prerequisites

  • Linux server (amd64 or arm64)
  • PostgreSQL 15+ (installed and running)
  • Redis 7+ (installed and running)
  • Root privileges

Installation Steps

curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash

The script will:

  1. Detect your system architecture
  2. Download the latest release
  3. Install binary to /opt/sub2api
  4. Create systemd service
  5. Configure system user and permissions

Post-Installation

# 1. Start the service
sudo systemctl start sub2api

# 2. Enable auto-start on boot
sudo systemctl enable sub2api

# 3. Open Setup Wizard in browser
# http://YOUR_SERVER_IP:8080

The Setup Wizard will guide you through:

  • Database configuration
  • Redis configuration
  • Admin account creation

Upgrade

You can upgrade directly from the Admin Dashboard by clicking the Check for Updates button in the top-left corner.

The web interface will:

  • Check for new versions automatically
  • Download and apply updates with one click
  • Support rollback if needed

Useful Commands

# Check status
sudo systemctl status sub2api

# View logs
sudo journalctl -u sub2api -f

# Restart service
sudo systemctl restart sub2api

# Uninstall
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash -s -- uninstall -y

Method 2: Docker Compose (Recommended)

Deploy with Docker Compose, including PostgreSQL and Redis containers.

Prerequisites

  • Docker 20.10+
  • Docker Compose v2+

Quick Start (One-Click Deployment)

Use the automated deployment script for easy setup:

# Create deployment directory
mkdir -p sub2api-deploy && cd sub2api-deploy

# Download and run deployment preparation script
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash

# Start services
docker compose up -d

# View logs
docker compose logs -f sub2api

What the script does:

  • Downloads docker-compose.local.yml (saved as docker-compose.yml) and .env.example
  • Generates secure credentials (JWT_SECRET, TOTP_ENCRYPTION_KEY, POSTGRES_PASSWORD)
  • Creates .env file with auto-generated secrets
  • Creates data directories (uses local directories for easy backup/migration)
  • Displays generated credentials for your reference

Manual Deployment

If you prefer manual setup:

# 1. Clone the repository
git clone https://github.com/Wei-Shaw/sub2api.git
cd sub2api/deploy

# 2. Copy environment configuration
cp .env.example .env
chmod 600 .env

# 3. Edit configuration (generate secure passwords)
nano .env

Required configuration in .env:

# PostgreSQL password (REQUIRED)
POSTGRES_PASSWORD=your_secure_password_here

# JWT Secret (RECOMMENDED - keeps users logged in after restart)
JWT_SECRET=your_jwt_secret_here

# TOTP Encryption Key (RECOMMENDED - preserves 2FA after restart)
TOTP_ENCRYPTION_KEY=your_totp_key_here

# Optional: Admin account
[email protected]
ADMIN_PASSWORD=your_admin_password

# Optional: Custom port
SERVER_PORT=8080

Generate secure secrets:

# Generate JWT_SECRET
openssl rand -hex 32

# Generate TOTP_ENCRYPTION_KEY
openssl rand -hex 32

# Generate POSTGRES_PASSWORD
openssl rand -hex 32
# 4. Create data directories (for local version)
mkdir -p data postgres_data redis_data

# 5. Start all services
# Option A: Local directory version (recommended - easy migration)
docker compose -f docker-compose.local.yml up -d

# Option B: Named volumes version (simple setup)
docker compose up -d

# 6. Check status
docker compose -f docker-compose.local.yml ps

# 7. View logs
docker compose -f docker-compose.local.yml logs -f sub2api

Deployment Versions

Version Data Storage Migration Best For
docker-compose.local.yml Local directories ✅ Easy (tar entire directory) Production, frequent backups
docker-compose.yml Named volumes ⚠️ Requires docker commands Simple setup

Recommendation: Use docker-compose.local.yml (deployed by script) for easier data management.

Access

Open http://YOUR_SERVER_IP:8080 in your browser.

If admin password was auto-generated, find it in logs:

docker compose -f docker-compose.local.yml logs sub2api | grep "admin password"

Upgrade

# Pull latest image and recreate container
docker compose -f docker-compose.local.yml pull
docker compose -f docker-compose.local.yml up -d

Easy Migration (Local Directory Version)

When using docker-compose.local.yml, migrate to a new server easily:

# On source server
docker compose -f docker-compose.local.yml down
cd ..
tar czf sub2api-complete.tar.gz sub2api-deploy/

# Transfer to new server
scp sub2api-complete.tar.gz user@new-server:/path/

# On new server
tar xzf sub2api-complete.tar.gz
cd sub2api-deploy/
docker compose -f docker-compose.local.yml up -d

Useful Commands

# Stop all services
docker compose -f docker-compose.local.yml down

# Restart
docker compose -f docker-compose.local.yml restart

# View all logs
docker compose -f docker-compose.local.yml logs -f

# Remove all data (caution!)
docker compose -f docker-compose.local.yml down
rm -rf data/ postgres_data/ redis_data/

Method 3: Apple container (macOS)

Apple-silicon Macs running macOS 26 can run the full Sub2API, PostgreSQL, and Redis stack with Apple container 1.1.0 or newer:

git clone https://github.com/Wei-Shaw/sub2api.git
cd sub2api/deploy
./apple-container.sh init
./apple-container.sh up
./apple-container.sh status

This is an operator-managed local workflow; Docker Compose remains the recommended production path. See deploy/APPLE_CONTAINER.md for lifecycle commands, persistence, upgrades, and runtime limitations.


Method 4: Build from Source

Build and run from source code for development or customization.

Prerequisites

  • Go 1.21+
  • Node.js 18+
  • PostgreSQL 15+
  • Redis 7+

Build Steps

# 1. Clone the repository
git clone https://github.com/Wei-Shaw/sub2api.git
cd sub2api

# 2. Install pnpm (if not already installed)
npm install -g pnpm

# 3. Build frontend
cd frontend
pnpm install
pnpm run build
# Output will be in ../backend/internal/web/dist/

# 4. Build backend with embedded frontend
cd ../backend
VERSION="$(./scripts/resolve-version.sh)"
go build -tags embed -ldflags="-X main.Version=${VERSION}" -o sub2api ./cmd/server

# 5. Create configuration file
cp ../deploy/config.example.yaml ./config.yaml

# 6. Edit configuration
nano config.yaml

Note: The -tags embed flag embeds the frontend into the binary. Without this flag, the binary will not serve the frontend UI.

Key configuration in config.yaml:

server:
  host: "0.0.0.0"
  port: 8080
  mode: "release"

database:
  host: "localhost"
  port: 5432
  user: "postgres"
  password: "your_password"
  dbname: "sub2api"

redis:
  host: "localhost"
  port: 6379
  username: ""
  password: ""

jwt:
  secret: "change-this-to-a-secure-random-string"
  expire_hour: 24

default:
  user_concurrency: 5
  user_balance: 0
  api_key_prefix: "sk-"
  rate_multiplier: 1.0

Sora Status (Temporarily Unavailable)

⚠️ Sora-related features are temporarily unavailable due to technical issues in upstream integration and media delivery. Please do not rely on Sora in production at this time. Existing gateway.sora_* configuration keys are reserved and may not take effect until these issues are resolved.

Additional security-related options are available in config.yaml:

  • cors.allowed_origins for CORS allowlist
  • security.url_allowlist for upstream/pricing/CRS host allowlists
  • security.url_allowlist.enabled to disable URL validation (use with caution)
  • security.url_allowlist.allow_insecure_http to allow HTTP URLs when validation is disabled
  • security.url_allowlist.allow_private_hosts to allow private/local IP addresses
  • security.response_headers.enabled to enable configurable response header filtering (disabled uses default allowlist)
  • security.csp to control Content-Security-Policy headers
  • billing.circuit_breaker to fail closed on billing errors
  • security.trust_forwarded_ip_for_api_key_acl enables legacy raw forwarded-header takeover (enabled by default for upgrade compatibility); disable it to enforce server.trusted_proxies, which should contain only the exact proxy CIDRs that connect directly to Sub2API
  • security.forwarded_client_ip_headers configures up to 16 third-party CDN client-IP header names; they are checked in order before the built-in headers only while legacy takeover is enabled
  • turnstile.required to require Turnstile in release mode

Custom client-IP headers can be set in YAML or as a comma-separated environment variable:

SECURITY_FORWARDED_CLIENT_IP_HEADERS=True-Client-IP,X-CDN-Client-IP

Header names are validated, canonicalized, and de-duplicated. The admin security settings can update the list without a restart; new installations persist YAML/environment defaults and existing installations backfill a missing database value. When legacy takeover is disabled, all custom and built-in raw forwarding headers are ignored and Gin uses only server.trusted_proxies. While takeover is enabled, firewall the origin to CDN/proxy addresses and make the edge overwrite every trusted client-IP header. See deploy/EDGE_SECURITY.md for the complete migration and trust-boundary rules.

⚠️ Security Warning: HTTP URL Configuration

When security.url_allowlist.enabled=false, the system performs minimal URL validation and allows HTTP URLs by default (dev-friendly mode; Docker Compose deployments use the same default). For production, explicitly tighten this to HTTPS-only:

security:
  url_allowlist:
    enabled: false                # Disable allowlist checks
    allow_insecure_http: false    # HTTPS only (recommended for production)

Or via environment variable:

SECURITY_URL_ALLOWLIST_ENABLED=false
SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP=false

Risks of allowing HTTP:

  • API keys and data transmitted in plaintext (vulnerable to interception)
  • Susceptible to man-in-the-middle (MITM) attacks
  • NOT suitable for production environments

When to use HTTP:

  • ✅ Development/testing with local servers (http://localhost)
  • ✅ Internal networks with trusted endpoints
  • ✅ Testing account connectivity before obtaining HTTPS
  • ❌ Production environments (use HTTPS only)

Example error for HTTP URLs when allow_insecure_http: false is set:

Invalid base URL: invalid url scheme: http

If you disable URL validation or response header filtering, harden your network layer:

  • Enforce an egress allowlist for upstream domains/IPs
  • Block private/loopback/link-local ranges
  • Enforce TLS-only outbound traffic
  • Strip sensitive upstream response headers at the proxy

OpenAI Responses WebSocket ingress limits

gateway.openai_ws bounds the lifetime and aggregate count of client-facing Responses WebSocket sessions. These safeguards apply independently from per-turn user and account concurrency slots, which are released between turns.

gateway:
  openai_ws:
    # Total time to receive and decompress the first client message.
    client_first_message_timeout_seconds: 30
    # Close a client socket idle between completed turns; 0 disables this safeguard.
    ingress_inter_turn_idle_timeout_seconds: 300
    # Distributed API-key limit for live client ingress sessions; 0 disables it.
    max_ingress_connections_per_api_key: 64

The first-message timeout is a total read deadline. Deployments that accept large contexts or image-heavy requests over slower links can raise it to 120-300 seconds. It expires before HTTP bridge routing, so bridge mode does not override this limit.

The connection cap is coordinated through Redis using a 60-second lease that is refreshed every 20 seconds. A process that cannot confirm a lease for a full lease lifetime closes its local WebSocket rather than continuing outside the global cap.

Enable the v2 mode router before selecting an account-level WS mode such as http_bridge:

gateway:
  openai_ws:
    mode_router_v2_enabled: true

Or set GATEWAY_OPENAI_WS_MODE_ROUTER_V2_ENABLED=true in the environment. Use http_bridge for client-WebSocket/upstream-HTTP operation when rolling out or mitigating upstream WebSocket issues.

⚠️ Important: Creating the Admin Account

The initial admin account is only created via the setup wizard (served at http://<host>:8080 on first run). The default.admin_email / default.admin_password fields in config.yaml are not used to create it — they exist in the template for historical reasons.

Because step 5 above pre-creates config.yaml, the setup wizard will be skipped on first run: the server detects an existing config and boots straight into normal mode with an empty users table, so the first login attempt fails with invalid email or password.

Two ways to create the admin account:

  1. Recommended — let the wizard generate config.yaml: Skip step 5 (do not run the cp). Start ./sub2api directly; the setup wizard at http://localhost:8080 walks you through database, Redis, and admin account setup, then writes config.yaml for you.

  2. If you already created config.yaml: Temporarily move it aside so the wizard can trigger on first run, then restore it afterwards:

    mv config.yaml config.yaml.bak
    ./sub2api        # wizard runs at http://localhost:8080 and writes a fresh config.yaml
    # stop the server (Ctrl+C) once the wizard completes, then restore your config:
    mv config.yaml.bak config.yaml
    ./sub2api        # restart in normal mode and log in with the admin you just created
    
# 6. Run the application
./sub2api

Development Mode

# Backend (with hot reload)
cd backend
go run ./cmd/server

# Frontend (with hot reload)
cd frontend
pnpm run dev

Code Generation

When editing backend/ent/schema, regenerate Ent + Wire:

cd backend
go generate ./ent
go generate ./cmd/server

Simple Mode

Simple Mode is designed for individual developers or internal teams who want quick access without full SaaS features.

  • Enable: Set environment variable RUN_MODE=simple
  • Difference: Hides SaaS-related features and skips billing process
  • Security note: In production, you must also set SIMPLE_MODE_CONFIRM=true to allow startup

Asynchronous Image Tasks

Long-running OpenAI/Grok image generation and editing can be submitted through /v1/images/generations/async or /v1/images/edits/async, then polled at /v1/images/tasks/{task_id} without holding a CDN connection open. See Asynchronous Image Tasks for request and response examples.


Grok / xAI Support

Sub2API supports both Grok subscription accounts through xAI OAuth and standard xAI API-key accounts. Both account types forward OpenAI-compatible Responses traffic to xAI.

Supported Scope

  • Platform name: grok
  • Account types: OAuth subscription accounts and xAI API-key accounts
  • Public Responses targets: /v1/responses, /responses, and /backend-api/codex/responses, forwarded to the Grok subscription proxy for OAuth accounts or https://api.x.ai/v1/responses for API-key accounts
  • Public Claude-compatible target: /v1/messages, converted to xAI Responses and returned as Anthropic Messages output for Claude CLI style clients
  • Public Chat Completions targets: /v1/chat/completions and /chat/completions, forwarded to the account-type-specific xAI upstream
  • Codex CLI style Responses WebSocket ingress is accepted on the Responses targets and bridged to xAI HTTP/SSE Responses upstream
  • Text models: grok-4.5, grok-4.3, grok-build-0.1, grok-composer-2.5-fast, grok-4.20-0309-reasoning, grok-4.20-0309-non-reasoning, and grok-4.20-multi-agent-0309
  • Media targets for Grok groups: /v1/images/generations, /images/generations, /v1/images/edits, /images/edits, /v1/videos/generations, /videos/generations, /v1/videos/edits, /videos/edits, /v1/videos/extensions, /videos/extensions, /v1/videos/{request_id}, and /videos/{request_id}. Generation, editing, and extension requests require the group image-generation permission.
  • Media models: grok-imagine, grok-imagine-image-quality, grok-imagine-image, grok-imagine-edit, grok-imagine-video, and grok-imagine-video-1.5
  • JSON image-edit and video-generation requests accept image references in image, images, reference_images, and mask objects. Use url for xAI-compatible payloads; the legacy image_url field remains accepted and is normalize