# GPTFREE 注册机 CDK 对接方案

> 当前统一契约为 v1.5.0。新接入必须以 `registration/login` 获取不透明项目会话，再用会话调用注册租约；本文中的 `activate`、`authorize`、`rebind` 仅为 v1.4 兼容路径。完整最新版：`/api/v1/integration-guide/v1.5.0`。

基础地址由部署环境提供。管理台中的“项目对接”入口可打开本文件对应的在线接口说明；首次接入先读取 `/api/v1/projects/registration_core/capabilities`，完整持久账号与换卡契约见 `/api/v1/registration-integration-guide`。

## 边界

本地注册机负责：

- 首次输入和保存本地用户名、密码；
- 保存本地工作区和账号运行状态；
- 生成稳定的 `client_user_ref`；
- 调用授权接口并在本地缓存短期授权结果。

云端 CDK 管理库负责：

- GPTFREE 注册机 CDK 的开卡、首次启用、有效期、禁用、轮换和续期；
- 用户绑定关系和最近心跳元数据；
- 客户端签名密钥的哈希保存、撤销和轮换；
- 管理员审计。

账号与 CDK 分离：账号身份由 `project_key + username + client_user_ref` 固定，CDK 到期只会使当前绑定失效，不会删除账号。到期后先调用 `POST /api/v1/projects/registration_core/registration/account/status`，新卡签发后调用 `POST /api/v1/projects/registration_core/registration/rebind`。账号配置使用 `/registration/accounts/{client_user_ref}/config` 的版本接口保存，换卡不丢失。

本地密码、工作区文件、会话内容和客户端密钥明文不提交到云端数据库。服务端响应只返回脱敏卡号、指纹和短期签名授权。

## 第一次配置

1. 管理员打开 CDK 管理台的“GPTFREE 注册机”。
2. 创建“本地客户端密钥”，明文只出现一次。
3. 本地注册机只在本地安全配置中保存该密钥；不要提交到 Git、日志、截图或普通配置导出。
4. 管理员为“GPTFREE 注册机”项目开普通 CDK。CDK 明文也只出现一次。

项目服务启动或用户输入 CDK 后，先用项目客户端密钥调用：

```text
POST /api/v1/projects/registration_core/registration/client-preflight
Authorization: Bearer <项目 CDK>
X-Registration-Client-Key: <registration_core 的项目客户端密钥>
X-Registration-Timestamp: <Unix seconds>
X-Registration-Signature: <HMAC-SHA256 hex>
Content-Type: application/json

{}
```

预检只校验密钥和 CDK，不启动时效、不建绑定、不扣额度。平台总管理员 CDK 也必须配合 `registration_core` 自己的项目客户端密钥；不能把平台 CDK 当成项目客户端密钥。预检失败时必须显示准确的密钥/权限错误并停止登录，不能回退到本地未授权模式。

普通 GPTFREE 注册机 CDK 只能用于 `registration_core`。平台总管理员 CDK，或管理员明确创建且 `registration_scope=global` 的管理员 CDK，可以在所有已启用的注册机项目使用；它们仍不能调用开卡、续期、账本、审计、队列和项目管理接口。

查询 `GET /api/v1/cdk/status?project_key=registration_core` 时，平台总管理员卡的 `cdk.project_key` 和 `cdk.effective_project_key` 均为 `registration_core`，原始签发项目放在 `cdk.card_project_key`。项目客户端只能依据 `cdk.project_access_allowed` 或项目契约的 `authentication.project_access.allowed` 判断权限，不得依据 CDK 前缀、长度或 `card_project_key` 拒绝授权。

## 请求签名

每次 GPTFREE 注册机请求都需要以下请求头：

```text
X-Registration-Client-Key: <一次性结果中的客户端密钥>
X-Registration-Timestamp: <Unix seconds>
X-Registration-Signature: <HMAC-SHA256 hex>
```

签名原文为：

```text
timestamp.METHOD.path.sha256(canonical_json_body)
```

其中 `canonical_json_body` 使用 UTF-8、JSON 对象键按字典序排列、无多余空白；空请求体按 `{}` 计算。HMAC 密钥是客户端密钥，服务端只接受时间偏差不超过 5 分钟的请求。

## 激活绑定

```bash
curl -sS -X POST "$BASE_URL/api/v1/projects/registration_core/registration/activate" \
  -H "Content-Type: application/json" \
  -H "X-Registration-Client-Key: <CLIENT_KEY>" \
  -H "X-Registration-Timestamp: <TIMESTAMP>" \
  -H "X-Registration-Signature: <SIGNATURE>" \
  -d '{
    "cdk": "<REGISTRATION_CDK>",
    "username": "local-user",
    "client_user_ref": "local-user-001",
    "device_summary": "optional-local-device-label"
  }'
```

