- 所有 API 中出现的用户标识一律为
string。 - 该约束覆盖路径参数、query 参数、请求体字段、响应 DTO 字段,以及统一响应结构中的业务数据内容。
- 任何旧草案中的整型用户标识写法都已失效,前后端正式契约只允许字符串用户标识。
除文件下载、文件内容读取这类二进制流接口外,所有 JSON API 必须统一使用以下成功响应结构:
{
"code": 0,
"msg": "成功",
"data": {},
"timestamp": "2026-03-12T06:00:00Z",
"requestId": "req-123"
}约束如下:
code:成功时固定为0;失败时固定为 HTTP 状态码,例如400、401、403、500。msg:返回给调用方的用户可读提示文案,必须通过 Spring BootMessageSource+ i18n 机制生成,禁止在 controller 中硬编码。msg的 locale 必须在响应封装层或全局异常处理层通过LocaleContextHolder从请求上下文自动获取,禁止在 controller/service 中显式传递Locale。data承载实际业务数据;列表、分页对象、详情对象、操作结果对象都必须放在data下。- 分页响应统一使用
{ items, total, page, size },禁止直接暴露 SpringPage的content/pageable/sort/first/last等内部结构。 timestamp:响应创建时间戳,由后端统一自动生成。requestId:请求链路 ID,由后端统一注入,便于日志追踪。- Controller 层禁止直接返回
Map、裸 DTO、裸Page、裸List作为 JSON 成功响应。 - 普通 JSON 接口应直接返回统一响应 DTO;仅文件下载、文件预览等需要自定义状态码或 header 的二进制接口保留
ResponseEntity。 - 删除、撤销、移动标签等操作也必须返回统一 JSON 结构;如无实体数据,返回
data.message或data=null,但外层结构不得变化。 - 错误响应与成功响应使用同一外层结构,不再使用单独的异常 JSON 结构。
- 异常链路中的
msg也必须通过 Spring Boot 标准 i18n 机制生成;参数校验异常、领域异常、认证鉴权异常都必须进入统一的@RestControllerAdvice出口。 - 二进制流接口保持原始 HTTP 语义,不套
code/data包装:/download/file- 其他返回
application/octet-stream、application/zip等内容类型的接口
成功响应示例:
{
"code": 0,
"msg": "发布成功",
"data": {
"skillId": 123,
"version": "1.0.0"
},
"timestamp": "2026-03-12T06:00:00Z",
"requestId": "req-123"
}错误响应示例:
{
"code": 403,
"msg": "需要命名空间管理员或所有者权限",
"data": null,
"timestamp": "2026-03-12T06:00:00Z",
"requestId": "req-123"
}| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/skills |
搜索/列表(匿名仅返回 PUBLIC 技能) |
| GET | /api/v1/skills/{namespace}/{slug} |
技能详情(PUBLIC 匿名可访问) |
| GET | /api/v1/skills/{namespace}/{slug}/versions |
版本列表 |
| GET | /api/v1/skills/{namespace}/{slug}/versions/{version} |
版本详情 |
| GET | /api/v1/skills/{namespace}/{slug}/versions/{version}/files |
文件清单 |
| GET | /api/v1/skills/{namespace}/{slug}/versions/{version}/file?path=... |
读取单个文件(query param 避免路径中 / 的解析问题) |
| GET | /api/v1/skills/{namespace}/{slug}/download |
下载默认安装版本(最新已发布版本) |
| GET | /api/v1/skills/{namespace}/{slug}/versions/{version}/download |
下载指定版本包 |
| GET | /api/v1/skills/{namespace}/{slug}/resolve |
解析技能版本(支持 query param: version、tag、hash) |
| GET | /api/v1/skills/{namespace}/{slug}/tags/{tagName}/download |
按标签下载(解析标签指向的版本后下载) |
| GET | /api/v1/skills/{namespace}/{slug}/tags/{tagName}/files |
按标签查看文件清单 |
| GET | /api/v1/skills/{namespace}/{slug}/tags/{tagName}/file?path=... |
按标签读取单个文件 |
| GET | /api/v1/namespaces |
公开命名空间列表 |
| GET | /api/v1/namespaces/{slug} |
命名空间详情 |
Public API 的可见性规则:
PUBLIC技能:匿名和已登录用户均可访问NAMESPACE_ONLY技能:仅该命名空间成员可访问(需登录)PRIVATE技能:owner 本人 + 该 namespace 的 ADMIN 以上可访问(需登录)
GET /api/v1/skills/{namespace}/{slug}/versions/{version} 的 data 字段除版本基础信息外,还必须包含:
parsedMetadataJson:SKILL.mdfrontmatter 的完整 JSON 序列化结果manifestJson:版本文件清单摘要 JSON
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /oauth2/authorization/github |
发起 GitHub OAuth 登录(Spring Security 内置) |
| GET | /login/oauth2/code/github |
GitHub OAuth 回调(Spring Security 内置) |
| GET | /api/v1/auth/me |
当前用户信息(未登录返回 401) |
| POST | /api/v1/auth/logout |
登出(清除 Session) |
| GET | /api/v1/auth/providers |
可用的 OAuth Provider 列表(前端渲染登录按钮用) |
| GET | /api/v1/auth/methods |
统一登录方式目录(密码/OAuth/direct/bootstrap 元数据) |
| POST | /api/v1/auth/direct/login |
显式走直连认证 provider 的兼容登录入口(默认关闭) |
| POST | /api/v1/auth/session/bootstrap |
显式尝试用外部被动会话换取 skillhub Session(默认关闭) |
/api/v1/auth/providers 响应示例:
{
"code": 0,
"msg": "获取成功",
"data": [
{ "id": "github", "name": "GitHub", "authorizationUrl": "/oauth2/authorization/github" }
],
"timestamp": "2026-03-12T06:00:00Z",
"requestId": "req-123"
}前端根据此接口动态渲染登录按钮,新增 Provider 无需改前端代码。
/api/v1/auth/methods 返回统一登录方式目录。典型项包括:
PASSWORD:现有本地账号密码登录OAUTH_REDIRECT:OAuth 跳转登录DIRECT_PASSWORD:默认关闭的直连认证兼容入口SESSION_BOOTSTRAP:默认关闭的被动会话引导入口
示例:
{
"code": 0,
"msg": "获取成功",
"data": [
{
"id": "local-password",
"methodType": "PASSWORD",
"provider": "local",
"displayName": "Local Account",
"actionUrl": "/api/v1/auth/local/login"
},
{
"id": "oauth-github",
"methodType": "OAUTH_REDIRECT",
"provider": "github",
"displayName": "GitHub",
"actionUrl": "/oauth2/authorization/github"
}
],
"timestamp": "2026-03-12T06:00:00Z",
"requestId": "req-123"
}/api/v1/auth/session/bootstrap 请求示例:
{
"provider": "private-sso"
}/api/v1/auth/session/bootstrap 协议约束:
- 开源版默认关闭,需显式开启
skillhub.auth.session-bootstrap.enabled=true - 关闭时返回
403 - provider 不存在时返回
400 - 外部会话不存在或校验失败时返回
401 - 成功时返回与
/api/v1/auth/me相同的用户结构,并建立标准 Session
/api/v1/auth/direct/login 请求示例:
{
"provider": "private-sso",
"username": "alice",
"password": "secret"
}/api/v1/auth/direct/login 协议约束:
- 开源版默认关闭,需显式开启
skillhub.auth.direct.enabled=true - 关闭时返回
403 - provider 不存在时返回
400 - 成功时返回与
/api/v1/auth/me相同的用户结构,并建立标准 Session /api/v1/auth/local/login继续保留,作为现有本地账号入口
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/skills/{namespace}/{slug}/star |
收藏 |
| DELETE | /api/v1/skills/{namespace}/{slug}/star |
取消收藏 |
| POST | /api/v1/skills/{namespace}/{slug}/rating |
评分 |
| GET | /api/v1/me/stars |
我的收藏列表 |
| GET | /api/v1/me/skills |
我发布的技能列表 |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/skills/{namespace}/{slug}/versions/{version}/submit-review |
将 DRAFT 版本再次提交审核(当前主要用于撤回后重提) |
| POST | /api/v1/skills/{namespace}/{slug}/versions/{version}/withdraw-review |
撤回提审(PENDING_REVIEW → DRAFT,同时删除关联的 PENDING review_task) |
| GET | /api/v1/skills/{namespace}/{slug}/versions/{version}/draft |
查看草稿详情(owner 或 namespace ADMIN 以上) |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/skills/{namespace}/{slug}/tags |
列出标签 |
| PUT | /api/v1/skills/{namespace}/{slug}/tags/{tagName} |
创建/移动自定义标签(latest 为系统保留标签,不可通过此接口操作) |
| DELETE | /api/v1/skills/{namespace}/{slug}/tags/{tagName} |
删除自定义标签(latest 不可删) |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/skills/{namespace}/{slug}/archive |
归档技能(namespace ADMIN 或 owner) |
| POST | /api/v1/skills/{namespace}/{slug}/unarchive |
恢复归档(namespace ADMIN 或 owner) |
| DELETE | /api/v1/skills/{namespace}/{slug}/versions/{version} |
删除 DRAFT/REJECTED 版本 |
当前代码中的 skill 生命周期读模型不再依赖 latestVersionStatus / viewingVersionStatus 一类拼装字段,而统一使用以下 projection:
headlineVersion:当前页面应展示的主版本publishedVersion:当前最新可分发的已发布版本ownerPreviewVersion:owner / namespace 管理者可见的待审核预览版本resolutionMode:PUBLISHED/OWNER_PREVIEW/NONE
其中:
- 公开详情、公开安装、公开搜索一律只认
publishedVersion - owner 详情页在没有可展示发布版本时,才允许
headlineVersion落到ownerPreviewVersion - 推广到全局一律使用
publishedVersion.id hidden是独立治理覆盖层,不属于生命周期状态机
发布成功响应中的 data 至少包含以下字段:
skillIdnamespaceslugversionstatusfileCounttotalSize
发布状态约束:
- 普通用户发布成功后,
status为PENDING_REVIEW - 持有
SUPER_ADMIN的用户通过 Web、/api/v1/publish、/api/v1/publish发布时,status为PUBLISHED,且不要求其必须是目标 namespace 成员 - 当前版本保持该审核策略,不再提供“全员直发”的运行模式
- 撤回审核不会删除版本记录,而是
PENDING_REVIEW → DRAFT
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/tokens |
创建 API Token |
| GET | /api/v1/tokens |
列出我的 Token |
| DELETE | /api/v1/tokens/{id} |
吊销 Token |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/whoami |
当前 Bearer Token 对应的用户信息 |
| POST | /api/v1/publish |
发布技能包(普通用户进入审核;SUPER_ADMIN 始终直发) |
| GET | /api/v1/resolve/{namespace}/{slug} |
解析版本 |
| GET | /api/v1/check/{namespace}/{slug}/{version} |
本地哈希与远端比对 |
一期不仅提供 skillhub 自有 CLI API,还必须暴露一组兼容 ClawHub CLI 的 registry API。
- 目标:让现有 ClawHub CLI 可通过配置 registry base URL 直接对接 skillhub
- 范围:一期聚焦覆盖 ClawHub CLI 所依赖的核心接口:查询、版本解析、下载、发布、whoami
- 要求:兼容层优先保持 ClawHub CLI 既有请求/响应语义;若内部领域模型不同,通过 adapter 层完成协议转换,而不是要求客户端适配 skillhub 私有协议
- 要求:兼容层纳入 OpenAPI 或独立兼容协议文档,并作为正式对外契约维护
- 要求:兼容层与 skillhub 自有
/api/v1/**并存,二者共享同一套权限、审计、限流与领域服务 - 非目标:前端页面不直接依赖兼容层;兼容层用于服务已有 ClawHub CLI 和相关自动化脚本
兼容层最少需要覆盖的能力类别:
- Registry metadata:技能查询、技能详情、版本列表、标签/默认版本解析
- Artifact resolution:按技能坐标或版本解析下载地址/下载流
- Publish workflow:包上传与发布结果返回
- Integrity check:版本存在性校验、摘要/哈希比对、whoami/token 上下文确认
如 ClawHub CLI 的现有协议与 skillhub 自有接口存在差异,文档以“兼容 ClawHub CLI 协议”为准,skillhub 内部 API 可继续保持当前风格。
Admin API 按最小权限拆分,不再统一要求 SUPER_ADMIN:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/admin/reviews |
全局空间待审核列表 |
| GET | /api/v1/admin/reviews/{id} |
全局空间审核详情 |
| POST | /api/v1/admin/reviews/{id}/approve |
通过全局空间审核 |
| POST | /api/v1/admin/reviews/{id}/reject |
拒绝全局空间审核 |
| GET | /api/v1/admin/promotions |
待审核提升申请列表 |
| GET | /api/v1/admin/promotions/{id} |
提升申请详情 |
| POST | /api/v1/admin/promotions/{id}/approve |
通过提升申请 |
| POST | /api/v1/admin/promotions/{id}/reject |
拒绝提升申请 |
| POST | /api/v1/admin/skills/{id}/hide |
隐藏技能 |
| POST | /api/v1/admin/skills/{id}/unhide |
恢复技能 |
| POST | /api/v1/admin/skills/{id}/yank/{versionId} |
撤回已发布版本 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/admin/users |
用户列表 |
| GET | /api/v1/admin/users/{id} |
用户详情 |
| PUT | /api/v1/admin/users/{id}/roles |
修改用户角色(USER_ADMIN 不可分配 SUPER_ADMIN) |
| POST | /api/v1/admin/users/{id}/approve |
审批待准入用户 |
| POST | /api/v1/admin/users/{id}/disable |
封禁用户 |
| POST | /api/v1/admin/users/{id}/enable |
解封用户 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/admin/audit-logs |
审计日志查询 |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/namespaces |
创建命名空间 |
| PUT | /api/v1/namespaces/{slug} |
更新命名空间信息 |
| GET | /api/v1/namespaces/{slug}/members |
成员列表 |
| POST | /api/v1/namespaces/{slug}/members |
添加成员 |
| PUT | /api/v1/namespaces/{slug}/members/{userId}/role |
修改成员角色 |
| DELETE | /api/v1/namespaces/{slug}/members/{userId} |
移除成员 |
| GET | /api/v1/namespaces/{slug}/reviews |
该空间待审核列表 |
| POST | /api/v1/namespaces/{slug}/reviews/{id}/approve |
空间管理员审核通过 |
| POST | /api/v1/namespaces/{slug}/reviews/{id}/reject |
空间管理员审核拒绝 |
| POST | /api/v1/namespaces/{slug}/skills/{skillId}/promote |
申请提升到全局 |
latest 自动跟随最新已发布版本,不可手动移动。
skill.latest_version_id:每次审核通过自动更新,始终指向最新 PUBLISHED 版本yank当前最新已发布版本时,需要同步重算latest_version_id指向下一个最新的PUBLISHED版本;若不存在则允许为nulllatest标签:系统保留,只读,自动与latest_version_id同步- 自定义标签(如
beta、stable-2026q1):允许人工创建和移动,用于固定安装通道
| 场景 | 使用字段 | 说明 |
|---|---|---|
| 搜索索引内容 | publishedVersion / latest_version_id |
外部协议仍叫 latest,但内部语义必须等价于最新已发布版本 |
/download(不带版本号) |
publishedVersion / latest_version_id |
下载最新已发布版本 |
CLI install @team/skill |
publishedVersion / latest_version_id |
等同于 @latest |
CLI install @team/skill@beta |
skill_tag 查询 |
自定义标签指向的版本 |
GET /api/v1/skills/{namespace}/{slug}/resolve 用于解析技能版本,支持以下 query param:
| 参数 | 类型 | 说明 |
|---|---|---|
version |
string | 精确版本号(如 1.2.0) |
tag |
string | 标签名(如 beta、latest) |
hash |
string | fingerprint 哈希,用于判断本地版本是否与 registry 同步 |
解析优先级:
version和tag不可同时传,同时传返回400 Bad Request- 仅传
version:精确匹配版本号 - 仅传
tag:查询skill_tag表获取target_version_id - 仅传
hash:遍历已发布版本,比对 fingerprint - 均不传:返回最新已发布版本;实现上可由
latest_version_id或等价 published projection 解析
响应:
{
"code": 0,
"msg": "获取成功",
"data": {
"skillId": 456,
"namespace": "team-name",
"slug": "my-skill",
"version": "1.2.0",
"versionId": 123,
"fingerprint": "sha256:abc123...",
"downloadUrl": "/api/v1/skills/team-name/my-skill/versions/1.2.0/download"
},
"timestamp": "2026-03-12T06:00:00Z",
"requestId": "req-123"
}hash 匹配时额外返回 "matched": true,不匹配时返回最新版本信息 + "matched": false。
兼容层 API 基地址为 /api/v1,通过 /.well-known/clawhub.json 发现。兼容层使用 canonical slug(双连字符映射规则,详见 00-product-direction.md 1.1 节)。
认证方式:Authorization: Bearer <token>。Bearer Token 可来自 CLI Device Flow 或平台 API Token。
GET /.well-known/clawhub.json
响应:
{
"apiBase": "/api/v1"
}
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/whoami |
当前用户信息 |
| GET | /api/v1/search |
搜索技能 |
| GET | /api/v1/resolve |
通过 slug + version 解析版本 |
| GET | /api/v1/download/{slug}/{version} |
下载技能 zip 包 |
| POST | /api/v1/publish |
发布技能(multipart/form-data,SUPER_ADMIN 直发) |
GET /api/v1/whoami
{
"handle": "username",
"displayName": "User Name",
"role": "user"
}GET /api/v1/search?q={keyword}&page={page}&limit={limit}
{
"results": [
{
"slug": "my-skill",
"name": "My Skill",
"description": "...",
"author": { "handle": "username", "displayName": "User Name" },
"version": "1.2.0",
"downloadCount": 100,
"starCount": 50,
"createdAt": "2026-01-01T00:00:00Z",
"updatedAt": "2026-03-01T00:00:00Z"
}
],
"total": 1,
"page": 1,
"limit": 20
}注意:兼容层返回的 slug 为 canonical slug 格式(全局空间直接返回 skill slug,团队空间返回 namespace--skill)。
GET /api/v1/resolve?slug={slug}&version={version}
{
"slug": "my-skill",
"version": "1.2.0",
"downloadUrl": "/api/v1/download/my-skill/1.2.0"
}GET /api/v1/download/{slug}/{version}
返回指定版本的 zip 文件流。默认版本解析由 resolve 接口负责。
POST /api/v1/publish
Content-Type: multipart/form-data
Parts:
- file: zip 包
一期同步响应,返回发布结果:
{
"slug": "my-skill",
"version": "1.0.0",
"status": "published"
}- 兼容层是独立的 Controller 层,内部调用与 native API 相同的领域服务
- 请求进入时将 canonical slug 转换为
(namespace_id, skill_slug)坐标 - 响应返回时将内部坐标转换为 canonical slug
- 兼容层不暴露 namespace 概念,对 ClawHub CLI 透明
- 发布时如果 canonical slug 包含
--,解析为团队空间发布;否则发布到全局空间 - 兼容层的认证复用 skillhub Bearer Token 体系,ClawHub CLI 通过登录或配置 token 后即可使用
分两阶段实施:
通过 Nginx Ingress limit-req 按 IP 全局限流,覆盖认证、搜索、下载等匿名可访问接口,防止基本的滥用和爬虫。
基于 Redis 滑动窗口,按用户/端点分类的精细限流。
| 端点类别 | 限流策略 |
|---|---|
| 搜索 API | 已登录 60 次/分钟,匿名 20 次/分钟(按 IP) |
| 下载 API | 已登录 120 次/分钟,匿名 30 次/分钟(按 IP) |
| 发布 API | 10 次/小时(按用户) |
| 认证 API | 30 次/分钟(按 IP) |
触发限流时返回 429 Too Many Requests + Retry-After Header。
- 统一响应包裹:
{ code, message, data, timestamp } - 分页格式:
{ items, total, page, size } - 错误码体系:业务错误码 + HTTP 状态码配合
- 版本策略:URL path 版本
/api/v1/ - 幂等性:写操作通过
X-Request-Id+ Redis 去重(TTL 24h)
- 响应格式完全遵循 ClawHub 协议,不套统一响应包裹
- 错误响应遵循 ClawHub 格式:
{ error: string, message: string } - 分页格式遵循 ClawHub 格式:
{ results, total, page, limit }
生成两份独立的 OpenAPI spec:
openapi-native.json:skillhub Native API,用于前端 SDK 生成openapi-compat-clawhub.json:ClawHub 兼容层 API,用于兼容性测试和文档