Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ on:
workflow_dispatch:

permissions:
contents: write
packages: write
contents: read

concurrency:
group: build-${{ github.ref }}
Expand All @@ -23,6 +22,22 @@ env:
IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/claude2api

jobs:
test:
name: Test and vet
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Test with race detector
run: go test -race ./...
- name: Vet
run: go vet ./...

build-binaries:
name: Build ${{ matrix.goos }}/${{ matrix.goarch }}
runs-on: ubuntu-latest
Expand Down Expand Up @@ -63,7 +78,9 @@ jobs:
CGO_ENABLED: '0'
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
run: go build -trimpath -ldflags='-s -w' -o dist/${{ matrix.artifact }} .
run: |
mkdir -p dist
go build -trimpath -ldflags='-s -w' -o dist/${{ matrix.artifact }} .

- name: Upload artifact
uses: actions/upload-artifact@v4
Expand All @@ -77,6 +94,8 @@ jobs:
needs: build-binaries
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
Expand All @@ -97,6 +116,9 @@ jobs:
docker:
name: Build and push Docker image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
.env.*
captured/
.claude/
tenants*.json
!tenants.example.json

# Binaries
*.exe
Expand Down
50 changes: 15 additions & 35 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,13 @@
http://127.0.0.1:8080/v1
```

所有 `/v1` 下的接口都需要认证。可以使用以下任意一种方式
所有 `/v1` 下的接口都使用独立代理 API Key 认证

```http
Authorization: Bearer <claude.ai sessionKey>
Authorization: Bearer <proxy-api-key>
```

或:

```http
X-Claude-Cookie: <完整 claude.ai 浏览器 Cookie>
```

如果服务端已经配置 `CLAUDE_SESSION_KEY` 或 `CLAUDE_COOKIE`,请求端可以省略对应 Header。
Bearer 不再接受 Claude sessionKey。上游 sessionKey/Cookie 只能由服务端通过单租户环境变量或 `TENANTS_FILE` 配置;客户端提交浏览器凭据 Header 会被拒绝。

## 错误格式

Expand All @@ -30,7 +24,8 @@ X-Claude-Cookie: <完整 claude.ai 浏览器 Cookie>
{
"error": {
"message": "错误信息",
"type": "invalid_request_error"
"type": "invalid_request_error",
"request_id": "non-secret-request-id"
}
}
```
Expand All @@ -40,8 +35,10 @@ X-Claude-Cookie: <完整 claude.ai 浏览器 Cookie>
| 状态码 | 说明 |
| --- | --- |
| `400` | 请求体无效或模型不支持。 |
| `401` | 缺少 sessionKey 或浏览器 Cookie。 |
| `401` | 缺少代理 API Key 或 key 无效。 |
| `429` | 当前租户超过速率限制。 |
| `502` | 上游 claude.ai 请求失败。 |
| `503` | 网关认证或租户上游凭据未配置。 |

## 模型列表

Expand All @@ -51,7 +48,7 @@ X-Claude-Cookie: <完整 claude.ai 浏览器 Cookie>

```bash
curl http://127.0.0.1:8080/v1/models \
-H 'Authorization: Bearer <sessionKey>'
-H 'Authorization: Bearer <proxy-api-key>'
```

响应示例:
Expand Down Expand Up @@ -122,7 +119,7 @@ OpenAI Chat Completions 兼容接口。

```bash
curl http://127.0.0.1:8080/v1/chat/completions \
-H 'Authorization: Bearer <sessionKey>' \
-H 'Authorization: Bearer <proxy-api-key>' \
-H 'Content-Type: application/json' \
-d '{
"model": "claude-sonnet-5",
Expand Down Expand Up @@ -161,7 +158,7 @@ curl http://127.0.0.1:8080/v1/chat/completions \

```bash
curl -N http://127.0.0.1:8080/v1/chat/completions \
-H 'Authorization: Bearer <sessionKey>' \
-H 'Authorization: Bearer <proxy-api-key>' \
-H 'Content-Type: application/json' \
-d '{
"model": "claude-sonnet-5",
Expand Down Expand Up @@ -396,7 +393,7 @@ data: {"type":"response.completed","response":{"status":"completed"}}

