14. 网络节点协议与互操作
本章说明跨节点协议如何把一次交互送达正确节点并保留可验证上下文;它不把“请求已送达”扩展为“资源已发布”,也不把“节点已认证”扩展为“资源控制权已验证”。
包络 -> 节点认证 -> 请求签名 -> 业务验证 -> 状态处理 -> 响应记录
在 OpenAgenet (OAN) 的实现中,跨节点操作通常由共享协议 crate 提供结构化请求和响应类型,再由具体节点服务负责 HTTP 路由、存储和后台任务。因而阅读一条链路时,应同时核对三层:HTTP 方法与路径、JSON 包络字段、节点内部的受理或发布状态。oan-protocol-common/schemas/agent-contract/openapi.yaml 当前是契约入口和版本占位,并不是所有节点接口的完整自动生成描述;不能仅凭该文件推断某个接口已经在每个部署中启用。
14.1 通用协议信封与请求头
通用包络可以采用如下字段语义,业务正文仍由对应接口定义:
{
"protocolVersion": "oan-resource-2026",
"requestId": "req-01",
"purpose": "cdn-publish",
"method": "POST",
"path": "/cdn/resources",
"aud": "did:oan:...",
"requestTimestamp": "2026-09-07T10:00:00Z",
"requestNonce": "random-value",
"bodyHash": "sha256:...",
"proof": {
"type": "Ed25519Signature2020",
"creator": "did:oan:...",
"created": "2026-09-07T10:00:00Z",
"proofPurpose": "assertionMethod",
"proofValue": "z...",
"cryptoSuite": "Ed25519Sha256",
"hashAlgorithm": "SHA-256",
"verificationMethod": "did:oan:...#key-1"
}
}
上例对应 SignedRequestEnvelope 的字段,而不是另加一套 sourceDid、targetDid、timestamp 或 sequence 字段。包络中的 aud 表示目标受众,proof 承载请求签名;资源包、注册提交或批次条目等业务载荷在包络之外由具体请求结构承载。发送方生成 requestId、requestTimestamp、requestNonce 和 bodyHash;接收方校验受众、时间、nonce、摘要、签名主体和签名覆盖范围。未知字段是否接受由协议版本和具体结构的反序列化规则决定,缺少必填字段或摘要不一致必须拒绝。
14.2 节点间认证
| 检查项 | 证明内容 | 失败处理 |
|---|---|---|
| 节点 DID | 通信主体是谁 | 拒绝认证 |
| 节点授权 VC/治理状态 | 是否具备当前通信或业务资格 | 拒绝或降级为只读 |
| 有效期和状态 | 凭证是否仍有效 | 不使用过期或撤销凭证 |
| 授权域和能力 | 请求动作是否在范围内 | 拒绝越权动作 |
| 资源控制证明 | 资源是否由提交者控制 | 进入业务验证失败 |
认证判断应按顺序完成:先确认连接目标和节点 DID,再解析节点 DID 文档中的验证方法,随后验证节点授权 VC 或治理状态,最后检查请求动作是否落在授权域内。节点授权资格与资源控制权是两条不同链路:前者说明某节点可以代表特定网络角色通信,后者由资源提交中的控制证明说明提交者能够控制资源 DID。即使节点认证成功,接收方仍必须对资源包、DID文档和控制证明执行业务校验。
14.3 签名上游请求
签名至少覆盖规范化方法、路径、目标节点、时间、nonce、请求 ID 和 bodyHash。验证方应依据 proof.verificationMethod 或兼容的验证方法标识解析公钥,确认 proof.creator、验证方法控制者和受信任的上游节点身份一致。
当前共享协议结构使用 proof,而不是在 SignedRequestEnvelope 中定义单独的 kid、signature 字段。proof.verificationMethod 指向 DID文档中的验证方法,proof.creator 表示签名创建者;oan-crypto 先按密码套件对可签名值进行规范化,再执行签名或验签。对于 Ed25519Sha256 和 Sm2Sm3,签名输入是规范化 JSON 的字节;旧版兼容套件可能先对规范化内容计算哈希,因此协议版本和密码套件必须共同解释签名输入。HTTP 头示例只有在具体部署把包络字段映射到请求头时才适用,不能替代 JSON 包络本身。
验签失败、目标 aud 不匹配、bodyHash 不匹配、验证方法不属于签名主体或请求已过期,均应在进入资源写入和发布队列前拒绝,并记录 requestId 与可供运维定位的错误类别。
X-Request-ID: req-01
X-Node-DID: did:oan:...
X-Timestamp: 2026-09-07T10:00:00Z
X-Nonce: random-value
X-Body-Hash: base64url(...)
X-Signature: jws-or-equivalent
14.4 节点授权 VC 交换
节点授权 VC 交换应遵循“携带或引用 → 解析签发方 DID → 验签 → 查询状态 → 检查授权域和能力”的顺序。缓存只能减少重复解析,不能绕过有效期、撤销状态和治理事件刷新。
节点授权 VC 的签发方是承担治理职责的可信机构或根平台,接收方应验证签发方 DID文档和签名,并检查 VC 的主体节点 DID、角色、授权域、能力、有效期及撤销或暂停状态。VC 可以作为请求中的完整凭证,也可以通过双方约定的引用方式取得;后一种方式必须保证引用内容可追溯、摘要可核对,且不能因为缓存命中就跳过状态检查。资源注册 VC 属于资源登记结果,不能反过来证明注册服务节点具备节点间通信资格。
14.5 注册节点到根平台协议
sequenceDiagram
participant R as 注册服务节点
participant G as 根平台
R->>G: 带节点身份、授权证明和请求签名的资源提交
G->>G: 校验节点授权、资源控制证明、版本和哈希
alt 校验失败
G-->>R: 拒绝、错误码、requestId
else 校验通过
G-->>R: 已受理或发布状态、版本、证明引用
end
根平台返回“已受理”只表示请求进入处理流程;注册 VC、根平台可信发布证明、内容分发和发现索引状态应分别表达。
注册服务节点向根平台提交的业务载荷由 ResourceVerifyAndPublishRequest 表示,其中包括注册服务节点 DID、资源登记提交内容和 upstreamAuth。登记提交内容包含资源 DID、资源类型、DID文档、元数据、版本、各项摘要、注册凭证和主体控制证明。根平台先验证上游包络及节点资格,再校验资源控制证明、DID文档与摘要的绑定关系;通过后才进入根平台的持久化和后续发布流程。超时或连接中断时,注册服务节点应使用原 requestId 查询受理状态,不能仅凭再次收到响应或本地重试次数判断资源是否已发布。
14.6 根平台到内容分发协议
分发请求应关联资源 DID、版本、资源包摘要、根平台可信发布证明引用和分发序列。内容分发服务接收后重新计算摘要,确认来源和版本,再返回接收、完成或失败状态;接收成功不等于发现节点已索引。
共享协议中,单条内容分发请求使用 ResourceCdnPublishRequest,批量请求使用 ResourceCdnBatchPublishRequest;批量条目以 publicationCursor 关联根平台的发布顺序,公共包络通过 upstreamAuth 证明上游调用上下文。代码中对应的内容分发路径常量为 /cdn/resources 和 /cdn/resources/batch,但实际可访问地址仍取决于服务的路由前缀和反向代理配置。内容分发平台完成校验后形成自己的可读取资源或清单,根平台的发布完成状态与发现服务节点的索引完成状态应分别记录。
14.7 内容分发到发现节点协议
发现节点同步应使用游标或批次序列,并对每个资源版本重新校验摘要和根平台可信发布证明引用。重复批次应幂等处理,游标推进必须在批次内容验证成功后完成。
对于从内容分发平台读取的增量数据,共享协议使用 ResourceCdnIndexResponse 表达 items、count、afterCursor、nextCursor 和 hasMore;每个条目携带当前游标和资源包。根平台向发现服务节点发送的通知则使用通知批次、起止序列和内容分发地址等信息,二者不能把字段名称直接混用。发现服务节点只有在资源包、DID文档、摘要和发布证明关联均通过验证后,才应写入本地索引并推进下一游标;失败批次保留原游标,待修复后重试。
sequenceDiagram
participant G as 根平台
participant C as 内容分发平台
participant D as 发现服务节点
G->>C: /cdn/resources 或批量发布请求
C-->>G: 接收/完成/失败及发布游标
D->>C: afterCursor 增量读取
C-->>D: items、nextCursor、hasMore
D->>D: 校验资源包和来源证明
D-->>D: 校验成功后推进同步游标
14.8 发现响应签名与来源证明
发现响应至少应能区分以下信息:
| 信息 | 含义 |
|---|---|
| 查询节点 DID | 谁生成了本次响应 |
| 数据来源 | 数据来自哪个分发或索引来源 |
| 索引版本/时间 | 查询使用的读模型新鲜度 |
| 匹配原因 | 哪些字段或标签命中 |
| 根平台证明引用 | 发布事实的外部证据入口 |
| 响应签名 | 防止响应被替换 |
响应签名证明节点对响应内容负责,不替代资源控制证明或根平台发布证明。
当前 ResourceDiscoveryResponse 结构包含 discoveryDid、candidates、createdAt 和可选 proof;发现服务节点的查询实现可以返回空的 proof,因此响应签名属于预留的协议能力或启用后的部署能力,不应描述为所有当前实例的默认行为。若启用,签名应覆盖查询结果、创建时间、查询节点 DID 以及必要的索引或来源摘要;调用方仍需独立检查候选资源的 DID文档、资源控制证明和根平台可信发布证明。候选项中的 score 只表示匹配相关性,不是信任等级或授权结论。
14.9 序列号、请求标识符与重放防护
| 字段 | 主要用途 | 重试时是否复用 |
|---|---|---|
requestId |
关联一次业务请求及其日志 | 同一逻辑请求复用 |
| 幂等键 | 防止提交副作用重复执行 | 同一操作复用 |
nonce |
防止签名消息重放 | 通常重新生成 |
sequence/游标 |
保证批次或事件顺序 | 按同步协议使用 |
| 时间窗口 | 限制请求新鲜度 | 重新签名时更新 |
requestId 用于把一次逻辑操作在注册服务节点、根平台、内容分发平台和发现服务节点之间串起来;requestNonce 用于阻止同一份签名包络被再次接受;publicationCursor、sequenceFrom、sequenceTo、afterCursor 和 nextCursor 用于传播顺序或增量读取。它们的数值空间和推进规则不同,不能把请求 ID 当作事件序列,也不能以发现查询的分页参数代替发布同步游标。重试同一逻辑请求时可以保留 requestId,但新的签名消息通常应使用新的 nonce,并由接收方依据幂等规则判断是否已处理。
14.10 分页、过滤与兼容性规则
客户端页面分页、发现查询分页和节点同步游标不可混用:页面分页面向展示,发现分页面向候选结果,同步游标面向增量传播。游标失效时应返回可识别错误或要求重新同步,不得静默从任意位置继续。
当前共享查询结构中的 limit 默认值是 10,发现查询还可携带资源类型、能力标签、协议、版本和版本模式等条件;这只是发现查询结构的默认行为,不代表所有管理列表接口的默认分页大小。同步响应中的 hasMore 与 nextCursor 服务于增量传播,查询结果的 score、版本模式和候选排序服务于用户检索。协议演进时,新增过滤字段可以按兼容规则处理,但改变游标含义、排序稳定性或版本模式应通过版本说明明确告知调用方。
14.11 超时、重试与幂等操作
| 操作 | 网络超时后的默认动作 | 原因 |
|---|---|---|
| 只读查询 | 有限退避后重试 | 通常无业务副作用 |
| 状态查询 | 使用原请求 ID 查询确认 | 区分未完成和未发生 |
| 资源提交 | 不盲目重放,先查询幂等状态 | 可能已产生登记或发布副作用 |
| 同步批次 | 按原游标和批次重试 | 保证增量顺序和幂等 |
重试必须区分“未收到响应”和“已收到明确失败”:网络超时只说明结果未知,资源提交、发布和状态变更类操作应先用原请求标识查询;明确的参数、授权或签名错误不应自动重试。后台发布器可以在同一发布游标上恢复处理,接收方应以资源 DID、版本、摘要和游标组合识别重复消息,避免重复写入或错误推进进度。具体超时、退避和并发度由部署配置决定,本章不把某一环境的数值固化为协议常量。
14.12 协议版本协商
版本协商至少应声明协议版本、支持的能力和不可兼容的约束。没有共同版本时应明确失败,不得按字段猜测或静默降级到不安全路径。
{
"protocolVersions": ["oan-resource-2026"],
"capabilities": ["signed-response", "cursor-sync"],
"required": ["request-signature"]
}
上例是协商信息的表达示例,不表示当前所有节点都提供独立的能力协商端点。当前共享协议将 oan-resource-2026 作为资源协议版本常量,并在包络中使用 protocolVersion;openapi.yaml 的 info.version 为契约文件版本,两者不能直接等同。协商结果至少要固定字段解释、签名覆盖范围、密码套件、游标语义和错误处理方式;若对方只支持不安全或无法解释的降级路径,应拒绝建立该业务链路。
14.13 消息大小、压缩与传输限制
限制应同时考虑压缩前消息、压缩后传输体和解压后内容;接收方应先执行大小和格式保护,再进行资源写入或高成本验证。压缩只改变传输方式,不改变签名和摘要覆盖的逻辑内容。
当前共享协议类型定义了资源包、批量条目和包络字段,但没有在该公共结构文件中统一声明所有部署的消息大小、压缩算法或 HTTP body 上限。因此这些限制应由具体服务配置、反向代理和运行环境共同确定,并在节点间能力或部署文档中公开可核对的值。对批量同步尤其要同时限制单项大小、批次项数和累计解压大小;超过限制时应在入库前返回明确错误,并保留请求 ID,不能截断后继续验签或索引。
14.14 关联、可追踪性与审计标识符
一次资源传播链至少应保持以下关联字段:
requestId -> resourceDid -> resourceVersion -> publishSequence
-> syncCursor -> discoveryIndexVersion -> errorCode
节点可以增加本地 trace 标识,但不得丢失上游 requestId、资源 DID 和版本;日志不得记录私钥和完整敏感凭证。
对发布链路,建议至少保存如下可检索关联:
| 链路阶段 | 关键关联字段 | 用途 |
|---|---|---|
| 注册受理 | requestId、资源 DID、资源版本 |
定位一次登记请求 |
| 根平台发布 | 资源包摘要、根平台可信发布证明引用、publicationCursor |
判断发布事实和顺序 |
| 增量同步 | afterCursor、nextCursor、批次序号 |
判断是否漏同步或重复同步 |
| 发现索引 | 发现服务节点 DID、索引时间、错误类别 | 解释查询结果的新鲜度 |
审计记录应保留验证结果和状态转换,而不是只记录 HTTP 200。对于敏感 VC,应记录其类型、摘要或引用及验证结论,避免把完整凭证内容和任何私钥写入普通业务日志。
14.15 协议兼容性与弃用规则
字段或版本弃用应经历“公告—兼容期—迁移—停止接受”阶段,并保留错误码和请求 ID。涉及签名覆盖范围、认证语义、游标含义或状态语义的变化不得仅通过增加可选字段解决,应升级协议版本或提供明确迁移规则。
兼容性测试至少应覆盖:旧版本包络能否被明确拒绝或按兼容规则解析、未知可选字段是否不会改变签名语义、旧游标是否不会被误当作新游标、签名主体和 aud 是否仍按同一规则验证。版本迁移期间,发送方应优先使用双方确认的共同版本,接收方应在错误响应中返回可关联的请求 ID 和不兼容原因;停止接受旧版本前,应先完成节点配置、后台发布任务和历史游标的迁移。协议文件、共享结构和服务实现必须同步更新,不能只修改文档中的版本字符串。
参考来源
| 来源 | 类型 | 链接 |
|---|---|---|
oan-protocol-common |
代码仓:跨节点请求、响应、错误和事件类型 | https://github.com/wolfbrother/oan-protocol-common |
oan-root-services |
代码仓:根平台节点间接口 | https://github.com/OpenAgenet/oan-root-services |
oan-registrar-node |
代码仓:注册服务节点接口和上游调用 | https://github.com/OpenAgenet/oan-registrar-node |
oan-discovery-node |
代码仓:发现同步和查询接口 | https://github.com/OpenAgenet/oan-discovery-node |
oan-trust-indexer |
代码仓:治理状态查询接口 | https://github.com/OpenAgenet/oan-trust-indexer |
| Agentic Overlay Network Architecture | IETF 草案:节点互操作架构 | https://datatracker.ietf.org/doc/draft-xu-agentic-overlay-network-architecture/ |
| OAN Resource Identity and Discovery | IETF 草案:资源身份和发现互操作 | https://datatracker.ietf.org/doc/draft-xu-oan-resource-identity-discovery/ |