中继描述符核心对象
RelayDescriptor
RelayDescriptor 是公共中继的可验证服务身份声明,通过客户端—中继接口发布,并在中继 Noise 握手中交换。它把链上 relay_id、对应的 Neo 公钥、客户端端点、libp2p Peer ID、协议能力和有效期绑定为一个签名对象。客户端用它确认业务端点与中继身份的绑定;其他中继直接验证握手提供的描述符,并结合 Registry 当前记录确认其与连接 Peer ID 的绑定。中继网络资格要求 Registry 记录的 status 为 active,并且 RelayDescriptor 有效。
Peer ID 由中继的 libp2p Peer 公钥按 libp2p 规范派生,用于标识安全连接身份;relay_id 标识中继的 Neo 账户身份。
{
"$type": "meshline.relay.descriptor",
"relay_id": "0x1234567890abcdef1234567890abcdef12345678",
"public_key": "base64url...",
"endpoints": [
"https://relay.example.com/meshline/v1",
"wss://relay.example.com/meshline/v1",
"/dns4/relay.example.com/tcp/4201/p2p/12D3KooW..."
],
"capabilities": [
"channel.host.v1"
],
"expires_at": 1730086400,
"relay_signature": "base64url..."
}
| 字段 | 类型 | 必需 | 语义与约束 |
|---|---|---|---|
$type |
string | 是 | 固定为 meshline.relay.descriptor |
relay_id |
string | 是 | Registry 记录中的中继 ID |
public_key |
string | 是 | 33-byte SEC1 compressed secp256r1 公钥,无 padding base64url;必须派生出 relay_id |
endpoints |
array<string> | 是 | 非空的端点地址集合;格式见中继端点地址 |
capabilities |
array<string> | 否 | 中继发布的能力声明;没有可选能力声明时为空或省略;最多 64 项,每项必须是非空且不超过 128 UTF-8 bytes 的字符串;按大小写敏感的完整字符串比较且不得重复;数组顺序不承载语义,未知名称必须忽略 |
expires_at |
integer | 是 | 到期时间;当前时间达到或超过该值后,描述符不再有效 |
relay_signature |
string | 是 | relay_id 对应的 Neo N3 中继账户按账户签名生成的 64-byte 签名,无 padding base64url |
签名时排除根 relay_signature 字段,并按网络绑定 JSON 输入规则构造签名输入。
服务要求与能力声明
公共中继必须完整提供中继发现和身份验证、HTTPS 客户端基础 API、设备与资料状态、账户路由、DHT 查询、中继间转发、消息发送、有限期账户消息时间线和 HTTP 同步。
本规范为频道和群组托管服务定义以下能力声明:
| 能力标识 | 对应服务与实现要求 |
|---|---|
channel.host.v1 |
中继提供频道托管服务时必须包含的能力声明;声明后必须完整提供频道方法索引中具有 HTTP 形式的接口,并按 auth.device.verify 的规则核验直接连接频道中继的远程设备;存在 WSS endpoint 时还必须提供频道订阅和频道通知 |
group.host.v1 |
中继提供群组托管服务时必须包含的能力声明;声明后必须完整提供群组方法索引中具有 HTTP 形式的接口,并按 auth.device.verify 的规则核验直接连接群组中继的远程设备;存在 WSS endpoint 时还必须提供群组订阅和群组通知 |
中继端点地址
地址格式与校验
本协议定义以下可拨号地址格式,其格式同时确定传输方式:
| 地址格式 | 用途 |
|---|---|
https://... |
公共客户端 HTTP API 基地址 |
wss://... |
公共中继提供的客户端 WebSocket 完整连接地址 |
包含 /tcp/ 和 /p2p/<peer-id> 的 libp2p multiaddr |
中继间连接 |
公共中继必须提供至少一个 HTTPS 地址和一个 libp2p TCP multiaddr。存在 WSS 地址表示中继完整提供WebSocket JSON-RPC的客户端 WebSocket 传输与通知机制;没有 WSS 地址表示该机制不可用。
每个 HTTPS 候选地址都必须满足 Registry 入口的全部格式约束。WSS 候选使用 wss scheme;其余格式约束与 HTTPS 候选相同。两者都必须是具有有效主机的绝对 URL;空 query 或 fragment 也不允许。HTTP 请求地址的构造和 WSS 连接方式见方法与请求映射。
对本规范已定义的 HTTPS、WSS 和 libp2p TCP 地址,任一候选地址格式无效时,整个描述符无效;不得把这些地址的格式错误作为未知传输格式忽略。验证方不得通过补齐路径、删除禁用部分或改写地址后接受描述符;签名验证使用原地址。认证 origin 另按中继 origin计算,不代替端点格式校验。
对于本规范未定义的传输格式,验证方必须保留地址原值及其数组位置参与验签,但在本协议中忽略该项,不得尝试连接,也不得将其计入必需地址或 Peer ID 绑定检查;不得仅因其存在拒绝整个描述符。
地址数组的每一项必须为非空字符串,且不得包含重复项。数组顺序不承载业务语义,签名仍保留实际数组顺序。
候选选择与重试
同一传输方式可以有多个候选地址;业务连接必须通过相应传输的身份与协议验证。服务端已经返回合法协议响应后,是否改用其他地址重试由相应方法的错误处理和重复请求规则决定;调用方不得把业务错误自动视为地址连接失败。
同一 RelayDescriptor 中某种传输方式的候选地址全部失败后,建议调用方刷新 Registry 状态和 RelayDescriptor,检查中继资格或服务地址是否发生变化。候选地址都属于同一 relay_id;地址切换不改变目标中继,也不表示存在备用中继或服务迁移。
对于可能已被服务端接受的写请求,地址切换后的处理遵循具体方法的重复请求和重试规则。方法明确允许幂等重试时,调用方在新端点满足身份验证及会话要求后,可以向同一中继原样提交请求;幂等保留期、当前授权及其他适用条件仍按该方法执行。其他方法继续按各自要求读取、同步或查询状态后再决定是否继续操作;地址切换本身不授权重放。
中继描述符验证规则
验证方必须:
- 从
public_key派生 Neo 单签账户,并确认其 script hash 等于relay_id; - 验证 Neo 账户签名;
- 确认对应 Registry 记录的
status为active; - 按中继端点地址规则验证
endpoints,忽略未知传输项,确认至少包含一个有效的 HTTPS 地址和一个有效的 libp2p TCP multiaddr; - 确认
RelayDescriptor尚未到期; - 确认
capabilities满足字段定义的约束;使用可选能力前确认RelayDescriptor包含对应能力声明; - 确认所有 libp2p TCP multiaddr 都包含 Peer ID,并且这些 Peer ID 完全相同。
客户端使用 HTTPS 或 WSS 时,必须从已经通过上述验证的 endpoints 中选择相应候选地址建立连接。origin 的计算、会话绑定和新建 WSS 连接的认证遵循会话认证规则;切换到不同 origin 后,调用需要会话的方法前必须在新 origin 按该方法允许的模式重新认证。
中继使用本描述符所列的 libp2p TCP 地址建立连接时,必须按中继连接与身份认证完成握手声明和身份验证。每个候选地址中的 Peer ID 都必须与实际安全连接的 Peer ID 相同,并且远端必须支持本规范定义的中继 DHT 协议与中继 RPC 协议;不满足条件的连接必须拒绝协议协商或关闭。从 DHT 候选地址开始的身份发现遵循该连接规范。各传输的失败处理、RelayDescriptor 刷新和请求重放约束见中继端点地址。