PairProxy

企业级 Claude Code 透明代理 — 统一管控 LLM API Key,精确追踪 token 用量,零侵入接入。
┌─────────────┐ HTTP/SSE ┌───────────────────────────┐ HTTPS ┌──────────────────┐
│ Claude Code │──────────────▶│ cproxy 127.0.0.1:8080 │──────────▶│ sproxy :9000 │──▶ Anthropic API
│ (code agent)│ │ 注入用户 JWT │ │ 验证身份 · 配额 │
└─────────────┘ └───────────────────────────┘ │ 统计用量 · 转发 │
└──────────────────┘
两个组件,解决一个问题:多人共用 LLM API Key 时,谁用了多少、超没超额、钱花哪了——一目了然。
功能特性
| 分类 |
功能 |
| 零侵入接入 |
用户只需设置两个环境变量,无需修改 Claude Code 配置 |
| JWT 认证 |
每位用户独立 JWT,access token 24h,refresh token 7天,支持自动刷新 |
| Token 统计 |
同步/流式(SSE)请求均精确统计 input/output tokens,不缓冲不延迟 |
| 费用估算 |
按模型定价配置,Dashboard 实时显示 USD 消耗 |
| 用户配额 |
按分组设置每日/每月 token 上限,超额返回 429 |
| 速率限制 |
每用户每分钟请求数(RPM)限制,滑动窗口算法 |
| 负载均衡 |
cproxy↔sproxy、sproxy↔LLM 两级负载均衡,加权随机策略 |
| 健康检查 |
主动(GET /health)+ 被动(连续失败熔断)双重检查 |
| 集群模式 |
primary + worker 多节点,路由表自动下发给 cproxy |
| Web Dashboard |
Go 模板 + Tailwind CSS,内嵌二进制,无需前端构建 |
| Admin CLI |
命令行管理用户、分组、配额、统计 |
| Prometheus 指标 |
GET /metrics,标准文本格式,可接 Grafana |
| Webhook 告警 |
节点故障、配额超限等事件推送到 Slack/飞书/企业微信 |
快速开始
前置条件
- Go 1.21+(仅编译时需要,二进制无其他依赖)
- 一台可被开发者访问的服务器(部署 sproxy)
- Anthropic API Key
1. 编译
git clone https://github.com/l17728/pairproxy.git
cd pairproxy
make build
# 输出:bin/cproxy bin/sproxy
跨平台发布包(Linux/macOS/Windows × amd64/arm64):
make release
# 输出:dist/pairproxy-linux-amd64.tar.gz 等
2. 部署 sproxy(服务端,一次性操作)
# 生成 admin 密码 hash
./bin/sproxy hash-password
# 输入密码后得到 $2a$10$... 格式的 hash
# 准备环境变量
export ANTHROPIC_API_KEY_1="sk-ant-api03-..."
export JWT_SECRET="$(openssl rand -hex 32)"
export ADMIN_PASSWORD_HASH='$2a$10$...' # 上一步输出的 hash
# 复制并编辑配置
cp config/sproxy.yaml.example sproxy.yaml
# 至少修改:llm.targets[0].url、database.path
# 启动
./bin/sproxy start --config sproxy.yaml
# INFO sproxy listening addr=0.0.0.0:9000
# INFO dashboard enabled path=/dashboard/
创建用户和分组(服务启动后操作):
# 创建分组(设置每日 100万 token 上限,每分钟 60 次请求)
./bin/sproxy admin group add engineering \
--daily-limit 1000000 --monthly-limit 20000000 --rpm 60
# 创建用户
./bin/sproxy admin user add alice --group engineering
# Password: ****
3. 用户本地配置(每位开发者执行一次)
# 下载 cproxy 二进制(或自行编译)
# 登录,获取 JWT
cproxy login --server http://proxy.company.com:9000
# Username: alice
# Password: ****
# ✓ Login successful. Token saved to ~/.config/pairproxy/token.json
# 启动本地代理
cproxy start
# cproxy listening on http://127.0.0.1:8080
4. 配置 Claude Code
# 在 shell profile 中添加(或在 IDE 中设置环境变量)
export ANTHROPIC_BASE_URL=http://127.0.0.1:8080
export ANTHROPIC_API_KEY=any-placeholder # 值任意填写,cproxy 会用 JWT 替换真实认证头
说明:ANTHROPIC_API_KEY 仅用于满足 Claude Code SDK 的非空校验,其值不会被发送给 Anthropic。cproxy 会将此 header 替换为用户 JWT,sproxy 再将其替换为真实 API Key。
之后正常使用 Claude Code,所有请求自动经过代理统计。
架构说明
Claude Code 发出:
POST http://127.0.0.1:8080/v1/messages
Authorization: Bearer any-placeholder ← 假 key
cproxy 转发给 sproxy:
POST http://proxy.company.com:9000/v1/messages
X-PairProxy-Auth: eyJhbGc... ← 用户 JWT(替换原 Authorization)
sproxy 转发给 Anthropic:
POST https://api.anthropic.com/v1/messages
Authorization: Bearer sk-ant-REAL-KEY ← 真实 API Key(替换 X-PairProxy-Auth)
用户永远看不到真实 API Key。
JWT 自动刷新机制
cproxy 启动时读取 ~/.pairproxy/token.json,并在请求前检查 token 有效期:
token 有效期 > refresh_threshold(默认30分钟)
→ 直接使用当前 access token
token 有效期 ≤ refresh_threshold
→ 用 refresh_token 向 sproxy 换取新的 access token
→ 新 token 保存到 token.json
refresh_token 也已过期(> 7天未使用)
→ 返回 401,提示用户重新执行 cproxy login
access token 有效期 24h,refresh token 7天,正常使用下用户无需手动登录。
Streaming Token 捕获
sproxy 使用 TeeResponseWriter 包装响应流:
Anthropic SSE 流
│
├── 同步写给 Claude Code(零缓冲,不增加延迟)
│
└── 同时解析 SSE 事件行
message_start → 记录 input_tokens
message_delta → 记录 output_tokens
message_stop → 异步写入 SQLite usage_logs
集群路由表推送
cproxy 启动
│
└── 连接 sproxy primary(cfg.sproxy.primary)
│
sproxy primary 在响应头中注入路由表:
X-Routing-Version: 3
X-Routing-Update: base64(JSON{entries:[sp1,sp2,sp3]})
│
cproxy 解析路由头,更新本地 Balancer
│
后续请求自动负载均衡到 sp1/sp2/sp3
Web Dashboard
访问 http://<sproxy-host>:9000/dashboard/,使用 admin 密码登录。
| 页面 |
内容 |
| 概览 |
今日 token 总量、请求次数、成功率、活跃用户数、估算费用;最近请求列表 |
| 用户 |
用量排行、创建用户、启用/禁用、重置密码 |
| 分组 |
分组管理、设置每日/每月 token 上限和 RPM |
| 日志 |
最近 N 条请求记录,支持按用户 ID 过滤 |
Admin CLI
# 用户管理
sproxy admin user add <username> [--group <group>]
sproxy admin user list [--group <group>]
sproxy admin user disable <username>
sproxy admin user enable <username>
sproxy admin user reset-password <username>
# 分组管理
sproxy admin group add <name> [--daily-limit <n>] [--monthly-limit <n>] [--rpm <n>]
sproxy admin group list
sproxy admin group set-quota <name> [--daily <n>] [--monthly <n>] [--rpm <n>]
# 统计查询
sproxy admin stats [--user <username>] [--days <n>]
# Token 管理
sproxy admin token revoke <username> # 强制下线用户
# 工具
sproxy hash-password [--password <pwd>] # 生成 bcrypt hash
sproxy version # 查看版本信息
# cproxy 命令
cproxy login --server <url> # 登录并保存 token
cproxy start [--config <path>]# 启动本地代理
cproxy status # 查看 token 状态
cproxy logout # 登出并撤销 refresh token
cproxy version # 查看版本信息
配置文件
cproxy(~/.config/pairproxy/cproxy.yaml)
listen:
host: "127.0.0.1"
port: 8080
sproxy:
primary: "http://proxy.company.com:9000" # 必填
lb_strategy: "round_robin"
health_check_interval: 30s
request_timeout: 300s
auth:
refresh_threshold: 30m # token 过期前多久自动刷新
log:
level: "info"
sproxy(sproxy.yaml)
listen:
host: "0.0.0.0"
port: 9000
llm:
lb_strategy: "round_robin"
request_timeout: 300s
targets:
- url: "https://api.anthropic.com"
api_key: "${ANTHROPIC_API_KEY_1}" # 从环境变量读取
weight: 1
database:
path: "/var/lib/pairproxy/pairproxy.db"
write_buffer_size: 200
flush_interval: 5s
auth:
jwt_secret: "${JWT_SECRET}" # 必填,建议从环境变量注入
access_token_ttl: 24h
refresh_token_ttl: 168h
admin:
password_hash: "${ADMIN_PASSWORD_HASH}"
cluster:
role: "primary" # primary | worker
self_addr: "http://sp1.company.com:9000"
self_weight: 50
alert_webhook: "" # Slack/飞书 webhook,留空不发告警
dashboard:
enabled: true
pricing:
default_input_per_1k: 0.003
default_output_per_1k: 0.015
models:
claude-sonnet-4-5:
input_per_1k: 0.003
output_per_1k: 0.015
claude-opus-4-5:
input_per_1k: 0.015
output_per_1k: 0.075
log:
level: "info"
完整注释版见 config/sproxy.yaml.example 和 config/cproxy.yaml.example。
集群部署
┌───────────────────────────────┐
[开发者 A] cproxy ──┤ │
[开发者 B] cproxy ──┤ sp-1 primary :9000 │──▶ Anthropic API
[开发者 C] cproxy ──┤ sp-2 worker :9000 │──▶ Anthropic API
│ sp-3 worker :9000 │──▶ Anthropic API
└──────────────┬────────────────┘
│
Web Dashboard
http://sp-1:9000/dashboard/
worker 节点额外配置:
cluster:
role: "worker"
primary: "http://sp1.company.com:9000" # primary 地址
self_addr: "http://sp2.company.com:9000"
self_weight: 50
dashboard:
enabled: false # worker 不开 Dashboard
worker 节点通过心跳向 primary 注册,primary 将路由表下发给所有 cproxy,实现自动感知和负载均衡。
Docker 部署
快速启动
# 1. 准备配置文件和密钥
cp config/sproxy.yaml.example sproxy.yaml # 编辑填入实际地址
cp .env.example .env # 编辑填入 API Key / JWT Secret
# 2. 启动
docker compose up -d
# 3. 查看日志
docker compose logs -f sproxy
手动构建镜像
# 构建 sproxy(注入版本信息)
docker build \
--build-arg VERSION=$(git describe --tags --always) \
--build-arg COMMIT=$(git rev-parse --short HEAD) \
--build-arg BUILT=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
-t pairproxy/sproxy .
# 运行
docker run -d \
--name sproxy \
-p 9000:9000 \
--env-file .env \
-v $(pwd)/sproxy.yaml:/etc/pairproxy/sproxy.yaml:ro \
-v sproxy-data:/var/lib/pairproxy \
pairproxy/sproxy
镜像说明
最终镜像基于 gcr.io/distroless/static-debian12:含 CA 证书(HTTPS 请求 Anthropic 需要)、无 shell、以 UID 65532 非 root 用户运行。二进制为全静态编译(CGO_ENABLED=0),镜像约 15 MB。
| 文件 |
说明 |
Dockerfile |
多阶段构建,--build-arg BINARY=cproxy|sproxy |
.dockerignore |
排除编译产物、DB 文件、.git 等 |
docker-compose.yml |
单机部署,含 worker 节点注释模板 |
.env.example |
环境变量模板(复制为 .env 后填写真实值) |
systemd 部署(Linux 生产环境)
安装
# 1. 复制二进制
sudo cp bin/sproxy /usr/local/bin/sproxy
sudo chmod +x /usr/local/bin/sproxy
# 2. 创建最小权限系统用户
sudo useradd --system --no-create-home --shell /usr/sbin/nologin pairproxy
# 3. 创建目录
sudo mkdir -p /etc/pairproxy /var/lib/pairproxy
sudo chown pairproxy:pairproxy /var/lib/pairproxy
sudo chmod 750 /var/lib/pairproxy
# 4. 复制配置文件
sudo cp config/sproxy.yaml.example /etc/pairproxy/sproxy.yaml
sudo chown root:pairproxy /etc/pairproxy/sproxy.yaml
sudo chmod 640 /etc/pairproxy/sproxy.yaml
# 编辑 sproxy.yaml,填写实际地址和路径
# 5. 创建环境变量文件(存放密钥,不进配置文件)
sudo install -m 640 -o root -g pairproxy /dev/null /etc/pairproxy/sproxy.env
sudo tee /etc/pairproxy/sproxy.env <<'EOF'
ANTHROPIC_API_KEY_1=sk-ant-api03-...
JWT_SECRET=your-32-char-random-secret
ADMIN_PASSWORD_HASH=$2a$10$...
EOF
# 6. 安装 service 文件并启动
sudo cp config/sproxy.service /etc/systemd/system/sproxy.service
sudo systemctl daemon-reload
sudo systemctl enable --now sproxy
常用运维命令
sudo systemctl status sproxy # 查看运行状态
sudo journalctl -u sproxy -f # 实时查看日志
sudo journalctl -u sproxy --since today # 今日日志
sudo systemctl reload sproxy # 热重载(发送 SIGHUP)
sudo systemctl restart sproxy # 重启服务
service 文件默认启用了以下安全加固:NoNewPrivileges、ProtectSystem=strict、PrivateTmp、MemoryDenyWriteExecute、系统调用白名单过滤、禁止 core dump(防止内存中密钥落盘)。
完整 service 文件见 config/sproxy.service,worker 节点见 config/sproxy-worker.service。
Prometheus 指标
GET http://<sproxy-host>:9000/metrics
# HELP pairproxy_tokens_today Total tokens today
pairproxy_tokens_today{type="input"} 1234567
pairproxy_tokens_today{type="output"} 345678
# HELP pairproxy_requests_today Total requests today
pairproxy_requests_today{type="total"} 1000
pairproxy_requests_today{type="error"} 5
# HELP pairproxy_active_users_today Active users today
pairproxy_active_users_today 12
# HELP pairproxy_cost_usd_today Estimated cost today (USD)
pairproxy_cost_usd_today 4.2100
指标每 30 秒缓存一次,避免频繁查询 DB。
Makefile 常用命令
make build # 编译当前平台二进制到 bin/
make test # 运行全部测试
make test-race # 竞态检测
make test-cover # 生成覆盖率报告
make release # 跨平台发布包(dist/)
make vet fmt lint # 代码检查
make bcrypt-hash # 生成 admin 密码 hash
make clean # 清理 bin/
发布新版本
确认所有改动已合并到 main 后,推送一个符合语义版本的 tag,CI 自动完成剩余全部工作:
git tag v1.2.3 && git push origin v1.2.3
GitHub Actions release.yml 随后自动执行:
- 交叉编译 5 个平台的二进制(Linux/macOS/Windows × amd64/arm64)
- 生成
SHA256SUMS.txt 校验文件
- 创建 GitHub Release,附上所有产物和自动生成的 release notes
- 构建 多架构 Docker 镜像(
linux/amd64 + linux/arm64)并推送到 ghcr.io/l17728/pairproxy,标签包含 v1.2.3、1.2、1、latest
项目结构
pairproxy/
├── cmd/
│ ├── cproxy/main.go # cproxy CLI 入口
│ └── sproxy/main.go # sproxy CLI 入口 + admin 子命令
├── internal/
│ ├── auth/ # JWT 管理、bcrypt、本地 token 文件
│ ├── proxy/ # cproxy/sproxy 核心 HTTP 处理器 + 中间件
│ ├── tap/ # TeeResponseWriter + Anthropic SSE 解析器
│ ├── lb/ # Balancer 接口、加权随机、健康检查
│ ├── cluster/ # 路由表、PeerRegistry、Reporter
│ ├── quota/ # 配额检查、速率限制、中间件
│ ├── db/ # SQLite + GORM 模型、UserRepo、UsageRepo
│ ├── api/ # AuthHandler、AdminHandler、ClusterHandler
│ ├── dashboard/ # Web Dashboard(Go 模板 + embed)
│ ├── metrics/ # Prometheus 格式 /metrics 端点
│ ├── alert/ # Webhook 告警通知器
│ ├── config/ # YAML 配置加载(支持 ${ENV_VAR} 展开)
│ └── version/ # 版本信息
├── config/
│ ├── cproxy.yaml.example
│ ├── sproxy.yaml.example
│ └── sproxy-worker.yaml.example
├── Makefile
└── go.mod # module: github.com/l17728/pairproxy
技术选型
| 组件 |
选型 |
理由 |
| HTTP 反向代理 |
net/http/httputil.ReverseProxy |
标准库,SSE 天然支持 |
| 数据库 |
SQLite + GORM (glebarez/sqlite) |
纯 Go,无 CGO,单文件部署 |
| CLI |
cobra |
标准 Go CLI 框架 |
| 日志 |
zap |
结构化日志,高性能 |
| 密码 |
bcrypt (golang.org/x/crypto) |
行业标准 |
| 前端 |
Go HTML 模板 + Tailwind CSS CDN |
无需构建,内嵌二进制 |
License
Apache License 2.0