```bash
curl -X DELETE http://127.0.0.1:8080/v1/conversations/my-chat-001 \
-H 'Authorization: Bearer <sessionKey>'
-H 'Authorization: Bearer <proxy-api-key>'
```

响应:
Expand All @@ -407,26 +404,9 @@ curl -X DELETE http://127.0.0.1:8080/v1/conversations/my-chat-001 \

注意:当前会话映射保存在内存中,服务重启后会丢失。

## 完整浏览器 Cookie 用法

如果 Bearer sessionKey 模式和浏览器行为不一致,或遇到浏览器正常但本地请求异常的情况,建议传完整 claude.ai 浏览器 Cookie:

```bash
curl http://127.0.0.1:8080/v1/chat/completions \
-H 'X-Claude-Cookie: sessionKey=...; sessionKeyLC=...; anthropic-device-id=...; lastActiveOrg=...; ...' \
-H 'Content-Type: application/json' \
-d '{
"model": "claude-sonnet-5",
"messages": [{"role": "user", "content": "Reply with exactly: pong"}],
"stream": false
}'
```

没有 Bearer token 时,代理会尝试从 Cookie 中提取 `sessionKey`。

## sessionKey 模式下自动生成的环境
## 服务端浏览器凭据

当只传 `sessionKey` 时,代理服务端会自动生成前端可生成的浏览器环境,并补齐更接近浏览器的请求结构,例如:
客户端不能提交 Claude sessionKey 或完整 Cookie。服务端可以为每个租户配置 sessionKey;代理会在该租户的上游 client 生命周期内生成前端环境,例如:

- `sessionKeyLC`
- `anthropic-device-id`
Expand All @@ -450,7 +430,7 @@ curl http://127.0.0.1:8080/v1/chat/completions \

`routingHint` 是 claude.ai 后端签发的路由提示,值通常是 `sk-ant-rh-...` 形式,内部结构类似带签名的 JWT。它一般在登录、会话刷新、账号路由初始化或组织信息加载时由 claude.ai 服务端下发;客户端最多只能原样保存和回传,不能自行生成有效值。

如果确实需要这些值,请使用 `X-Claude-Cookie` 或 `CLAUDE_COOKIE` 传入真实浏览器完整 Cookie
如果确实需要这些值,请仅在服务端的私密 `TENANTS_FILE` 中为对应租户配置 `claude_cookie`。完整安全配置见 [SECURITY.md](SECURITY.md)

## 不支持的接口

Expand Down
50 changes: 15 additions & 35 deletions API_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@ Base URL:
http://127.0.0.1:8080/v1
```

All endpoints under `/v1` require authentication. Use either:
All endpoints under `/v1` require an independent proxy API key:

```http
Authorization: Bearer <claude.ai sessionKey>
Authorization: Bearer <proxy-api-key>
```

or:

```http
X-Claude-Cookie: <full claude.ai browser Cookie header>
```

If `CLAUDE_SESSION_KEY` or `CLAUDE_COOKIE` is configured on the server, callers may omit the matching request header.
The Bearer value is never a Claude session key. Upstream browser credentials are configured only on the server through single-tenant environment variables or `TENANTS_FILE`. Client browser-credential headers are rejected.

## Errors

Expand All @@ -28,7 +22,8 @@ Errors use an OpenAI-like JSON shape:
{
"error": {
"message": "error message",
"type": "invalid_request_error"
"type": "invalid_request_error",
"request_id": "non-secret-request-id"
}
}
```
Expand All @@ -38,8 +33,10 @@ Common status codes:
| Status | Meaning |
| --- | --- |
| `400` | Invalid request body or unsupported model. |
| `401` | Missing session key or browser Cookie. |
| `401` | Missing or invalid proxy API key. |
| `429` | The authenticated tenant exceeded its rate limit. |
| `502` | Upstream claude.ai request failed. |
| `503` | Gateway authentication or the tenant upstream credential is not configured. |

## Models