`username` 只是本地用户名标识，不是密码。首次成功激活后，普通时长 CDK 的有效期才开始计算。相同 `project_key + cdk_id + username + client_user_ref` 重复请求会返回同一绑定，不会重复创建。全局管理员 CDK 可在另一个注册机项目建立另一条独立绑定，但不能让项目级客户端密钥跨项目使用。

成功响应重点字段：

```json
{
  "ok": true,
  "created": true,
  "binding": {
    "binding_id": "regbind_xxx",
    "cdk_id": 12,
    "cdk_masked": "regcdk_l...abc123",
    "cdk_role": "customer_cdk",
    "registration_scope": "project",
    "is_platform_master": false,
    "project_key": "registration_core",
    "username": "local-user",
    "status": "active",
    "expires_at": "2026-09-01T00:00:00Z"
  },
  "authorization": {
    "signed_authorization": "<OPAQUE_SHORT_LIVED_VALUE>",
    "cache_expires_in_seconds": 900
  }
}
```

`signed_authorization` 是本地运行授权，不是本地用户密码。按 `cache_expires_in_seconds` 缓存，过期后调用授权刷新接口。

## 授权刷新和工作区心跳

刷新授权：

```bash
curl -sS -X POST "$BASE_URL/api/v1/projects/registration_core/registration/authorize" \
  -H "Content-Type: application/json" \
  -H "X-Registration-Client-Key: <CLIENT_KEY>" \
  -H "X-Registration-Timestamp: <TIMESTAMP>" \
  -H "X-Registration-Signature: <SIGNATURE>" \
  -d '{"binding_id":"regbind_xxx","client_user_ref":"local-user-001"}'
```

更新心跳元数据：

```bash
curl -sS -X POST "$BASE_URL/api/v1/projects/registration_core/registration/workspace" \
  -H "Content-Type: application/json" \
  -H "X-Registration-Client-Key: <CLIENT_KEY>" \
  -H "X-Registration-Timestamp: <TIMESTAMP>" \
  -H "X-Registration-Signature: <SIGNATURE>" \
  -d '{
    "binding_id":"regbind_xxx",
    "client_user_ref":"local-user-001",
    "workspace_updated_at":"2026-08-15T00:00:00Z",
    "device_summary":"optional-local-device-label"
  }'
```

心跳接口不接受工作区内容，不接受密码，不接受 Cookie 或会话数据。

## 续期换卡

管理员调用：

```bash
curl -sS -X POST "$BASE_URL/api/v1/admin/registration-core/cdks/12/renew" \
  -H "Authorization: Bearer <SUPER_ADMIN_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"validity_seconds":2592000,"validity_label":"1个月"}'
```

新 CDK 明文只在此次响应中返回。新卡激活后，旧绑定变为 `replaced`，旧授权立即不可用；原账号本地工作区不删除。

## 状态处理

绑定状态包括：

```text
active | expiring | expired | disabled | revoked | replaced
```

本地处理规则：

- `active`、`expiring`：允许本地继续工作；
- `expired`：提示管理员续期；
- `disabled`、`revoked`：停止授权请求；
- `replaced`：切换到管理员发放的新 CDK，保留原工作区；
- `401`：检查客户端密钥、时间戳和 HMAC；
- `403`：检查 CDK 状态、用户名绑定、项目状态和 `registration_scope`；
- `409`：不要覆盖本地用户，先读取管理台绑定关系。

全局范围相关错误码：

```text
registration_project_access_denied
registration_scope_denied
registration_client_key_scope_denied
registration_cdk_role_unsupported
```

## 管理员接口

```text
GET  /api/v1/admin/registration-core/overview
GET  /api/v1/admin/registration-core/users?q=&status=&limit=&offset=
POST /api/v1/admin/registration-core/cdks/{id}/renew
GET  /api/v1/admin/registration-core/client-keys
POST /api/v1/admin/registration-core/client-keys
POST /api/v1/admin/registration-core/client-keys/{id}/rotate
```

这些接口必须使用独立的 `super_admin` 管理员认证。普通任务 CDK、管理员任务 CDK 和注册机客户端密钥都不能调用开卡、续期、轮换或审计接口。

完整字段、错误结构和现有任务接口见：

- `/api/v1/openapi.yaml`
- `/api/v1/integration-guide`
- `/api/v1/errors`
