中继—推送网关协议
本协议规定推送网关地址的获取方式,以及中继向网关发送消息通知的报文、认证和交互规则,能力标识为 push.notify.v1。
网关声明与能力
设备通过 DeviceCertificate 中的 push_gateway 声明完整回调地址。省略表示没有声明网关。该地址可随设备证书传播;不得在地址或证书中放入推送平台用于定位设备的标识、回调密钥或平台凭据。
中继通过 push.notify.v1 声明遵循本协议的推送能力。
地址来源与更新
中继通过已验证的设备证书获取推送网关地址。网关声明在本网络内按 (account, device_id) 区分;采用设备证书时,以其中的 push_gateway 更新声明,字段省略时清除原声明。连接关闭或会话到期不清除声明。
中继确认设备不再获得账户授权时,清除其网关声明;恢复授权后,须重新取得并验证设备证书。发送通知时遵循发送目标验证。
网关地址
push_gateway 必须是具有有效主机的完整绝对 HTTPS URL:以 https:// 开头,最多 512 UTF-8 bytes,允许 query,不得包含空白、fragment、userinfo 或反斜杠;空 fragment 也不允许。
该完整地址就是接收端点,中继不得追加固定路径。设备证书和通知中的 push_gateway 必须保留原地址;不得通过补齐路径、删除部分、大小写转换或其他改写后接受不合规地址。目标地址绑定按完整字符串逐字比较,不按 URL 等价关系比较。
地址格式验证不依赖当时的 DNS 可用性。每次实际连接还须满足回调连接安全。
PushNotification
每个通知只面向一个接收设备。字段表示和未知属性遵循协议总则。
| 字段 | 类型 | 必需 | 语义与约束 |
|---|---|---|---|
$type |
string | 是 | 固定为 meshline.push.notification |
network |
string | 是 | 完整规范网络上下文,同时包含 Neo network magic 和 Registry 合约地址 |
relay_id |
string | 是 | 报告事件的中继 ID |
public_key |
string | 是 | 33-byte SEC1 compressed secp256r1 公钥,无 padding base64url;必须派生出 relay_id |
push_gateway |
string | 是 | 本次接收端点,满足网关地址规则 |
issued_at |
integer | 是 | 本通知签发时的非负 UTC Unix 秒 |
message_type |
string | 是 | 中继可见的原始消息对象的 $type,须逐字保留;非空,不限定枚举值 |
to |
string | 是 | 接收账户的账户 ID |
to_device_id |
string | 是 | 接收设备 ID,遵循设备 ID格式 |
message_id |
string | 是 | 原始消息 ID |
from |
string | 是 | 发送账户 ID |
accepted_at |
integer | 是 | 本中继记录的原消息接受时间,非负 UTC Unix 秒 |
sequence |
integer | 是 | 原记录在本中继上的时间线位置,非负安全整数;具体取值遵循原记录的约束 |
sender_nickname |
string | 否 | 发送账户的公开昵称,取自有效的 AccountProfile.nickname;非空且不得仅包含空白字符,最多 256 UTF-8 bytes |
subject_id |
string | 否 | 原消息关联对象的标识;含义和格式由 message_type 对应的消息定义确定 |
subject_name |
string | 否 | 该关联对象的显示名称,遵循对应对象的名称约束 |
relay_signature |
string | 是 | 中继账户对本通知生成的 64-byte Neo N3 签名,无 padding base64url |
通知中的消息信息必须与原消息及其记录一致。网关不得仅因未识别 message_type 而拒绝通知。
未取得昵称或昵称为空时省略 sender_nickname。
通知不得包含原消息信封、密文、密钥盒、设备证书或附件内容,也不得通过未知属性携带这些内容。其用途只限提示、展示和导航;接收方不得根据通知推进同步游标、标记消息已读或认定消息内容有效。
签名与认证
签名时只排除根 relay_signature,保留全部其他字段及未知属性,按网络绑定 JSON 输入和中继账户签名规则签署。
网关必须从可信配置取得预期网络和自己的完整公开回调地址,而不是采用请求或未经信任的转发 header 建立预期值。接受通知前必须完成:
- 验证 JSON 表示、对象类型、字段、大小和标识格式;账户 network magic 必须匹配预期网络。
- 逐字比较
network与预期网络、push_gateway与配置的公开接收地址;从预期网络构造签名输入。 - 验证公钥与
relay_id绑定及通知签名;中继公钥、签名表示沿用RelayDescriptor的相应字段约束。 - 通过预期网络的 Registry 确认中继登记为
active。 - 完成网关接受与去重。
网关对通知请求返回成功响应前必须完成上述验证,重复请求也不例外。
网关按上述规则认证发送通知的中继,不要求接收设备对各中继另行授权。
HTTP 回调
发送目标验证
每次发送通知前,按地址来源与更新核对当前网关地址。已签发通知中的 push_gateway 与当前地址不一致时,不得继续发送。
发送前必须确认原消息允许推送、目标设备当前获账户授权且有权访问通知涉及的信息。账户消息的 MessageEnvelope 和群消息的 GroupMessageEnvelope 仅当 notify 为 true 时允许推送。
设备当前授权由其归属中继确认:本中继负责该账户时读取本地当前权威设备状态,否则使用 device.status 的已验证结果;缓存的复用和失效遵循该方法及缓存失效与补查规则。没有可用的已验证授权结果时不得发送。
网关确认
中继向 push_gateway 的完整地址发送不含请求体的 HTTPS OPTIONS 请求,遵循回调连接安全。支持本协议的端点返回:
HTTP/1.1 204 No Content
Meshline-Push: push.notify.v1
只有收到 204 No Content 且 Meshline-Push 响应头的值为 push.notify.v1 时,确认才成功;字段名大小写不敏感,字段值大小写敏感。
中继可复用已取得的确认结果,但当前网关地址必须与确认请求的完整 URL 逐字相同;地址变更后须重新确认。未确认成功时,不得向该地址发送通知。
请求
中继向 push_gateway 的完整地址发送 HTTPS POST,使用 Content-Type: application/json 和 UTF-8 编码的完整 PushNotification,不使用 JSON-RPC 封装。请求体最多 4 KiB(4,096 bytes),计入空白和未知属性。
成功响应
通知请求成功时,网关返回 200 OK、Content-Type: application/json 和结果对象:
{"status":"accepted"}
| 字段 | 类型 | 必需 | 语义与约束 |
|---|---|---|---|
status |
string | 是 | accepted、skipped 或 duplicate,含义如下 |
| 状态 | 含义 |
|---|---|
accepted |
网关已接受本通知,负责后续平台发送和失败处理 |
skipped |
网关已跳过本通知,不向平台发送 |
duplicate |
相同通知已被接受或跳过,本次请求不另行触发处理 |
三种状态都终止中继重试。accepted 不表示平台已经接受,更不表示设备已经收到、展示或读取通知。
响应可以增加不改变上述含义的扩展属性,接收方忽略这些属性。只有 200 OK 与符合本节定义的结果对象组合才构成成功响应;不满足该条件的 2xx 响应视为结果未知。
错误与发送重试
| HTTP 状态 | 含义 |
|---|---|
400 Bad Request |
格式或字段无效 |
403 Forbidden |
网络、目标地址、签名、身份绑定或已确认的中继资格不符合要求 |
413 Content Too Large |
请求体超过上限 |
429 Too Many Requests |
请求被限流 |
503 Service Unavailable |
接受前临时不可用,例如无法确认 Registry 资格或无法满足去重要求 |
错误正文可以提供诊断信息。网关接受通知后,不得因后续平台处理失败而返回可重试的 HTTP 错误。
选择重试的中继可以对连接失败、结果未知、408、429 和 5xx 重试;其他 4xx 终止本通知。429 可以提供非负整数秒的 Retry-After;中继重试时必须满足该等待时间,非法值不得按零处理。
重试必须原样复用完整已签名通知。重试前还须执行发送目标验证。
网关接受与去重
去重要求适用于同一网关的单次运行。网关在同一网络、同一接收账户和设备范围内,以 (from, message_id) 判断重复通知。验证通过后,同一通知在去重有效期内只能接受或跳过一次;后续及并发重复请求必须返回 duplicate,不得另行触发处理,即使原通知尚未处理完成。
回调连接安全
每次建立连接时,必须验证最终实际目标为公网可路由单播 IP,拒绝本机、环回、私网、链路本地、多播、未指定及其他非公网地址,包括 IPv4 映射 IPv6 中对应的受限 IPv4 地址。
DNS 解析检查必须与实际连接目标保持一致;不得检查一个地址后再不经检查地重新解析或连接另一个地址。必须验证 TLS 证书与原 URL 主机的绑定,不得关闭证书验证。任何 3xx 都不得跟随,也不根据其中的地址改投。未通过上述验证时不得发送请求。
兼容性与一致性要求
符合本模块的实现必须通过推送通知测试向量,并按一致性测试边界覆盖:
- 网关声明:证书字段省略及新增、修改、移除,双签名覆盖、设备 ID 稳定、未知字段保留、地址格式与大小;从设备证书取得地址,以及证书更新、会话结束和设备失效时的声明处理规则。
- 通知报文与认证:字段约束、签名及身份伪造、跨网络隔离和目标地址绑定。
- 发送目标:消息推送条件、当前设备授权与信息访问权限、地址变更后的目标校验,以及授权结果不可用时禁止发送。
- 网关确认:空请求体、响应状态与能力响应头、完整 URL 绑定、地址变更后重新确认,以及确认失败时禁止发送通知。
- HTTP 交互:三种成功结果、错误响应、可选重试、等待时间、原样重试、并发去重、去重范围及不可用时的拒绝。
- 回调连接安全:受限 IP、DNS 目标切换、TLS 主机验证及重定向。