AirGate is not another monolithic gateway that hard-codes a list of AI providers. It is an open architecture where provider capabilities are shipped as plugins and loaded by the runtime on demand.
- Core (this repo) = users, accounts, scheduling, billing, rate limiting, subscriptions, admin dashboard — everything provider-agnostic.
- Plugin = a standalone Go process that talks gRPC to Core and implements the SDK contract for a specific upstream.
Plugins can be released, installed, uninstalled, and hot-reloaded independently, with zero downtime to Core or other plugins. You only ship the capabilities you need, and writing a private plugin for an internal service is a first-class workflow.
- 🔌 Plugin runtime — Provider capabilities run as gRPC subprocesses (powered by hashicorp/go-plugin). Install via marketplace, GitHub Release, binary upload, or dev hot-reload — all without restarting Core.
- 🧩 Dynamic route injection — Routes declared by a plugin are auto-registered into the HTTP gateway. Account form fields and React components are auto-mounted into the admin dashboard.
- 🎯 Smart account scheduling — Priority + health + concurrency limit drive automatic account selection, with degraded accounts auto-quarantined.
- 💰 Accurate billing — Token × per-model price metering in real time, with rate multipliers, user balances, subscriptions, and quotas.
- 🛡 Complete admin dashboard — Users, groups, accounts, subscriptions, IPs, proxy pool, plugin marketplace, and settings in one place. Account import/export, auto-refresh, and admin API key authentication included.
- 📦 One-command deploy — Multi-arch images (amd64/arm64) on
ghcr.io. End users only needdocker compose up -d.
| Plugin | Type | Capabilities | Repository |
|---|---|---|---|
| gateway-openai | gateway | OpenAI Responses / Chat Completions / ChatGPT OAuth / Anthropic protocol translation / WebSocket | DouDOU-start/airgate-openai |
| gateway-claude | gateway | Claude Messages API gateway: OAuth authorization, TLS fingerprinting, usage monitoring | DouDOU-start/airgate-claude |
| gateway-kiro | gateway | Kiro (AWS CodeWhisperer) reverse proxy gateway compatible with Anthropic Messages API | DouDOU-start/airgate-kiro |
| airgate-playground | extension | AI chat plugin: web chat, multi-model switching, conversation management | DouDOU-start/airgate-playground |
| airgate-studio | extension | Unified creation center for multimodal image, video, and audio generation | DouDOU-start/airgate-studio |
| payment-epay | extension | Multi-channel payment: EPay (Xunhu/Rainbow) / Alipay Official / WeChat Pay Official, with recharge page, order management, provider configuration | DouDOU-start/airgate-epay |
| airgate-health | extension | AI provider health monitoring: active probing, availability/latency aggregation, public status page | DouDOU-start/airgate-health |
In the admin dashboard → Plugin Management → choose any of:
1. Marketplace → click "Install" (pulls latest GitHub Release matching your arch)
2. Upload → drop a binary file (good for private plugins)
3. GitHub → enter owner/repo (good for plugins not yet listed in marketplace)
The marketplace periodically syncs the latest release of each plugin via the GitHub API (every 6 hours by default, using ETag to avoid quota cost). You can also click the refresh button on the marketplace page to sync immediately.
Pull in airgate-sdk and implement the GatewayPlugin interface:
type GatewayPlugin interface {
Info() PluginInfo // Metadata: ID, version, account fields, frontend components
Platform() string // Platform key
Models() []ModelInfo // Model list + pricing (used for billing)
Routes() []RouteDefinition // HTTP route declarations
Forward(ctx, req) (*ForwardResult, error) // Actual forwarding logic
}See airgate-openai for a complete reference, including Makefile, release workflow, and embedded frontend.
| Layer | Tech |
|---|---|
| Backend | Go 1.25 · Gin · Ent ORM · PostgreSQL 17 · Redis 8 |
| Frontend | React 19 · Vite · TanStack Query · Tailwind CSS |
| Plugin protocol | hashicorp/go-plugin (gRPC) |
| Deployment | Docker Compose · GitHub Container Registry · multi-arch (amd64/arm64) |
| Auth | JWT + Admin API Key |
Pick one. Both are production-ready.
| Path | Best for | You provide |
|---|---|---|
| 1A. Bare-metal install.sh | You already run PostgreSQL + Redis, want the leanest setup, prefer systemd | PostgreSQL 15+ / Redis 7+ |
| 1B. Docker Compose | A clean server, want pg + redis + core all containerized | Docker only |
⚠️ Pick one — do NOT mix. Running both 1A and 1B gives you two database instances fighting each other.
curl -sSL https://raw.githubusercontent.com/DouDOU-start/airgate-core/master/deploy/install.sh | sudo bashinstall.sh will:
- Detect OS / arch (linux/amd64 or linux/arm64)
- Download
airgate-core-{os}-{arch}from the latest GitHub Release (the frontend SPA and translation files are//go:embed-ed into the binary — single file, ready to run) - Install to
/opt/airgate-core/airgate-corewith sha256 verification - Create system user
airgateand directories/etc/airgate-core//var/lib/airgate-core - Install systemd unit
airgate-core.service
The script does not start the service nor write config.yaml — that's deliberate so you can review first:
sudo systemctl start airgate-core
sudo systemctl enable airgate-core
# Then visit http://<your-host>:9517 — the wizard will ask for:
# - PostgreSQL connection (your existing instance)
# - Redis connection (your existing instance)
# - Admin account
# Final config gets written to /etc/airgate-core/config.yamlAfter the admin UI is up, go to Plugin Management → Marketplace to install gateway-openai / gateway-claude / gateway-kiro / airgate-playground / airgate-studio / payment-epay / airgate-health on demand (/var/lib/airgate-core/plugins is the persistent location).
Upgrade / uninstall:
# Upgrade to latest (config and data preserved)
curl -sSL https://raw.githubusercontent.com/DouDOU-start/airgate-core/master/deploy/install.sh | sudo bash -s -- upgrade
# Pin a specific version
curl -sSL https://raw.githubusercontent.com/DouDOU-start/airgate-core/master/deploy/install.sh | sudo bash -s -- -v v0.1.0
# Uninstall (keeps /etc/airgate-core and /var/lib/airgate-core by default)
curl -sSL https://raw.githubusercontent.com/DouDOU-start/airgate-core/master/deploy/install.sh | sudo bash -s -- uninstall -yCommon commands:
sudo systemctl status airgate-core # status
sudo journalctl -u airgate-core -f # logs
sudo systemctl restart airgate-core # restartmkdir airgate && cd airgate
curl -sSL https://raw.githubusercontent.com/DouDOU-start/airgate-core/master/deploy/docker-deploy.sh | bash
# Review the generated files, then start
docker compose up -d
docker compose logs -f coredocker-deploy.sh only prepares files — it does NOT run up -d for you, so you can audit first:
- Verify
docker/docker composeare installed - Create
data/{postgres,redis,plugins,uploads}under the current directory - Download
docker-compose.yml - Generate
DB_PASSWORD/REDIS_PASSWORD/JWT_SECRETviaopenssl randand write.env(mode 600)
After you up -d, visit http://<your-host>:9517. The install wizard will automatically skip the DB / Redis steps (env vars are already set) and only ask you to create the admin account.
All persistent data lives under ./data/, so backup is just tar czf backup.tgz data .env.
Upgrade / uninstall:
# Upgrade to latest (run in the directory that holds docker-compose.yml)
# Default AIRGATE_IMAGE_TAG=latest, so just pull the new image; if you pinned a version, edit AIRGATE_IMAGE_TAG in .env first
docker compose pull
docker compose up -d
docker compose logs -f core
# Upgrade to a specific version
sed -i 's/^AIRGATE_IMAGE_TAG=.*/AIRGATE_IMAGE_TAG=v0.1.0/' .env
docker compose pull && docker compose up -d
# Uninstall (keep data): stop and remove containers; ./data and .env are untouched
docker compose down
# Full uninstall (also wipes data — back up first!)
docker compose down
rm -rf data .env docker-compose.ymlRecommended: snapshot before upgrading with
tar czf backup-$(date +%F).tgz data .env. Core runs database migrations automatically on startup, no manual step required.
Key environment variables (full list in .env.example):
| Variable | Description | Required |
|---|---|---|
DB_PASSWORD |
Postgres password — do not change after first boot | ✅ |
REDIS_PASSWORD |
Redis auth password, recommended openssl rand -hex 24; not persisted, can be rotated by restart |
✅ |
JWT_SECRET |
JWT signing key, recommended openssl rand -hex 32 |
✅ |
BIND_HOST |
Bind address; set 127.0.0.1 when behind a reverse proxy |
❌ |
PORT |
External port, default 9517 | ❌ |
TZ |
Timezone, default Asia/Shanghai |
❌ |
AIRGATE_IMAGE_TAG |
Image tag, default latest, can pin to v0.x.y |
❌ |
API_KEY_SECRET |
User API Key encryption key, hex-encoded ≥64 chars | ❌ |
If you want to expose core via https://your-domain instead of plain http://host:9517, the simplest option is Caddy — it ships with automatic Let's Encrypt issuance and renewal, and the config is only a dozen lines. The example below targets Ubuntu / Debian and works for both Method 1A and 1B.
Prerequisites
- The domain's A record points to this machine's public IP;
- Firewall / security group allows 80 and 443 (HTTP-01 challenge + HTTPS);
- Port 9517 may stay open or be restricted to localhost — Caddy will be the public entrypoint on 443.
Install Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddyFor other systems see the official install docs. After installation Caddy runs as a systemd service and reads /etc/caddy/Caddyfile.
Configure /etc/caddy/Caddyfile
Replace the file contents with the following, then change the domain and email:
airgate.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:9517 {
# Disable response buffering so SSE / streaming responses arrive in real time
flush_interval -1
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
# LLM requests can be slow — relax the timeouts
transport http {
read_timeout 30m
write_timeout 30m
dial_timeout 10s
}
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
}If core runs in docker compose and Caddy runs on the host, leave 127.0.0.1:9517 as is — compose already publishes 9517 on the host.
Apply / verify
sudo caddy fmt --overwrite /etc/caddy/Caddyfile # format (optional)
sudo systemctl reload caddy # hot reload, no downtime
sudo journalctl -u caddy -f # watch certificate issuanceOn the first reload Caddy contacts Let's Encrypt automatically; within a few seconds to a minute the log shows certificate obtained successfully, and https://airgate.example.com becomes reachable. Renewal is fully automatic.
Common gotchas
- Don't drop
flush_interval -1— without it Caddy buffers the response and SSE / streaming endpoints turn into "all-at-once" replies. - Bump the timeouts — large-model inference can take minutes; Caddy's default reverse-proxy timeouts are too short.
- Port 80 must be open — Let's Encrypt uses HTTP-01 to validate; if 80 is blocked, no certificate. While debugging, add a
{ acme_ca https://acme-staging-v02.api.letsencrypt.org/directory }global block at the top of the file to switch to staging and avoid the production rate limits. - To close direct access on 9517 — change
core.portsin deploy/docker-compose.yml to127.0.0.1:9517:9517so only Caddy can reach it from outside; for the bare-metal install, set the listen address to127.0.0.1inconfig.yaml.
For development or contributions. Pick one of the two paths:
A. Fully containerized (recommended, zero host dependencies)
The host only needs Docker. Clone airgate-sdk and airgate-core into a shared parent directory:
mkdir airgate && cd airgate
git clone https://github.com/DouDOU-start/airgate-sdk.git
git clone https://github.com/DouDOU-start/airgate-core.git
cd airgate-core
docker compose -f deploy/docker-compose.dev.yml updeploy/docker-compose.dev.yml brings up postgres + redis, builds the sdk / core frontends, and runs core via go run ./cmd/server — all inside containers. Visit http://localhost:9517 once it is up.
B. Run on the host directly
Requires Go 1.25+, Node 22+, local Postgres + Redis, and the sibling airgate-sdk repo:
git clone https://github.com/DouDOU-start/airgate-sdk.git
git clone https://github.com/DouDOU-start/airgate-core.git
cd airgate-core
make install # Install backend & frontend dependencies
make dev # Start dev serversSee make help for more commands.
⚠️ Do NOT use the dev compose for production. It runsgo run, bind-mounts host source, and hardcodes weak passwords (airgate/airgate-dev). It is for local development only. For production use Method 1A or 1B.
┌──────────────────────────────────────────┐
│ AirGate Core (this repo) │
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
Users / Admin ──► │ │ HTTP │ │ Sched. │ │ Billing │ │
│ │ Router │ │ + Limit │ │ + Subs │ │
│ └────┬────┘ └────┬────┘ └────┬─────┘ │
│ │ Plugin Manager (gRPC) │ │
│ └────────────┬─────────────┘ │
└────────────────────┼─────────────────────┘
│ go-plugin
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────────┐┌──────────────┐┌──────────────┐
│ gateway- ││ gateway- ││ payment- │
│ openai ││ claude ││ epay │
│ (subprocess) ││ (subprocess) ││ (subprocess) │
└──────┬───────┘└──────┬───────┘└──────────────┘
│ HTTPS │ HTTPS
▼ ▼
OpenAI / ChatGPT Anthropic
Request lifecycle:
User request ──► Core auth ──► Core picks account ──► Plugin.Forward() ──► Upstream AI API
│
▼
ForwardResult
┌──────┴──────┐
Token usage Account status
Core bills Core updates account
airgate-core/
├── backend/ # Go backend
│ ├── cmd/server/ # Entry point
│ ├── internal/
│ │ ├── server/ # HTTP routes & middleware
│ │ ├── plugin/ # Plugin lifecycle + marketplace + forwarder
│ │ ├── scheduler/ # Account scheduling
│ │ ├── billing/ # Billing & usage
│ │ ├── ratelimit/ # Rate limiting
│ │ └── app/ # Domain use cases
│ └── ent/ # Database ORM (Ent)
├── web/ # Admin dashboard (React + Vite)
│ └── src/
│ ├── pages/admin/ # Admin pages
│ ├── shared/api/ # API client
│ └── i18n/ # zh / en strings
├── deploy/ # Deployment
│ ├── install.sh # Bare-metal installer (systemd; curl | sudo bash)
│ ├── docker-deploy.sh # Docker compose helper (curl | bash)
│ ├── airgate-core.service # systemd unit
│ ├── docker-compose.yml # Production compose (pulls ghcr.io image)
│ ├── docker-compose.dev.yml # Development compose (source mount)
│ ├── Dockerfile # Multi-stage build
│ ├── config.docker.yaml # Image-baked default config
│ └── .env.example # Environment template (for docker deploy)
├── .github/workflows/
│ ├── ci.yml # PR checks
│ └── release.yml # Tag-triggered: multi-arch image + native binaries
└── Makefile
- Health check:
GET /healthzpublic endpoint, ready for docker / k8s - Self-contained binary: Frontend SPA and translation files are
//go:embed-ed into the binary. Bare-metal installs are a single file with no extra static asset directories to manage. - Persistence:
- Bare-metal (1A):
/var/lib/airgate-core/{plugins,uploads}+/etc/airgate-core/config.yaml. PostgreSQL / Redis are managed by you. - Docker (1B): All data lives in
./data/{postgres,redis,plugins,uploads}(bind mounts). Backup istar czf backup.tgz data .env.
- Bare-metal (1A):
- Upgrade:
- Bare-metal:
curl -sSL .../install.sh | sudo bash -s -- upgrade - Docker: edit
AIRGATE_IMAGE_TAGin.env→docker compose pull && docker compose up -d
- Bare-metal:
- DB migrations: Ent schema changes regenerate code via
make ent; core auto-migrates on startup - Plugin upgrade: Marketplace → click refresh → uninstall old version → reinstall
Migrating existing Docker named-volume deployments: Older compose files used named volumes
postgres_data/redis_data/airgate_plugins/airgate_uploads. The new compose uses./data/*bind mounts. To migrate:docker compose down mkdir -p data/postgres data/redis data/plugins data/uploads docker run --rm -v <project>_postgres_data:/from -v $(pwd)/data/postgres:/to alpine cp -a /from/. /to/ docker run --rm -v <project>_redis_data:/from -v $(pwd)/data/redis:/to alpine cp -a /from/. /to/ docker run --rm -v <project>_airgate_plugins:/from -v $(pwd)/data/plugins:/to alpine cp -a /from/. /to/ docker run --rm -v <project>_airgate_uploads:/from -v $(pwd)/data/uploads:/to alpine cp -a /from/. /to/ curl -O https://raw.githubusercontent.com/DouDOU-start/airgate-core/master/deploy/docker-compose.yml docker compose up -d # After verifying everything works, drop the old named volumes docker volume rm <project>_postgres_data <project>_redis_data <project>_airgate_plugins <project>_airgate_uploads
<project>is the docker compose project prefix (defaults to the current directory name);docker volume lsshows the actual names.
- Bugs / Features: Issues
- Plugin development docs: airgate-sdk
- Reference plugin implementation: airgate-openai
MIT