CC Gateway — AI API Identity Gateway
CC Gateway — AI API身份网关
Skill by
ara.so — Daily 2026 Skills collection.
CC Gateway is a TypeScript reverse proxy that sits between Claude Code clients and the Anthropic API. It normalizes 40+ device fingerprint dimensions (device ID, email, environment, RAM, headers, and system prompt content) to a single canonical identity, manages OAuth token refresh centrally, and prevents telemetry leakage from multi-machine setups.
技能由
ara.so 提供 — 2026年度技能合集。
CC Gateway是一款TypeScript编写的反向代理,部署在Claude Code客户端与Anthropic API之间。它可将40+项设备指纹维度(设备ID、邮箱、运行环境、RAM、请求头、系统提示词内容)标准化为单一规范身份,集中管理OAuth令牌刷新,防止多设备部署场景下的遥测数据泄露。
Architecture Overview
架构概述
Client (Claude Code + env vars + Clash)
└─► CC Gateway (rewrite + auth inject + SSE passthrough)
└─► api.anthropic.com (single canonical identity)
Gateway also contacts:
platform.claude.com (OAuth token refresh only)
Three-layer defense:
| Layer | Mechanism |
|---|
| Env vars | Route CC traffic to gateway, disable side channels |
| Clash rules | Block any direct Anthropic connections at network level |
| Gateway | Rewrite all 40+ fingerprint dimensions in-flight |
Client (Claude Code + env vars + Clash)
└─► CC Gateway (rewrite + auth inject + SSE passthrough)
└─► api.anthropic.com (single canonical identity)
Gateway also contacts:
platform.claude.com (OAuth token refresh only)
三层防护机制:
| 层级 | 防护机制 |
|---|
| 环境变量 | 将Claude Code流量路由到网关,禁用侧边通道 |
| Clash规则 | 在网络层面拦截所有直接访问Anthropic的请求 |
| 网关 | 动态重写全部40+项指纹维度 |
- Node.js 18+ or Docker
- A machine that has previously logged into Claude Code (for OAuth token extraction)
- Node.js 18+ 或 Docker
- 曾登录过Claude Code的机器(用于提取OAuth令牌)
bash
git clone https://github.com/motiful/cc-gateway.git
cd cc-gateway
npm install
bash
git clone https://github.com/motiful/cc-gateway.git
cd cc-gateway
npm install
Generate Identity and Tokens
生成身份与令牌
Create a stable canonical identity (device_id, email, env profile)
创建稳定的规范身份(device_id、邮箱、环境配置)
npm run generate-identity
npm run generate-identity
Create a bearer token for a specific client machine
为指定客户端机器生成Bearer令牌
npm run generate-token my-laptop
npm run generate-token work-desktop
npm run generate-token my-laptop
npm run generate-token work-desktop
Extract OAuth Token (from a logged-in machine)
提取OAuth令牌(从已登录的机器)
macOS — copies refresh_token from Keychain
macOS — 从钥匙串复制refresh_token
bash scripts/extract-token.sh
bash scripts/extract-token.sh
bash
cp config.example.yaml config.yaml
bash
cp config.example.yaml config.yaml
identity:
device_id: "GENERATED_DEVICE_ID" # from generate-identity
email: "canonical@example.com"
platform: "darwin"
arch: "arm64"
node_version: "20.11.0"
shell: "/bin/zsh"
home: "/Users/canonical"
working_directory: "/Users/canonical/projects"
memory_gb: 16 # canonical RAM value
oauth:
refresh_token: "EXTRACTED_REFRESH_TOKEN" # from extract-token.sh
clients:
- name: my-laptop
token: "GENERATED_CLIENT_TOKEN"
- name: work-desktop
token: "ANOTHER_CLIENT_TOKEN"
server:
port: 8443
tls: false # true for production with certs
identity:
device_id: "GENERATED_DEVICE_ID" # 来自generate-identity命令输出
email: "canonical@example.com"
platform: "darwin"
arch: "arm64"
node_version: "20.11.0"
shell: "/bin/zsh"
home: "/Users/canonical"
working_directory: "/Users/canonical/projects"
memory_gb: 16 # 规范RAM值
oauth:
refresh_token: "EXTRACTED_REFRESH_TOKEN" # 来自extract-token.sh命令输出
clients:
- name: my-laptop
token: "GENERATED_CLIENT_TOKEN"
- name: work-desktop
token: "ANOTHER_CLIENT_TOKEN"
server:
port: 8443
tls: false # 生产环境带证书时设置为true
Development (no TLS, hot reload)
开发模式(无TLS、热重载)
npm run build && npm start
npm run build && npm start
Docker Compose (recommended for production)
Docker Compose(生产环境推荐)
Docker Compose Example
Docker Compose示例
docker-compose.yml
docker-compose.yml
version: "3.8"
services:
cc-gateway:
build: .
ports:
- "8443:8443"
volumes:
- ./config.yaml:/app/config.yaml:ro
restart: unless-stopped
environment:
- NODE_ENV=production
version: "3.8"
services:
cc-gateway:
build: .
ports:
- "8443:8443"
volumes:
- ./config.yaml:/app/config.yaml:ro
restart: unless-stopped
environment:
- NODE_ENV=production
Show before/after rewrite diff (requires client token)
查看重写前后的差异(需要客户端令牌)
Client Machine Setup
客户端机器配置
On each machine running Claude Code, set these environment variables:
在每台运行Claude Code的机器上,设置以下环境变量:
~/.bashrc or ~/.zshrc
~/.bashrc 或 ~/.zshrc
Route all Claude Code API traffic through the gateway
将所有Claude Code API流量路由到网关
Disable side-channel telemetry (Datadog, GrowthBook, version checks)
禁用侧边通道遥测(Datadog、GrowthBook、版本检查)
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
Skip browser OAuth — gateway handles authentication
跳过浏览器OAuth — 网关负责身份认证
export CLAUDE_CODE_OAUTH_TOKEN="gateway-managed"
export CLAUDE_CODE_OAUTH_TOKEN="gateway-managed"
Authenticate to the gateway with the per-machine token
使用单设备专属令牌向网关认证
export ANTHROPIC_CUSTOM_HEADERS="Proxy-Authorization: Bearer YOUR_CLIENT_TOKEN"
Or use the interactive setup script:
```bash
bash scripts/client-setup.sh
Then just run
— no login prompt required.
export ANTHROPIC_CUSTOM_HEADERS="Proxy-Authorization: Bearer YOUR_CLIENT_TOKEN"
或者使用交互式配置脚本:
```bash
bash scripts/client-setup.sh
Clash Rules (Network-Level Blocking)
Clash规则(网络层级拦截)
Add to your Clash configuration to block any direct Anthropic connections:
在你的Clash配置中添加以下规则,拦截所有直接访问Anthropic的请求:
clash-rules.yaml excerpt
clash-rules.yaml 片段
rules:
- DOMAIN,gateway.your-domain.com,DIRECT # Allow your gateway
- DOMAIN-SUFFIX,anthropic.com,REJECT # Block direct API calls
- DOMAIN-SUFFIX,claude.com,REJECT # Block direct OAuth
- DOMAIN-SUFFIX,claude.ai,REJECT # Block Claude web
- DOMAIN-SUFFIX,datadoghq.com,REJECT # Block Datadog telemetry
- DOMAIN-SUFFIX,statsig.com,REJECT # Block feature flags
See `clash-rules.yaml` in the repo for the full template.
rules:
- DOMAIN,gateway.your-domain.com,DIRECT # 放行你的网关地址
- DOMAIN-SUFFIX,anthropic.com,REJECT # 拦截直接API调用
- DOMAIN-SUFFIX,claude.com,REJECT # 拦截直接OAuth请求
- DOMAIN-SUFFIX,claude.ai,REJECT # 拦截Claude网页端
- DOMAIN-SUFFIX,datadoghq.com,REJECT # 拦截Datadog遥测
- DOMAIN-SUFFIX,statsig.com,REJECT # 拦截特征旗标请求
完整模板可查看仓库中的`clash-rules.yaml`文件。
What Gets Rewritten
重写内容说明
| Layer | Field | Transformation |
|---|
| Identity | | → canonical ID from config |
| Identity | | → canonical email |
| Environment | object (40+ fields) | → entire object replaced |
| Process | (physical RAM) | → canonical value |
| Process | , , | → randomized in realistic range |
| Headers | | → canonical CC version string |
| Headers | | → real OAuth token (injected) |
| Headers | x-anthropic-billing-header
| → canonical fingerprint |
| Prompt text | , , | → canonical values |
| Prompt text | , | → canonical home prefix |
| Leak fields | | → stripped |
| Leak fields | provider field | → stripped |
| 层级 | 字段 | 转换规则 |
|---|
| 身份 | | → 配置中的规范ID |
| 身份 | | → 规范邮箱 |
| 环境 | 对象(40+字段) | → 替换为完整的规范对象 |
| 进程 | (物理RAM) | → 规范值 |
| 进程 | 、、 | → 在合理范围内随机化 |
| 请求头 | | → 规范的CC版本字符串 |
| 请求头 | | → 注入真实OAuth令牌 |
| 请求头 | x-anthropic-billing-header
| → 规范指纹 |
| 提示词文本 | 、、 | → 规范值 |
| 提示词文本 | 、 | → 规范的home路径前缀 |
| 泄露字段 | | → 移除 |
| 泄露字段 | 提供者字段 | → 移除 |
TypeScript Usage Examples
TypeScript使用示例
Custom Rewriter Extension
自定义重写器扩展
typescript
// src/rewriters/custom-field-rewriter.ts
import { RequestRewriter } from '../types';
export const customFieldRewriter: RequestRewriter = {
name: 'custom-field-rewriter',
rewriteBody(body: Record<string, unknown>, config: CanonicalConfig): Record<string, unknown> {
// Strip any custom analytics fields your org adds
const { __analytics, __session_debug, ...cleaned } = body as any;
// Normalize any additional identity fields
if (cleaned.metadata?.user_id) {
cleaned.metadata.user_id = config.identity.device_id;
}
return cleaned;
},
rewriteHeaders(headers: Record<string, string>, config: CanonicalConfig): Record<string, string> {
return {
...headers,
'x-custom-client': 'canonical',
};
}
};
typescript
// src/rewriters/custom-field-rewriter.ts
import { RequestRewriter } from '../types';
export const customFieldRewriter: RequestRewriter = {
name: 'custom-field-rewriter',
rewriteBody(body: Record<string, unknown>, config: CanonicalConfig): Record<string, unknown> {
// 移除你的组织添加的自定义分析字段
const { __analytics, __session_debug, ...cleaned } = body as any;
// 标准化其他额外身份字段
if (cleaned.metadata?.user_id) {
cleaned.metadata.user_id = config.identity.device_id;
}
return cleaned;
},
rewriteHeaders(headers: Record<string, string>, config: CanonicalConfig): Record<string, string> {
return {
...headers,
'x-custom-client': 'canonical',
};
}
};
Programmatic Gateway Start
编程式启动网关
typescript
// scripts/start-with-monitoring.ts
import { createGateway } from '../src/gateway';
import { loadConfig } from '../src/config';
async function main() {
const config = await loadConfig('./config.yaml');
const gateway = await createGateway(config);
gateway.on('request', ({ clientId, path }) => {
console.log(`[${new Date().toISOString()}] ${clientId} → ${path}`);
});
gateway.on('rewrite', ({ field, before, after }) => {
console.log(`Rewrote ${field}: ${before} → ${after}`);
});
gateway.on('tokenRefresh', ({ expiresAt }) => {
console.log(`OAuth token refreshed, expires: ${expiresAt}`);
});
await gateway.listen(config.server.port);
console.log(`Gateway running on port ${config.server.port}`);
}
main().catch(console.error);
typescript
// scripts/start-with-monitoring.ts
import { createGateway } from '../src/gateway';
import { loadConfig } from '../src/config';
async function main() {
const config = await loadConfig('./config.yaml');
const gateway = await createGateway(config);
gateway.on('request', ({ clientId, path }) => {
console.log(`[${new Date().toISOString()}] ${clientId} → ${path}`);
});
gateway.on('rewrite', ({ field, before, after }) => {
console.log(`Rewrote ${field}: ${before} → ${after}`);
});
gateway.on('tokenRefresh', ({ expiresAt }) => {
console.log(`OAuth token refreshed, expires: ${expiresAt}`);
});
await gateway.listen(config.server.port);
console.log(`Gateway running on port ${config.server.port}`);
}
main().catch(console.error);
Token Generation (Programmatic)
编程式生成令牌
typescript
// scripts/provision-client.ts
import { generateClientToken, addClientToConfig } from '../src/auth';
async function provisionNewMachine(machineName: string) {
const token = await generateClientToken(machineName);
await addClientToConfig('./config.yaml', {
name: machineName,
token,
created_at: new Date().toISOString(),
});
console.log(`Client token for ${machineName}:`);
console.log(token);
console.log('\nAdd to client machine:');
console.log(`export ANTHROPIC_CUSTOM_HEADERS="Proxy-Authorization: Bearer ${token}"`);
}
provisionNewMachine(process.argv[2] ?? 'new-machine');
typescript
// scripts/provision-client.ts
import { generateClientToken, addClientToConfig } from '../src/auth';
async function provisionNewMachine(machineName: string) {
const token = await generateClientToken(machineName);
await addClientToConfig('./config.yaml', {
name: machineName,
token,
created_at: new Date().toISOString(),
});
console.log(`Client token for ${machineName}:`);
console.log(token);
console.log('\nAdd to client machine:');
console.log(`export ANTHROPIC_CUSTOM_HEADERS="Proxy-Authorization: Bearer ${token}"`);
}
provisionNewMachine(process.argv[2] ?? 'new-machine');
| Command | Purpose |
|---|
| Start with hot reload (development) |
| Compile TypeScript to |
| Run compiled production build |
| Run rewriter test suite (13 tests) |
npm run generate-identity
| Create canonical device profile |
npm run generate-token <name>
| Create per-client bearer token |
| 命令 | 用途 |
|---|
| 热重载启动(开发环境) |
| 将TypeScript编译到目录 |
| 运行编译后的生产构建版本 |
| 运行重写器测试套件(13项测试) |
npm run generate-identity
| 创建规范设备配置文件 |
npm run generate-token <name>
| 生成单客户端Bearer令牌 |
Multiple Machines, One Identity
多设备共享同一身份
On gateway server — generate once
在网关服务器上 — 仅需执行一次
npm run generate-identity
npm run generate-identity
→ device_id: abc-123, email: canonical@proxy.local
→ device_id: abc-123, email: canonical@proxy.local
Provision each machine
为每台机器分配令牌
npm run generate-token laptop-home # → token-aaa
npm run generate-token laptop-work # → token-bbb
npm run generate-token desktop # → token-ccc
npm run generate-token laptop-home # → token-aaa
npm run generate-token laptop-work # → token-bbb
npm run generate-token desktop # → token-ccc
All three machines present as the same device to Anthropic
三台机器对Anthropic都表现为同一设备
Rotating the Canonical Identity
轮换规范身份
Generate a new identity (e.g., after a suspected flag)
生成新身份(例如怀疑账号被标记时)
npm run generate-identity --force
npm run generate-identity --force
Update config.yaml with new device_id
用新的device_id更新config.yaml
Restart gateway — all clients immediately use new identity
重启网关 — 所有客户端立即使用新身份
docker-compose restart cc-gateway
docker-compose restart cc-gateway
Checking for New Telemetry Fields After CC Updates
CC更新后检查新遥测字段
After a Claude Code update, use _verify to diff
Claude Code更新后,使用_verify接口查看差异
Monitor Clash logs for new endpoints
监控Clash日志排查新端点
Any REJECT hits on new domains = new hardcoded endpoints
任何新域名的REJECT命中 = 新的硬编码端点
still prompts for browser login
- Ensure
CLAUDE_CODE_OAUTH_TOKEN=gateway-managed
is exported
- Verify points to your running gateway
- Check gateway logs:
docker-compose logs -f cc-gateway
- 确保已导出
CLAUDE_CODE_OAUTH_TOKEN=gateway-managed
- 验证指向你运行的网关地址
- 检查网关日志:
docker-compose logs -f cc-gateway
401 Unauthorized from gateway
网关返回401未授权
- Confirm contains
Proxy-Authorization: Bearer <token>
- Verify the token in config.yaml matches the one set in env var
- Run
curl -H "Authorization: Bearer $TOKEN" http://localhost:8443/_health
- 确认包含
Proxy-Authorization: Bearer <token>
- 验证config.yaml中的令牌与环境变量中设置的一致
- 运行
curl -H "Authorization: Bearer $TOKEN" http://localhost:8443/_health
排查
OAuth token expired
OAuth令牌过期
Re-extract from a logged-in machine
从已登录的机器重新提取
bash scripts/extract-token.sh
bash scripts/extract-token.sh
Paste new refresh_token into config.yaml
将新的refresh_token粘贴到config.yaml
docker-compose restart cc-gateway
docker-compose restart cc-gateway
MCP servers bypassing gateway
MCP服务器绕过网关
MCP uses
which ignores
. Add to Clash:
yaml
- DOMAIN,mcp-proxy.anthropic.com,REJECT
MCP使用
,该地址会忽略
。在Clash中添加规则:
yaml
- DOMAIN,mcp-proxy.anthropic.com,REJECT
Requests reaching Anthropic directly (Clash not blocking)
请求直接到达Anthropic(Clash未拦截)
- Check Clash is running:
- Verify rules are loaded: look for REJECT entries in Clash dashboard
- Test:
curl https://api.anthropic.com
— should fail if Clash is active
- 检查Clash是否运行:
- 验证规则已加载:在Clash控制台中查看REJECT条目
- 测试:
curl https://api.anthropic.com
— 如果Clash正常运行应该请求失败
Gateway rewrite not applying to a new field
新字段未应用网关重写
After a Claude Code update, new telemetry fields may not be covered. Check
for
, then open an issue or add a custom rewriter (see Custom Rewriter Extension above).
Claude Code更新后,可能出现未覆盖的新遥测字段。检查
接口的
,然后提交Issue或者添加自定义重写器(参考上方自定义重写器扩展部分)。
- MCP servers — hardcoded endpoint, use Clash to block if not needed
- CC updates — monitor Clash REJECT logs after every Claude Code update for new endpoints
- Refresh token lifetime — if the OAuth refresh token expires, re-run
- ToS — do not use for account sharing; intended for managing your own devices under one subscription
- Alpha — test with a non-primary account before production use
- MCP服务器 — 端点为硬编码,不需要的话可以用Clash拦截
- CC更新 — 每次Claude Code更新后都要监控Clash的REJECT日志,排查新端点
- 刷新令牌生命周期 — 如果OAuth刷新令牌过期,重新运行即可
- 服务条款 — 请勿用于账号共享,仅适用于同一订阅下管理你自己的多台设备
- Alpha版本 — 生产使用前请先在非主账号上测试