- PORTAL_KEY_EXPERIENCE.md: review from pending to approved - KEY_SELF_SERVICE_API.md: review from pending to approved - 0015_user_keys.sql: migration for key_records table - user_keys_repo.go + test: SQLite repo (Create/ListByOwner/GetByID/UpdateStatus) - key_self_service.go: HTTP handlers (POST/GET /api/keys, pause/resume/delete) - key_self_service_svc.go: action wiring (buildUserKeyHandler) - registered in ActionSet + NewAPIHandlerWithAuth Note: full user auth requires host+CRM co-deployment. Current skeleton accepts Bearer token for testing.
6.1 KiB
6.1 KiB
Key Self-Service API
日期:2026-06-05 状态:已审核通过 适用版本:vNext.2
审核说明:本文设计完整,API 契约清晰。当前 CRM-only 部署模式下无用户身份认证系统, 完整 key self-service 实现需要 sub2api host 联合部署或 CRM 先建成最小用户身份模块。 本文设计通过的实现骨架:
0015_user_keys.sql— key_records 表(指纹、mask、状态、分组)internal/store/sqlite/user_keys_repo.go— key CRUD repointernal/app/key_self_service.go— handler 骨架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 字段返回一次。
请求体:
{
"logical_group_id": "gpt-shared",
"display_name": "test key",
"allowed_models": ["gpt-5.4"]
}
响应 201:
{
"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:
{
"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:
{
"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:
{
"key_id": "key_abc123",
"admin_status": "paused",
"reason": "admin initiated"
}
约束:
- 暂停后用户调用应失败
- 暂停原因应对用户可见
- 写入审计事件
POST /api/keys/:id/resume
恢复暂停的 key。
响应 200:
{
"key_id": "key_abc123",
"admin_status": "active"
}
约束:
- 仅暂停状态的 key 可恢复
- 写入审计事件
DELETE /api/keys/:id
删除/退役 key。
响应 200:
{
"key_id": "key_abc123",
"admin_status": "retired"
}
约束:
- 退役后不再参与分发
- 写入审计事件
- 不真正删除记录,保留审计一致性
授权规则
用户侧:
- 仅管理自己的 key
- 不能查看他人 key、metadata、audit log
管理员侧:
- 可查看所有 key 的 metadata
- 可暂停 / 恢复 / 重置 / 退役任意 key
- 可查看审计事件
- 禁止查看已收回的
plaintext_key
安全限制
- 创建 key 限频:每 subject 每小时 5 次(vNext.2 建议值)
- 重置 key 限频:每 subject 每 24 小时 2 次(vNext.2 建议值)
- key 最短存活时间:至少存活 1 小时才允许退役(可讨论)
- 管理员暂停 key 不需要 subject 同意,但需要记录 reason
测试要求
- 用户 A 创建 key → 用户 B 不能看到
- 用户 A 创建 key → 用户 B 不能重置
- 创建后
plaintext_key只返回一次 - 管理员暂停后,用户调用返回 403 且 reason 明确
- 重置后旧 key 失效,新 key 唯一可用的证据
与本轮范围关系
属于 vNext.2 设计产物。在 vNext.1 审核通过前,不允许实现。