16. 客户端 SDK、社区 Skill 与开发者体验
本章说明客户端层如何为资源提供方、调用方和节点运营者提供不同入口:oan-sdk-ts 面向程序化集成,社区技能包面向智能体辅助的资源整理、注册、发现和验证,官网提供浏览器交互。三者复用 OpenAgenet (OAN) 协议,不改变 DID、控制证明、注册 VC、根平台可信发布证明或治理状态的语义。主体私钥、资源控制私钥和本地身份备份留在客户端,服务端只接收公开材料和必要证明。
| 入口 | 主要使用者 | 核心职责 | 不承担的职责 |
|---|---|---|---|
| TypeScript SDK | 应用开发者、平台集成者 | 类型、请求构造、注册发现调用、验证辅助 | 替用户作出信任决定 |
| 社区技能包 | 资源提供方、智能体操作者 | 整理资源、辅助注册发现、解释结果 | 持有治理密钥或替代节点服务 |
| 官网 | 浏览器用户 | 交互式注册、发现、预览和本地下载 | 托管用户私钥或保证端点安全 |
| 运维辅助 | 节点运营者 | 构建、配置检查、部署与烟测 | 自动决定生产密钥和回滚策略 |
16.1 TypeScript SDK 范围
TypeScript SDK 由协议类型、HTTP 客户端、治理读取和工作流辅助组成,适合 Node.js、浏览器构建环境及兼容 fetch 的运行时。调用方可分别配置注册服务节点、发现服务节点、根平台和内容分发平台地址。SDK 负责序列化、请求和响应解析,不替调用方决定资源是否可信。
代码组织上,packages/protocol-types 提供 ResourceRegistrationSubmission、ResourcePackage、发现查询和治理响应等共享类型;packages/client-ts 提供 OanClient 及各节点 HTTP 调用;packages/sdk-ts 提供 DID文档草稿、资源包绑定、哈希格式、信任摘要和生命周期判断;packages/governance-ts 面向链下信任索引器的治理读取。这样的分层使应用可以只依赖 HTTP 客户端,也可以在需要时组合资源校验和生命周期辅助,而不必复制协议结构。
| 能力域 | SDK 提供 | 调用方仍需负责 |
|---|---|---|
| DID 与资源模型 | 类型、解析和构造辅助 | 确认资源内容和控制关系 |
| 注册 | 请求构造、提交和状态读取 | 保管私钥、确认提交目标 |
| 发现 | DID 精确查询、语义查询和结果解析 | 判断相关性是否满足业务需求 |
| 验证 | 证明字段读取和验证辅助 | 制定授权策略与最终调用决策 |
| 生命周期 | 汇总登记、发布、分发和可见性状态 | 处理超时、重试和业务补偿 |
SDK 的端点选择有两种常见方式:面向官网和社区用户时传入一个 baseUrl,由客户端派生官方网关下的服务地址;进行多节点或第三方节点测试时,分别传入 registrarEndpoint、discoveryEndpoint、rootEndpoint 和 cdnEndpoint。端点配置只决定请求发送到哪里,不会替调用方完成节点授权、资源控制证明或治理判断。
16.2 SDK 中的 DID 身份与密钥操作
身份和密钥操作在客户端完成。SDK 生成主体身份、资源 DID 和验证方法,使用私钥签署控制 challenge、资源版本或请求,并将公钥写入 DID文档。备份可包含主体和资源控制材料,但不得进入日志、HTTP 请求或索引;导入时应验证结构、DID 与公钥关系及签名能力。
Node.js 场景可以使用 identity-store-node 将主体、智能体和节点身份分桶保存到本地目录;浏览器场景则由网页状态或浏览器存储承载当前会话,并通过用户操作导出备份。两种实现的共同边界是:DID文档可以公开传播,私钥只能用于客户端签名;服务端收到的是已经签名的控制证明或节点请求包络,而不是原始私钥。
- [x] 私钥生成、签名和备份导出发生在客户端。
- [x] 注册请求只携带公开 DID文档、资源信息和必要控制证明。
- [x] 导入备份前校验格式、版本、DID、公钥和签名能力。
- [ ] 不得把私钥写入 URL、日志、遥测或服务端存储。
16.3 注册流程示例
注册流程应依次准备本地身份、资源 DID、DID文档、元数据、资源包、哈希和控制证明,再调用注册服务节点。下面只展示客户端调用边界,submission 应由经过本地校验和签名的实际数据构造:
import { OanClient } from "@openagenet/oan-sdk-ts/client";
const client = new OanClient({
registrarEndpoint: "https://registrar.example.org",
});
const result = await client.registerResource(submission);
console.log({
resourceDid: result.resourceDid,
registrationVc: result.registrationVc ?? null,
});
注册 VC 可能暂缺,客户端应保存资源 DID、请求 ID 和状态;网络超时不能直接判断注册未发生。应先确认明确成功或失败,再按请求 ID 或资源 DID查询状态,并区分注册服务节点受理、根平台发布和发现索引可见三个阶段。
提交前的最小检查可以归纳为“对象一致、摘要一致、控制有效、目标明确”:didDocument.id 必须等于 resourceDid,资源类型必须与 DID 主体编码一致,DID文档、元数据和资源包的摘要必须对应实际内容,控制证明必须由相应资源控制私钥生成,注册端点必须是用户明确选择的注册服务节点。技能包可以辅助发现缺失项,但最终提交仍由客户端或用户确认。
登记受理 -> 根平台接受 -> 内容分发完成 -> 发现索引可见
| | | |
Registrar Root CDN Discovery
16.4 DID 精确发现示例
DID 精确发现以资源 DID 为定位键,不依赖名称相似度。客户端调用发现接口后,应核对返回 DID、版本、资源包和证明引用,再验证 DID文档、版本哈希、生命周期、根平台可信发布证明和端点策略。
const resourceDid = "did:oan:SKDM:example";
const resource = await client.getDiscoveryResource(resourceDid);
if (resource.resourceDid !== resourceDid) {
throw new Error("discovery_did_mismatch");
}
精确命中只证明发现服务节点返回了对应标识的记录,不能单独证明版本仍有效、节点仍获授权或业务端点可以安全调用。
应用在取得候选结果后,可以先按 resourceDid 做一致性检查,再按请求的版本模式选择候选版本,最后加载资源包或 DID文档完成证据验证。若详情接口返回 404,通常表示发现索引中存在候选摘要但对应详情尚不可读;客户端应保留该差异并向调用方报告,而不是把候选结果直接当作完整可调用对象。
16.5 语义发现示例
语义发现可组合自然语言任务、能力标签和结构化条件。官网英文查询 “I need a tool that can search code repositories and summarize the project structure.” 可作为示例输入:
const discovery = await client.discoverResources({
query: "I need a tool that can search code repositories and summarize the project structure.",
resourceType: "tool",
limit: 10,
});
for (const candidate of discovery.candidates) {
console.log(candidate.resourceDid, candidate.score);
}
响应应保留命中字段、标签、资源类型、协议、索引版本、来源和匹配原因。语义相关性回答“可能适用”,结构化条件回答“满足显式筛选”,可信证据回答“当前版本是否可验证”,三者不能互相替代。
SDK 对 HTTP 响应的封装应保留协议阶段,而不是把不同接口都压缩成一个布尔成功值。注册调用至少需要保留注册服务节点的 status、资源 DID、注册 VC 和根平台响应;发现调用需要保留 discoveryDid、候选数组、创建时间和可选响应 proof;动态状态或解释响应应保留原始 JSON,供上层按实际字段处理。这样,SDK 才能区分“登记已受理”“根平台已验证并排队”“资源已被发现”和“证据已完成验证”。
语义查询的结果处理宜分成两步:先用查询文本、资源类型、标签或协议缩小候选范围,再对候选逐项检查生命周期、授权域、服务绑定和证明状态。发现服务节点可能使用语义索引,也可能在后端不可用时回退到关键词检索;调用方应关注返回的候选和解释信息,不应把某一种搜索后端名称写死为业务依赖。
16.6 验证与信任证据处理
验证应先检查 DID文档与资源 DID、公钥和控制证明,再分别检查注册 VC、根平台可信发布证明和节点授权 VC 的签发者、主体、版本、签名、有效期、状态、授权域和能力范围。关键证明缺失或过期时,客户端应标记不可确认或拒绝使用。
| 证据 | 主要证明的事实 | 不能证明的事实 |
|---|---|---|
| 控制证明 | 提交者能使用对应控制私钥 | 注册服务节点已受理或资源已发布 |
| 注册 VC | 注册服务节点对登记事实的声明 | 根平台已发布或端点安全 |
| 根平台可信发布证明 | 特定资源版本通过根平台验证并进入发布流程 | 所有发现服务节点均已索引 |
| 节点授权 VC | 节点在授权域和期限内具备相应资格 | 节点返回的每条业务数据都正确 |
| 治理状态 | 当前治理侧记录的状态事实 | 资源功能质量和业务适用性 |
一个可复现的验证夹具至少包含:资源 DID、DID文档、资源包、对应摘要、控制证明或注册 VC,以及用于校验节点资格的节点 DID文档和授权材料。测试先使用未篡改材料确认通过,再分别修改 DID、版本、摘要、签发者或生命周期状态,确认 SDK 返回明确的验证错误。验证失败是否阻止使用取决于证据类型:控制证明或摘要失败通常应阻止登记,语义匹配不足则只影响候选排序,治理状态不确定则应阻止高风险调用或要求人工确认。
16.7 社区 Skill 包职责
社区技能包通过脚本和说明辅助资源整理、标签推荐、DID 生成、注册请求、发现查询、证据核验和结果解释。它不是注册服务节点、根平台或发现服务节点,不应持有治理密钥;用户确认、私钥使用、敏感路径和最终发布决定仍由用户或运营者控制。
典型交互遵循“读取材料 → 形成草稿 → 用户确认 → 本地签名 → 调用服务 → 解释结果”的顺序。技能包可以提示缺失字段、推荐语义标签和调用 SDK,但不得静默替换资源端点、扩大授权域或把推荐内容冒充资源提供方声明。
OanSkill 的公开工作流入口包括 draftRegistrationFromResourceDescription()、registerFromResourceDescription()、registerBatchFromResourceDescriptions()、discover()、lifecycle() 和只读治理辅助方法。资源描述工作流会返回候选信息、缺失输入、质量问题和建议动作;这使批量接入能够逐项修复,而不是因为一个资源失败就丢失整个批次的诊断结果。技能包默认使用官方公共网关,第三方节点只有在用户明确配置后才参与流程。
16.8 资源编写与注册辅助
资源编写辅助应统一处理智能体服务、技能包、MCP 服务和工具 API,并引导填写名称、描述、用例、端点、协议、版本、能力标签、授权域和资源包信息。推荐标签不是最终声明,生成前应检查 DID文档、元数据和资源包哈希一致性。
| 输入类别 | 最低检查内容 | 产出用途 |
|---|---|---|
| 身份与控制 | 主体 DID、资源 DID、验证方法 | 生成控制证明 |
| 能力语义 | 名称、描述、用例、能力标签 | 语义治理与发现 |
| 调用信息 | 协议、端点、认证提示 | 调用前筛选和协商 |
| 版本材料 | 版本、前序版本、资源包摘要 | 更新、幂等和完整性校验 |
| 治理边界 | 资源类型、授权域、生命周期 | 准入和发现过滤 |
资源整理时应把 authorizedDomains 与 capabilityTags 分开处理:前者是注册服务节点用于授权范围判断的机器可读域,后者是语义发现信号。技能包可以根据描述提出标签建议,但不能从 DID 主体编码、端点主机名或自然语言描述擅自推导授权域;无法确定时,应把缺失事实返回给用户补充。
16.9 节点运营者与部署辅助
节点运营辅助可执行依赖检查、配置检查、Rust 编译、服务启动、健康检查和部署后注册发现烟测。服务器、域名、密钥、生产配置及回滚由人工确认。
- [x] 检查进程、监听端口和健康端点。
- [x] 检查数据库连接与必要表结构。
- [x] 检查链下信任索引器状态及同步序列是否持续推进。
- [x] 验证注册结果结构、DID 精确发现和语义发现最小路径。
- [ ] 不以单一健康端点成功代替完整业务烟测。
运维辅助的验收顺序应与业务传播顺序一致:先检查进程和健康端点,再检查数据库及节点授权状态,之后验证资源注册请求是否得到结构化响应,最后使用 DID 精确查询和语义查询确认发现链路。注册服务节点返回成功而根平台、内容分发平台或发现服务节点不可用时,应报告对应阶段,不应把整条链路压缩成一个“部署成功”结论。
16.10 包分发与兼容性管理
发布物必须包含运行入口、构建后的 JavaScript、类型声明、脚本、必要文档和依赖。社区技能包发布到 npm 时不得遗漏 SKILL.md、脚本或资源文件;SDK 发布前应从干净目录安装并验证导入、注册请求构造和发现请求构造。
发布验收至少包括:
- 预览实际入包文件,确认源码仓存在不等于发布包已包含。
- 在空目录安装待发布包,验证声明支持的模块入口。
- 运行最小注册、DID 精确发现和语义发现请求构造测试。
- 核对 README、类型声明、脚本和资源文件可从安装包访问。
- 对破坏性接口或协议语义变化提高主版本,并说明迁移边界。
对于社区技能包,npm 发布物还必须保留 SKILL.md、references、agents、src 和构建后的 dist;不能只检查 TypeScript 编译成功。对于 SDK,应检查根入口及 ./client、./governance、./protocol-types、./identity-store-node 等 exports 是否都指向实际存在的 JavaScript 和声明文件。发布验证应在未使用源码目录的空目录中执行,以便发现 package.json 的 files、入口或依赖声明遗漏。
16.11 示例资源与集成模板
最小集成流程为:创建或导入本地身份;生成资源 DID、DID文档、元数据、资源包和控制证明;提交注册服务节点;执行 DID 精确查询;执行自然语言、标签和结构化发现;调用前验证版本、证明、治理状态和端点。
flowchart LR
A[创建或导入本地身份] --> B[准备四类资源之一]
B --> C[生成 DID文档与资源包]
C --> D[本地签名并提交注册]
D --> E[查询生命周期状态]
E --> F[DID 精确发现或语义发现]
F --> G[验证证明与治理状态]
G --> H[业务侧决定是否调用]
SDK 是程序化入口,技能包是操作辅助,官网是交互入口。四类模板可以共享身份、版本、语义和证明字段,但协议、端点和资源包内容必须体现各自差异。
四类资源可以复用同一套接入骨架,但其调用材料不同:智能体服务通常需要可调用服务端点,技能包需要清单或下载入口,MCP 服务需要 MCP 协议绑定,工具 API 需要 API 端点和输入输出模式。模板中的 protocolBindings、服务端点、资源描述和包信息应按实际资源填写;不能因为四类资源都可被发现,就把一种资源的协议字段复制到另一种资源。
16.12 客户端安全与秘密处理
私钥和身份备份只在客户端生成、使用和保存。VC、DID文档和资源包按公开性分别处理;日志只记录 DID、版本、请求 ID、状态和错误代码,不记录私钥、完整本地身份或未脱敏签名原文。
客户端安全边界: 能够被下载或导出的本地身份文件属于控制权材料,不因其由官网生成而成为公开文件。注册 VC、DID文档和资源包也应按各自公开策略处理,不能因为共同打包下载就获得相同的披露级别。
SDK 和技能包的测试不应把真实私钥写入固定夹具或提交记录。自动化测试可以使用临时目录、临时密钥和占位 DID;测试结束后清理身份文件,并检查请求体、异常文本和日志中没有私钥字段。若应用需要长期保存本地身份,应由使用者选择加密存储、操作系统凭据库或离线备份方案,SDK 不应默认为用户上传到远程服务。
16.13 浏览器注册与本地身份备份行为
浏览器注册页面把本地身份作为用户控制材料。用户可新建并下载带时间标记的 oan-registration-identity.bundle.<timestamp>.json,也可导入备份;浏览器只向注册服务节点发送公开 DID文档、资源数据和控制证明。注册结果包必须在提交成功且确认资源 DID 后生成。
| 用户动作 | 浏览器侧变化 | 服务端影响 |
|---|---|---|
| 新建并备份 | 生成身份、更新当前主体、触发下载 | 无 |
| 从本地导入 | 校验并加载用户选择的备份 | 无 |
| 预览 DID文档 | 按当前表单和身份生成或复用预览 | 无注册提交 |
| 提交注册 | 校验当前输入并发送公开材料和证明 | 产生注册请求 |
| 成功后下载 | 打包 DID文档、注册 VC 和本地身份 | 不产生第二次注册 |
16.13.1 浏览器端密钥生成
浏览器端使用 Web Crypto 或 SDK 实现生成密钥并建立 DID 与公钥绑定。页面不得把私钥放入 DOM、URL、公开 API 或错误消息;刷新或清理站点数据可能丢失未备份状态,应提示用户及时备份。
16.13.2 本地身份导入
导入操作读取用户明确选择的 JSON 文件,检查版本、主体配置、资源配置和私钥材料结构,并验证 DID文档与公钥关系。导入失败不能覆盖已有有效身份;成功后预览和提交使用导入身份并重新计算控制证明。
16.13.3 本地备份下载
本地备份下载使用浏览器标准下载能力,文件名包含 oan-registration-identity.bundle 和生成时间,避免覆盖旧备份。服务端不参与文件生成,用户应将其作为敏感密钥备份保管。不同浏览器和移动系统对自动下载的支持存在差异,下载失败不得把空文件或未完成状态标记为有效备份。
16.13.4 注册结果打包下载
注册结果打包下载仅在服务端明确返回成功且页面确认资源 DID 后触发。压缩包名将资源 DID 中的冒号替换为连字符,包含注册 VC、DID文档和已有本地身份文件;VC 为空可保留空项,但失败、超时或缺少 DID 时不得下载。
16.14 DID、VC 与资源包下载格式
DID文档、注册 VC、资源包和本地身份备份使用可识别的 JSON 文件名和稳定编码。公开文件不得混入私钥;导入方按内容字段验证 DID、版本和哈希,不应仅凭扩展名或文件名信任。
| 文件 | 建议内容 | 敏感级别 | 最低校验 |
|---|---|---|---|
| DID文档 | DID、公钥、服务与 OAN 元数据 | 公开 | DID、验证方法、结构版本 |
| 注册 VC | 签发者、主体、声明、签名与状态引用 | 按签发和披露策略 | 签发者、主体、签名、有效期 |
| 资源包 | 资源描述、端点、能力与版本材料 | 按资源策略 | 资源 DID、版本、摘要 |
| 本地身份备份 | 主体及资源控制私钥材料 | 高敏感 | 包版本、DID、公钥私钥对应关系 |
未知字段在兼容版本内可以保留,缺失必填字段、无法识别的结构版本、摘要不一致或私钥混入公开文件时必须拒绝导入或发布。
下面是一个用于说明字段关系的示例资源包,其中 DID、摘要、时间和签名值均为占位值,不代表线上资源。示例展示公开资源包如何同时引用 DID 文档、元数据摘要、资源包摘要和根平台可信发布证明;实际实现还可以保留协议类型允许的扩展字段。
{
"packageVersion": "1.0.0",
"resourceDid": "did:oan:SKDM:ExampleResourceDid00000000000000000000",
"resourceType": "skill",
"didDocument": {
"@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/oan/v1"],
"id": "did:oan:SKDM:ExampleResourceDid00000000000000000000",
"verificationMethod": [
{
"id": "did:oan:SKDM:ExampleResourceDid00000000000000000000#key-1",
"type": "Ed25519VerificationKey2020",
"controller": "did:oan:SKDM:ExampleResourceDid00000000000000000000",
"publicKeyMultibase": "zExamplePublicKey"
}
],
"authentication": [
"did:oan:SKDM:ExampleResourceDid00000000000000000000#key-1"
],
"oanMetadata": {
"subjectType": "skill",
"resourceType": "skill",
"resourceDescription": {
"name": "Example repository structure skill",
"description": "Searches a code repository and summarizes its project structure.",
"capabilityTags": ["code-repository", "project-structure"]
},
"capabilityTags": ["code-repository", "project-structure"]
}
},
"didDocumentHash": "sha256:example-did-document-hash",
"metadataHash": "sha256:example-metadata-hash",
"packageHash": "sha256:example-package-hash",
"hashAlgorithm": "sha-256",
"metadata": {
"resourceDid": "did:oan:SKDM:ExampleResourceDid00000000000000000000",
"resourceType": "skill",
"subjectType": "skill",
"name": "Example repository structure skill",
"description": "Searches a code repository and summarizes its project structure.",
"capabilityTags": ["code-repository", "project-structure"],
"lifecycleState": "active",
"packageVersion": "1.0.0",
"packageHash": "sha256:example-package-hash",
"metadataHash": "sha256:example-metadata-hash",
"hashAlgorithm": "sha-256",
"updatedAt": "2026-01-01T00:00:00Z"
},
"rootProof": {
"rootDid": "did:oan:INPT:ExampleRootDid00000000000000000000",
"signature": "zExampleRootSignature",
"hashAlgorithm": "sha-256"
},
"createdAt": "2026-01-01T00:00:00Z"
}
上述资源包可以作为公开发布和发现侧的输入,但不能替代本地身份备份。后者还包含主体或资源控制私钥,必须单独导出、导入和保管;把本地身份与资源包放进同一个注册结果压缩包,只表示下载操作集中,不改变两类材料的敏感级别和验证方式。下载格式的兼容判断应以内容结构为准,文件名中的时间戳和 DID 只用于人类识别和避免覆盖,不能作为真实性、签发者或控制权证明。
参考来源
| 来源 | 类型 | 链接 |
|---|---|---|
oan-sdk-ts |
代码仓:DID、身份、资源包、注册和发现客户端能力 | https://github.com/OpenAgenet/oan-sdk-ts |
oan-community-skill |
代码仓:社区 Skill、脚本和开发者工作流 | https://github.com/OpenAgenet/oan-community-skill |
oan-protocol-common |
代码仓:SDK 使用的协议类型和错误契约 | https://github.com/wolfbrother/oan-protocol-common |
| OAN Yellow Paper | arXiv 黄皮书:客户端、资源发布和验证流程 | https://arxiv.org/abs/2606.03163 |
| OAN White Paper | arXiv 白皮书:开发者生态和开放基础设施定位 | https://arxiv.org/abs/2606.03161 |