docs(integration): complete + correct the desktop client API doc
Review of the client doc against the real code found and fixed: - §1 auth was not self-contained (deferred the canonical to the deprecated doc). Inlined the full signing contract verified against middleware/device_signature.go: the exact header set, the fixed-order canonical string (method/path/ts/nonce/ fingerprint/eph_pubkey/sha256(body)), ed25519(sha256(canonical)), the heicode-aead-v1 encrypted-body rules, and the X-Heicode-Auth-Error / X-Heicode-Server-Time failure headers. - §2 auth mismatch (accuracy bug): /api/user/self is UserAuth (session/JWT), NOT device-signed — a device-only client cannot call it. Marked it optional and clarified the two different auth schemes (/api/user/self* vs /api/heicode/*). - §8: documented that failures return HTTP 200 with success:false (client MUST read success), and that error.retryable is always false (decide retry by code). - §10 inventory: corrected /api/user/self auth + added /self/models. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -29,33 +29,60 @@
|
||||
|
||||
## 1. 认证与加密 🟢
|
||||
|
||||
与模型调用(`/v1/*`)完全一致,复用同一套 `encryptedFetch` / V2 设备签名,无需新增协议:
|
||||
桌面客户端用**设备绑定密钥**对每个请求做 Ed25519 签名;有 body 的写请求额外用 X25519-ECDH + HKDF + ChaCha20-Poly1305 加密 body。**与调模型 `/v1/*` 用的是同一套**(`cc-haha/src/services/device/signRequest.ts`),无需新增协议。本节自包含,照此即可实现。
|
||||
|
||||
| 方式 | 适用 |
|
||||
**每个请求都带的头:**
|
||||
|
||||
| 头 | 含义 |
|
||||
|---|---|
|
||||
| **V2 加密 body + 设备签名**(`Content-Encoding: heicode-aead-v1` + `X-Heicode-*`,ChaCha20-Poly1305 + Ed25519) | 有 body 的写请求(POST/DELETE) |
|
||||
| **V2 无 body 设备签名**(`X-Heicode-*` 签名头,不带 `Content-Encoding`) | GET 等无 body 请求 |
|
||||
| Manager 会话 cookie + `New-Api-User: <user_id>` | 网页控制台路径(桌面端可不用) |
|
||||
| `X-Heicode-Device-Id` | 设备 id(配对时获得) |
|
||||
| `X-Heicode-Timestamp` | unix 毫秒(服务端校验时钟偏移窗口) |
|
||||
| `X-Heicode-Nonce` | 每次随机,防重放 |
|
||||
| `X-Heicode-Fingerprint` | 设备指纹 |
|
||||
| `X-Heicode-Eph-Pubkey` | 本次临时 X25519 公钥(base64),**每请求新生成** |
|
||||
| `X-Heicode-Signature` | 下面 canonical 的签名(base64) |
|
||||
| `Content-Encoding: heicode-aead-v1` | **仅有加密 body 的写请求带**;GET 不带 |
|
||||
|
||||
- canonical 里的 `path_with_query` 用 HM 实际收到的 path(不含 origin);每次新 nonce + 新临时 X25519 key。
|
||||
**签名 canonical(字节序固定,服务端按此重建,顺序不可改):**
|
||||
|
||||
```text
|
||||
canonical = method + "\n" # "GET" / "POST" / "DELETE"
|
||||
+ path_with_query + "\n" # 服务端实际收到的 path(含 query,不含 origin)
|
||||
+ timestamp_ms + "\n"
|
||||
+ nonce + "\n"
|
||||
+ fingerprint + "\n"
|
||||
+ eph_pubkey_b64 + "\n"
|
||||
+ hex( sha256(body明文) ) # 无 body(GET) 填 hex(sha256("")) = e3b0c442…b855
|
||||
signature = base64( ed25519_sign( device_priv, sha256(canonical) ) )
|
||||
```
|
||||
|
||||
- **GET / 无 body**:不发 `Content-Encoding`、body 不加密,canonical 末项填空串哈希。
|
||||
- **POST / DELETE 有 body**:发 `Content-Encoding: heicode-aead-v1`;body = 用 `eph_priv × 服务器公钥` 做 X25519-ECDH → HKDF → ChaCha20-Poly1305 加密;canonical 末项是**加密前明文 body** 的 sha256(hex)。
|
||||
- 服务端中间件 `UserOrV2DeviceAuth` 解密 + 验签,从设备绑定 token 解析用户身份。
|
||||
- GET 无 body:不发 `Content-Encoding`、不加密,body 哈希填 `sha256("")`。
|
||||
|
||||
> 细节同旧文档 §1(该机制未变,只有上面这层加密链路保留)。
|
||||
**鉴权失败时的响应头**(客户端据此给用户准确提示,而非笼统"token 失效"):
|
||||
- `X-Heicode-Auth-Error`:`timestamp_drift`(时钟偏移)/ `nonce_replay` / `revoked` / `fingerprint_mismatch` / `signature_invalid` / `device_not_found` / `eph_pubkey_missing` …
|
||||
- `X-Heicode-Server-Time`:服务器 unix 毫秒(用来校正本地时钟,解决 `timestamp_drift`)。
|
||||
|
||||
> 设备如何拿到 `device_id` + 设备密钥:走与模型调用相同的**设备配对/登录流程**(`cc-haha/src/services/device`、`/api/heicode-auth/*`),本文不重复。
|
||||
|
||||
---
|
||||
|
||||
## 2. 账户与余额 🟢
|
||||
## 2. 账户与余额 🟢(可选;用**用户会话/JWT**,非设备签名)
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|---|---|---|
|
||||
| GET | `/api/user/self` | 当前用户:`quota`(剩余)、`used_quota`(累计已用)、`request_count`、分组 |
|
||||
| 方法 | 路径 | 鉴权 | 说明 |
|
||||
|---|---|---|---|
|
||||
| GET | `/api/user/self` | **`UserAuth`(会话 cookie / 用户 JWT)** | 当前用户:`quota`(剩余)、`used_quota`(累计已用)、`request_count`、分组 |
|
||||
| GET | `/api/user/self/models` | 同上 | 当前用户可用模型 |
|
||||
|
||||
```json
|
||||
// data 摘要
|
||||
{ "id":22, "username":"chenchen", "quota":..., "used_quota":..., "request_count":... }
|
||||
```
|
||||
|
||||
> ⚠️ **注意鉴权不同**:`/api/user/self*` 走 `UserAuth`,**不接受设备签名**——客户端要用登录拿到的**用户会话/JWT**(`Authorization: Bearer <jwt>` 或会话 cookie)调用。`/api/heicode/*`(§3–§5)走 `UserOrV2DeviceAuth`,设备签名即可。
|
||||
> 余额仅用于展示用量,**非客户端核心流程**(部署在网页台);拿不到也不影响"列 agent → 直连用"。
|
||||
|
||||
---
|
||||
|
||||
## 3. 能力发现 🟢
|
||||
@@ -168,13 +195,17 @@
|
||||
## 8. 响应 envelope 与错误码
|
||||
|
||||
**成功**:`{ "success": true, "data": {…} }`
|
||||
**失败**:`{ "success": false, "error": { "code": "…", "message": "…" } }`
|
||||
**失败**:`{ "success": false, "message":"…", "error": { "code":"…", "message":"…", "retryable": false, "request_id":"…" } }`
|
||||
|
||||
| code | 场景 | 说明 |
|
||||
> ⚠️ 两个易踩的点:
|
||||
> - **失败也是 HTTP 200**(`success:false`)——客户端**必须读 `success` 字段判成败**,不能只看 HTTP 状态码(否则失败会被当成功)。
|
||||
> - `error.retryable` **当前一律为 `false`,不要据它判重试**;按下表的 `code` 判。`request_id` 便于排查。
|
||||
|
||||
| code | 场景 | 处理 |
|
||||
|---|---|---|
|
||||
| `POLICY_REJECTED` | 参数非法 / 未认证 / 未知 `template_id` | 不可重试,改参数 |
|
||||
| `POLICY_REJECTED` | 参数非法 / 未认证 / 未知 `template_id` | 改参数,不重试 |
|
||||
| `RESOURCE_BINDING_INVALID` | `binding_ids` 里有不存在/非本人的绑定 | 检查资源 |
|
||||
| `RUNTIME_UNAVAILABLE` | 调 AM 失败(含 AM 接口未就绪) | AM 侧问题,可稍后重试 |
|
||||
| `RUNTIME_UNAVAILABLE` | 调 AM 失败(含 AM 接口未就绪) | AM 侧问题,**可稍后重试** |
|
||||
| `DEPLOYMENT_CONFLICT` | agent 不存在 | — |
|
||||
| `DEPLOYMENT_PERSIST_FAILED` | HM 落库失败 | — |
|
||||
|
||||
@@ -203,8 +234,8 @@
|
||||
|
||||
| 接口 | 鉴权 | 状态 |
|
||||
|---|---|---|
|
||||
| `GET /api/heicode/capabilities` | 无 | 🟢 |
|
||||
| `GET /api/user/self` | 会话/设备 | 🟢 |
|
||||
| `GET /api/heicode/capabilities` | 无(公开) | 🟢 |
|
||||
| `GET /api/user/self`、`/self/models` | **UserAuth(会话/JWT)** | 🟢 |
|
||||
| `GET /api/heicode/agent-templates` | 会话/设备 | 🟢(生产已验证 19 中文模板) |
|
||||
| `GET /api/heicode/agents` `/{id}` `/{id}/status` | 会话/设备 | 🟢 |
|
||||
| `POST /api/heicode/agents` | 会话/设备 | 🟢(调 AM,AM 接口 🔴) |
|
||||
|
||||
Reference in New Issue
Block a user