# Key Self-Service API 日期:2026-06-05 状态:已审核通过 适用版本:vNext.2 > 审核说明:本文设计完整,API 契约清晰。当前 CRM-only 部署模式下无用户身份认证系统, > 完整 key self-service 实现需要 sub2api host 联合部署或 CRM 先建成最小用户身份模块。 > 本文设计通过的实现骨架: > > 1. `0015_user_keys.sql` — key_records 表(指纹、mask、状态、分组) > 2. `internal/store/sqlite/user_keys_repo.go` — key CRUD repo > 3. `internal/app/key_self_service.go` — handler 骨架 > 4. `deploy/tksea-portal/` — 前端 key 管理区骨架 > > 完整用户面 200 闭环需联合部署后完成。 ## 目的 定义用户 key 自助申请流程中的 API 契约,包括 key 的创建、展示、重置、暂停、恢复、查询。当前版本仅做设计,不实现。 ## 实体与状态 ### KeyRecord | field | type | 说明 | | ---------------- | -------- | ------------------------------------ | | key_id | string | 唯一 ID | | owner_subject_id | string | 属主 | | key_fingerprint | string | 生成时对完整 key 取 sha256 | | masked_preview | string | 最后 4 位或 `sk-****....abcd` | | display_name | string | 用户可编辑名称 | | logical_group_id | string | 对应逻辑分组 | | allowed_models | []string | 该 key 可调用的模型列表 | | admin_status | string | active / paused / disabled / retired | | quota_status | string | ok / exhausted / limited / unknown | | last_used_at | datetime | 空表示从未使用 | | created_at | datetime | 创建时间 | | expires_at | datetime | 可选失效时间 | ### 审计事件 | field | type | 说明 | | ---------------- | -------- | ---------------------------------------- | | event_id | string | 唯一 ID | | actor_subject_id | string | 操作者 | | actor_role | string | admin / user | | target_key_id | string | 受影响的 key | | action | string | create / reset / pause / resume / delete | | result | string | success / denied / failed | | reason | string | 操作说明 | | created_at | datetime | 事件时间 | ## REST API 契约 ### POST /api/keys 创建 key。明文 key 在返回的 `plaintext_key` 字段返回一次。 请求体: ```json { "logical_group_id": "gpt-shared", "display_name": "test key", "allowed_models": ["gpt-5.4"] } ``` 响应 201: ```json { "key": { "key_id": "key_abc123", "plaintext_key": "sk-...full-key...", "masked_preview": "sk-****abcd", "display_name": "test key", "logical_group_id": "gpt-shared", "allowed_models": ["gpt-5.4"], "admin_status": "active", "quota_status": "ok", "created_at": "2026-06-04T..." } } ``` 说明: - `plaintext_key` 只在本响应返回 - 后续所有列表/详情接口都不包含 `plaintext_key` ### GET /api/keys 获取当前用户自己的 key 列表。 响应 200: ```json { "keys": [ { "key_id": "key_abc123", "masked_preview": "sk-****abcd", "display_name": "test key", "logical_group_id": "gpt-shared", "allowed_models": ["gpt-5.4"], "admin_status": "active", "quota_status": "ok", "last_used_at": null, "created_at": "2026-06-04T..." } ] } ``` 约束: - 只返回当前 subject 的 key - 不返回 `plaintext_key` - 不返回 `route_id`、`shadow_group_id`、`host_account_id` ### GET /api/keys/:id 获取单个 key 元数据。校验属主或管理员权限。 响应 200:同上(无 `plaintext_key`)。 ### POST /api/keys/:id/reset 重置 key。旧 key 失效,新明文 key 在响应中返回一次。 响应 200: ```json { "plaintext_key": "sk-...new-full-key...", "masked_preview": "sk-****wxyz", "admin_status": "active" } ``` 约束: - 写入审计事件 - 旧 `plaintext_key` 立即失效 - 重置后当前 subject 的 sticky binding 应重新评估 ### POST /api/keys/:id/pause 暂停 key。请求体可选 `reason`。 响应 200: ```json { "key_id": "key_abc123", "admin_status": "paused", "reason": "admin initiated" } ``` 约束: - 暂停后用户调用应失败 - 暂停原因应对用户可见 - 写入审计事件 ### POST /api/keys/:id/resume 恢复暂停的 key。 响应 200: ```json { "key_id": "key_abc123", "admin_status": "active" } ``` 约束: - 仅暂停状态的 key 可恢复 - 写入审计事件 ### DELETE /api/keys/:id 删除/退役 key。 响应 200: ```json { "key_id": "key_abc123", "admin_status": "retired" } ``` 约束: - 退役后不再参与分发 - 写入审计事件 - 不真正删除记录,保留审计一致性 ## 授权规则 用户侧: - 仅管理自己的 key - 不能查看他人 key、metadata、audit log 管理员侧: - 可查看所有 key 的 metadata - 可暂停 / 恢复 / 重置 / 退役任意 key - 可查看审计事件 - 禁止查看已收回的 `plaintext_key` ## 安全限制 1. 创建 key 限频:每 subject 每小时 5 次(vNext.2 建议值) 2. 重置 key 限频:每 subject 每 24 小时 2 次(vNext.2 建议值) 3. key 最短存活时间:至少存活 1 小时才允许退役(可讨论) 4. 管理员暂停 key 不需要 subject 同意,但需要记录 reason ## 测试要求 - 用户 A 创建 key → 用户 B 不能看到 - 用户 A 创建 key → 用户 B 不能重置 - 创建后 `plaintext_key` 只返回一次 - 管理员暂停后,用户调用返回 403 且 reason 明确 - 重置后旧 key 失效,新 key 唯一可用的证据 ## 与本轮范围关系 属于 vNext.2 设计产物。在 vNext.1 审核通过前,不允许实现。