Expand All @@ -49,7 +46,7 @@ Returns the supported model list.

```bash
curl http://127.0.0.1:8080/v1/models \
-H 'Authorization: Bearer <sessionKey>'
-H 'Authorization: Bearer <proxy-api-key>'
```

Response:
Expand Down Expand Up @@ -120,7 +117,7 @@ Messages are flattened to a claude.ai prompt using `[System]`, `[Human]`, and `[

```bash
curl http://127.0.0.1:8080/v1/chat/completions \
-H 'Authorization: Bearer <sessionKey>' \
-H 'Authorization: Bearer <proxy-api-key>' \
-H 'Content-Type: application/json' \
-d '{
"model": "claude-sonnet-5",
Expand Down Expand Up @@ -159,7 +156,7 @@ Response:

```bash
curl -N http://127.0.0.1:8080/v1/chat/completions \
-H 'Authorization: Bearer <sessionKey>' \
-H 'Authorization: Bearer <proxy-api-key>' \
-H 'Content-Type: application/json' \
-d '{
"model": "claude-sonnet-5",
Expand Down Expand Up @@ -237,7 +234,7 @@ Delete a persistent conversation:

```bash
curl -X DELETE http://127.0.0.1:8080/v1/conversations/my-chat-001 \
-H 'Authorization: Bearer <sessionKey>'
-H 'Authorization: Bearer <proxy-api-key>'
```

Response:
Expand All @@ -248,26 +245,9 @@ Response:

The conversation mapping is stored in memory and is lost after server restart.

## Full Browser Cookie Usage

When the simple Bearer session key gets rate-limited or behaves differently from the browser, pass the full claude.ai browser Cookie:

```bash
curl http://127.0.0.1:8080/v1/chat/completions \
-H 'X-Claude-Cookie: sessionKey=...; sessionKeyLC=...; anthropic-device-id=...; lastActiveOrg=...; ...' \
-H 'Content-Type: application/json' \
-d '{
"model": "claude-sonnet-5",
"messages": [{"role": "user", "content": "Reply with exactly: pong"}],
"stream": false
}'
```

The proxy extracts `sessionKey` from the Cookie when no Bearer token is present.

## Generated Environment in sessionKey Mode
## Server-side browser credentials

When only a `sessionKey` is provided, the server generates browser-like values and request structure that are normally frontend-generated, such as:
Clients cannot submit Claude session keys or full Cookies. When a tenant uses a server-side session key, the service generates browser-like values and request structure within that tenant's upstream client lifecycle, such as:

- `sessionKeyLC`
- `anthropic-device-id`
Expand All @@ -289,7 +269,7 @@ Signed server-side or Cloudflare-issued cookies are not forged and are not sent

`routingHint` is issued by the claude.ai backend. It commonly appears as `sk-ant-rh-...` and its internal shape is similar to a signed JWT. It is usually created during login, session refresh, account routing initialization, or organization loading. Clients can only store and replay a real value; they cannot generate a valid one locally.

Use `X-Claude-Cookie` or `CLAUDE_COOKIE` with a real browser Cookie header if you need those values.
If these values are required, configure `claude_cookie` only in the tenant's protected server-side `TENANTS_FILE`. See [SECURITY.md](SECURITY.md).

## Unsupported Endpoints

Expand Down
7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,22 @@ RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/claude2

FROM alpine:3.22

RUN apk add --no-cache ca-certificates tzdata
RUN apk add --no-cache ca-certificates tzdata \
&& addgroup -S claude2api \
&& adduser -S -G claude2api -H -s /sbin/nologin claude2api

WORKDIR /app
COPY --from=builder /out/claude2api /app/claude2api

ENV PORT=8080 \
BIND_ADDR=0.0.0.0 \
CLAUDE_BASE_URL=https://claude.ai \
CLAUDE_TIMEZONE=Asia/Singapore \
CLAUDE_LOCALE=en-US \
DEFAULT_MODEL=claude-sonnet-5

EXPOSE 8080

USER claude2api

ENTRYPOINT ["/app/claude2api"]
Loading