Skip to content

Forward Code Agent messages and count usage information.

License

Notifications You must be signed in to change notification settings

l17728/pairproxy

PairProxy

CI Release Go Report Card

企业级 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天,支持自动刷新
LDAP / AD 集成 一行配置接入企业 LDAP / Active Directory,首次登录自动 JIT 创建用户,支持 LDAPS
多 Provider 支持 透传 Anthropic / OpenAI / Ollama 请求,按路径自动路由,精确解析各 provider token 用量
数据导出 sproxy admin export --format csv|json CLI 导出或 GET /api/admin/export 流式下载;sproxy admin backup 一键备份 SQLite
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;含 DB 文件大小、配额缓存命中率、心跳延迟
Webhook 告警 节点故障、配额超限等事件推送到 Slack/飞书/企业微信
OpenTelemetry 追踪 可选启用,支持 gRPC/HTTP/stdout exporter;自动对接 Jaeger、Grafana Tempo 等后端
登录频率限制 每 IP 5 次失败后锁定 5 分钟,防暴力破解
管理审计日志 所有用户/分组增删改操作记录到 audit_logs,可在 Dashboard 查看
Token 自动刷新 cproxy 自动检测 token 过期,5s 内向 sproxy 换取新 token

快速开始

前置条件

  • 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,所有请求自动经过代理统计。


架构说明

Header 替换流程

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.exampleconfig/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,仅重载 log.level)
sudo systemctl restart sproxy         # 重启服务(完整配置重载)

service 文件默认启用了以下安全加固:NoNewPrivilegesProtectSystem=strictPrivateTmpMemoryDenyWriteExecute、系统调用白名单过滤、禁止 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 随后自动执行:

  1. 交叉编译 5 个平台的二进制(Linux/macOS/Windows × amd64/arm64)
  2. 生成 SHA256SUMS.txt 校验文件
  3. 创建 GitHub Release,附上所有产物和自动生成的 release notes
  4. 构建 多架构 Docker 镜像(linux/amd64 + linux/arm64)并推送到 ghcr.io/l17728/pairproxy,标签包含 v1.2.31.21latest

项目结构

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

文档

文档 说明
docs/manual.md 完整用户手册(部署、配置、CLI 命令、Dashboard 使用)
docs/API.md REST API 参考(Auth、Admin、Stats 端点)
docs/CLUSTER_DESIGN.md 多节点集群架构设计(路由分发、心跳、故障恢复)
docs/SECURITY.md 安全模型说明(JWT 防护、集群 API 鉴权、TLS 配置、密钥轮换)
docs/TROUBLESHOOTING.md 故障排查手册(启动失败、JWT 错误、配额超限、集群问题、性能调优)
docs/UPGRADE.md 版本升级指南(Schema 变更、回滚方法、不兼容变更清单)
docs/PERFORMANCE.md 性能调优指南(缓冲区、WAL 模式、连接池、缓存调优)

技术选型

组件 选型 理由
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

About

Forward Code Agent messages and count usage information.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages