# API 总体约定 ## 1. API 分面 | 分面 | 基地址 | 使用者 | 稳定性 | | --- | --- | --- | --- | | Native API | `https://api./v1` | Web、Tauri、第三方应用 | 公开稳定 | | OpenAI-compatible | `https://gateway./openai/v1` | OpenAI SDK/兼容客户端 | 对齐所声明子集 | | Anthropic-compatible | `https://gateway./anthropic/v1` | Anthropic SDK/兼容客户端 | 对齐所声明子集 | | Provider Webhook | `https://api./webhooks/v1` | 支付、媒体等 Provider | Provider 签名契约 | | Internal Port | 进程内接口或私有网络 | Core、Worker、Sandbox、Plugin Runtime | 不公开 | Native、兼容和内部接口不能共享权限推断。兼容入口只提供模型推理,不直接读写角色、记忆、社区、设备和交易资源。 ## 2. 认证 ### 2.1 1.0 认证组合 | 客户端 | 凭据 | 传输位置 | | --- | --- | --- | | Web | OAuth 2.1 登录后的 `__Host-hunjing_session` | `Cookie` 请求头 | | Tauri Desktop/Mobile | Authorization Code + PKCE 获得的短期 access token | `Authorization: Bearer` | | 第三方用户应用 | OAuth 2.1 access token | `Authorization: Bearer` | | Service Account | 可轮换、限 scope 的 opaque API key | `Authorization: Bearer` | | OpenAI-compatible | Service account/API key | `Authorization: Bearer` | | Anthropic-compatible | Service account/API key | `x-api-key`;OAuth 客户端可用 `Authorization: Bearer` | 所有凭据只能通过 Header 传递,不得进入 URL、query、业务 JSON、SSE data 或 WebSocket URL。Cookie 本身是 HTTP Header;Web 的状态变更请求还必须通过 `Origin` 校验和 `Hunjing-CSRF-Token` 双提交校验。 2.0 才设计自研加密登录、刷新和设备令牌协议。1.0 不自创密码学认证协议。 ### 2.2 OAuth 端点 OAuth/OIDC 元数据不放在 `/v1`: ```text GET /.well-known/oauth-authorization-server GET /.well-known/openid-configuration GET /.well-known/jwks.json GET /oauth2/authorize POST /oauth2/token POST /oauth2/revoke GET /oauth2/userinfo ``` - Web 使用 Authorization Code;服务端建立 HttpOnly、Secure、`SameSite=Lax` Session Cookie。 - Tauri 使用 Authorization Code + PKCE,回调采用已登记的 loopback/deep link。 - Refresh token 只发送到 `/oauth2/token`,支持轮换和重用检测。 - Service Account API key 只显示一次,数据库只保存哈希、前缀、scope、过期和撤销状态。 ## 3. 通用请求头 | Header | 必需 | 语义 | | --- | --- | --- | | `Authorization` 或 Web Cookie | 是 | 调用主体认证;公开匿名读取除外 | | `Content-Type` | 有 body 时 | JSON 为 `application/json` | | `Accept` | 推荐 | JSON、SSE 或具体媒体类型 | | `traceparent` | 推荐 | W3C Trace Context | | `tracestate` | 否 | 供应商追踪状态,不含敏感数据 | | `baggage` | 否 | 登记的低敏、低基数键;不参与授权 | | `Hunjing-Request-ID` | 推荐 | 业务请求关联;缺失时服务端生成 | | `Idempotency-Key` | 条件必需 | 创建 Run、支付、退款、结算和副作用命令 | | `If-Match` | 更新时推荐 | ETag 乐观并发 | | `Hunjing-CSRF-Token` | Cookie 状态变更 | 与受保护 Cookie 值匹配 | 服务端响应回显 `Hunjing-Request-ID`、`traceparent`,资源响应提供 `ETag`。敏感 Header 必须在反向代理、APM 和应用日志完全脱敏。 ## 4. 资源与 JSON - 字段统一 `snake_case`;ID 使用契约前缀,并按[协议与稳定标识](README.md#id-前缀)校验 UUID version。 - 时间是 UTC RFC 3339;时长是非负 `_ms` 整数。 - 金额是 `{amount_minor: integer, currency: "USD"}` 或明确 Decimal string,不使用浮点。 - 不适用字段省略;已知未知值用 `null`;真实零值用 `0`。 - 创建成功返回 `201` + `Location`;同步读取/修改返回 `200`;无 body 删除返回 `204`;异步操作返回 `202` + Job/Run。 - 资源直接作为成功 body 返回,不套无意义的 `{data: ...}`;列表除外。 ### 4.1 列表 ```json { "data": [], "next_cursor": "opaque-or-null", "has_more": false } ``` 查询统一使用 `limit`(默认 50,最大 200)、`cursor`、`order=asc|desc`。Cursor 是 opaque,并绑定排序和稳定过滤条件;客户端不得解析。 ### 4.2 乐观并发 可变资源响应提供 `ETag: ""`。`PATCH`、发布、合并、权限变更和关键状态动作带 `If-Match`;版本过期返回 `412 precondition_failed`,不做静默覆盖。 ## 5. 命令与状态动作 - CRUD 使用 `POST/GET/PATCH/DELETE`。 - 非 CRUD、具有领域语义且需审计的动作使用子资源: ```text POST /v1/runs/{run_id}/cancellations POST /v1/approvals/{approval_id}/decisions POST /v1/releases/{release_id}/publications POST /v1/orders/{order_id}/refunds ``` 不使用含糊的 `/action` 或把命令塞进 `PATCH status`。命令响应包含其创建的资源或 `202` Job。 ## 6. 幂等 `Idempotency-Key` 是调用方生成的 opaque string,最长 128 字符。服务端按“认证主体 + HTTP 方法 + 路径模板 + Key”隔离,并保存规范化请求哈希: - 相同 Key、相同请求:返回原响应和原资源 ID; - 相同 Key、不同请求:`409 idempotency_conflict`; - 未知副作用结果:返回原操作状态,不自动重复执行; - Webhook 使用 Provider event ID 独立去重。 聊天消息与 Run 原子创建时,一个 Key 覆盖消息和 Run 两个资源。 ## 7. 错误 ```json { "error": { "code": "validation_failed", "message": "请求字段不合法。", "trace_id": "trace_01...", "request_id": "request_01...", "details": { "field_errors": [ {"path": "content.0.text", "reason": "required"} ] } } } ``` - `code` 是稳定机器值;`message` 可本地化;`details` 不稳定但必须脱敏。 - 401 不泄露凭据是否存在;跨租户/跨域对象按场景返回 404。 - 429 返回 `Retry-After`;SDK 只自动重试网络、408、409(安全时)、429 和 5xx。 - 流式 HTTP 200 后错误通过事件发送,最终资源状态仍可通过 GET 查询。 ## 8. API 版本 - 公共路径使用 major `/v1`;兼容入口保留其协议 `/v1`。 - 向后兼容增加字段不升 major;客户端必须忽略未知字段和枚举。 - 破坏性变更进入 `/v2`,不在同一字段上改变语义。 - Preview 能力通过明确 `Hunjing-Preview` Header 和独立 Schema,不用 `X-` 前缀。 - 1.0 实现阶段不为未发布旧契约保留兼容层;正式发布后按公开弃用策略处理。 ## 9. 文件上传 大文件不经过 JSON base64: 1. `POST /v1/uploads` 创建 Upload; 2. 服务端返回单次或 multipart 预签名 URL、允许 MIME、大小和过期; 3. 客户端直传对象存储; 4. `POST /v1/uploads/{upload_id}/completions` 完成并触发校验/扫描; 5. 成功后产生 `file_` 或 `media_` 资源。 预签名 URL 属短期对象访问 capability,不是用户登录或 API 凭据,因此是“凭据不得进入 URL”的唯一受控例外。它必须短期、绑定对象路径/方法/大小/MIME,优先单次使用;不得进入 Referer、业务日志、社区内容、Message、Audit、SSE 或诊断响应。未经完成和扫描的 Upload 不能被 Agent、社区或市场引用。