M6 私有服务控制契约
本页是截至 2026-10-18 已确认的实施规范;规范本身不表示所有平台或运行验收完成。A 指 ns 节点实现,B 指 NSD/NSGW 服务端实现。范围为私有服务与本地 Short DNS;Subnet、R2 和 M8 公共名字不在本次范围。
1. 不变量、标识和编码
Section titled “1. 不变量、标识和编码”- ServiceRef = NSD 身份绑定的 UUID + Network ID + Service ULID,发布者以完整身份公钥验证。NSD 当前 Network 使用规范 ULID;线上仍为原始 16 字节,不散列、不重新分配。客户端绑定入口也接受既有规范 UUID 表示,同一 16 字节值对应同一 scope。名字/VIP/短 Node ID 不作为权限键。
- report_revision 只排序该发布者/NSD/Network 的完整声明;snapshot_revision 排序 NSD 给目标/Network 的完整配置;listener_revision 排序单服务监听意图。三个计数不互相代用,重连不增加声明或监听版本。
- 同一发布者身份的一个 P 跨协议只归属于一个 ServiceRef;同服务可 TCP/UDP 双协议。P 可能被安装后不跨服务自动复用。普通 TCP/UDP 没有 Service ID,控制面换 ID 不能隔离旧连接意图。
- NSD 只协调自身可见资源;A 以一个 daemon 的持久表仲裁全部 membership。不按来源排名、列表顺序或原始到达顺序为已知冲突选赢家。
- 先持久提交再发送成功回执/执行可能安装的动作。数据库、journal 损坏或提交失败时不继续接受授权,不重置计数伪装恢复。
- 声明、目录、监听准备、ready、租约获准、业务可达是不同状态。后端更新可保持仍获授权的原流;撤销/租约到期不保留无授权原流。
共享 crate 定名 nsshared-service-control,能力为 service_control.v1,schema 为整数 1;本地持久 schema 为 ns.service-control.v1。这些是新契约,不复用 short_dns.v2 或旧 services_ack。wire 使用既有认证控制传输;其现有签名/防重放规则仍生效,不另造密码协议。
编码为确定性 CBOR:只用无符号整数、布尔、字节串、UTF-8 文本、null、定长容器;最短整数/长度表示;禁止不定长、浮点、tag、重复键和未知字段。map 键限定 ASCII 文本,按编码后键长度、再按编码字节序排序;集合按规范成员编码排序且不重复。接收端重编码校验,非规范输入拒绝。计数均为 u64,初值 1,溢出显式失败,禁止回绕。UUID/ULID 用 16 字节,完整节点公钥及 SHA-256 摘要用 32 字节,nonce 用 16 个随机字节且不得全零;诊断使用规范文本,不进入权限比较。
listener摘要内容固定为 service_ref, publisher_identity, listener_revision, P, protocols, backend_host, backend_port, enforcement_policy_version, enforcement_policy_digest,其中后两个字段绑定现有授权编译器的规范输入版本与完整服务执行授权摘要,不新增策略语言;不含显示名、昵称、runtime/session或租约。摘要采用 SHA-256,输入为相应域名字符串加一个零字节再加规范 CBOR:ns.service.report.v1、ns.service.snapshot.v1、ns.service.listener.v1。签名校验覆盖原始消息,内容摘要用于幂等/一致性,不替代认证。
服务控制 nsd_id 是 NSD 数据库中持久保存的 UUID,通过现有签名 info.get 的 service_control_nsd_id 字段发布(规范小写 UUID 文本,线上消息转换为 16 字节)。它在权威 epoch 变化和重启后保持不变;A 必须使用已信任 NSD 响应密钥验证后消费。客户端本地注册 UUID 只作配置索引,不能代替服务控制权威 ID,否则不同客户端无法引用同一服务范围。缺少该字段时不启用新服务契约,不生成本地替代 ID。
通用消息含 schema, kind, nsd_id, network_id, subject_identity, runtime_generation, runtime_id, authority_epoch, request_id, body。仅bootstrap消息 runtime.floor 与 runtime.bind 允许 authority_epoch=null;runtime.floor还允许runtime_generation/runtime_id=null。成功bind返回当前epoch,其后消息必须精确匹配。需要流绑定的消息另含 session_seq。服务消息 body 带 ServiceRef;无需流的报告/恢复消息不因配置流尚未建立而拒绝。响应回显目标、request_id 和相关版本/摘要,禁止跨目标复用。
2. 报告、恢复与快照
Section titled “2. 报告、恢复与快照”2.1 完整报告
Section titled “2.1 完整报告”完整声明内容 = scope, strict_mode, declarations。scope 为发布者完整身份+NSD/Network;declarations 按声明键的 UTF-8 字节排序,字段为 declaration_key, display_name, description, enabled, protocols, backend_host, backend_port, port_mode, requested_port。protocols 是 tcp/udp 的有序集合;backend_host 是带类型的 IPv4/IPv6 字节或规范 DNS 主机文本,后端端口 1..65535。fixed 的 requested_port 必须非零,auto 为 null。显示名和描述缺失编码 null,不混入本地短名、系统信息、runtime/session 或提议端口。私有 profile 的 HTTP/HTTPS 仅消费既有声明经校验得到的 TCP 后端语义,不启用 Host/SNI、公网域或 gateway/tunnel 角色;不支持的声明要求明确报 unsupported-field,不能静默丢弃有行为含义的要求。
A 在 fanout 前为每个 scope 原子保存规范内容、摘要和 report_revision,然后发送 report.replace。相同内容沿用版本。B 在授权/Network/当前机器实例/报告行同一事务校验:低版本 stale-report;同版本不同摘要 digest-conflict;同版本同摘要返回原结果;高版本按完整声明替换并写 outbox。展示名更新不换 ID;声明键变化、删除后重建换 ID。离线、读取失败、无报告不是删除;有效空报告才撤销全部该scope声明。
响应 report.accepted 含 accepted_revision, report_digest, bindings,bindings 是声明键到 ServiceRef/P/提议状态的对应表。暂未分配 P 的资源可 pending,不将 pending 当作访问许可。runtime 必须为当前实例;旧实例即使报告版本更高也拒绝。
2.2 恢复下界
Section titled “2.2 恢复下界”runtime.floor 是机器完整身份签名及既有所有权认证的只读bootstrap请求,不要求已经是当前runtime或已知epoch;返回目标scope、当前runtime代次/ID/有效状态和当前authority_epoch,不能单凭响应取得服务许可。启动代次丢失时A先持久保存更高代次,并以recovery模式关联该floor请求进行bind。B若仍认为旧实例有效,拒绝仅凭floor自动抢占;须等其按既有实例存活规则失效,或由本地所有者明确执行恢复操作后再bind。正常保留启动代次的重启按§3处理;这阻止恢复接口循环依赖,也避免把读下界变成自动接管。
report.head 通过当前实例认证,返回 scope, accepted_revision, report_digest, bindings;无报告用 revision=0 与 digest=null。A 本地持久报告与服务端相同则重发幂等;本地有已落盘且较新的报告则发送该报告。提交成功但回执丢失由同版本同摘要恢复。若本地状态丢失,先成功读取完整声明:与服务端摘要一致则保存并沿用服务端版本/绑定;内容不同则持久保存 server_revision+1 后发送。读取失败/不完整不能合成空报告。服务端恢复下界倒退、相同版本摘要不符或本地持久记录损坏报 state-inconsistent,停止该 scope 写入,不靠重登记规避。
2.3 一致快照与 ACK
Section titled “2.3 一致快照与 ACK”B 在一致数据库事务视图捕获资源/授权/监听/当前提议,持久分配目标+Network 的 snapshot_revision、内容摘要与 outbox;事务内不做网络 I/O。沿用现有 Network/authority 锁顺序,再锁报告/服务/端口行;跨多个身份锁按完整身份字节排序,仅用于避免死锁,不用于选择服务归属。重试整个事务,不能拼接多个独立读版本。
快照内容包含 scope, snapshot_revision, resources, listeners, grants, withdrawals。数组按完整资源引用/端点键排序。listeners 仅对发布端含后端信息;消费者只得 P、身份、协议和授权。纯租约刷新通过独立 lease 消息传递,不改声明/监听版本;若快照内容实际变化则增加快照版本,不能以相同 revision 替换内容。
A 校验完整快照后准备本地视图,撤销先禁授权,再原子切换有效服务视图。snapshot.ack 回显版本/摘要并分别给出 accepted, enforcement_applied, dns_applied 及错误;ACK 不是业务 ready。DNS 失败不撤销无关 membership。快照及其 ACK 不能单独延长使用租约。
聚合监听授权与单个来源授权使用不同摘要。ListenerIntent.enforcement_policy_digest 继续绑定完整服务授权集合(ns.service.authorization.v1),用于监听提议、执行 ACK 与 ready 证明;Grant.policy_digest 绑定完整 ServiceRef、发布者身份、消费者身份和该来源允许的 TCP/UDP(ns.service.source-authorization.v1)。两者不要求相等;变更另一消费者的授权必须更新聚合监听证明,但不能仅因聚合摘要改变就撤销仍获授权来源的旧流。
NSD 在同一权威事务中更新聚合监听与双方来源投影。只有发布者和消费者的前一快照连续持有完全一致的 Grant,且本次来源身份、协议、摘要仍相同、权威策略版本未倒退,才保留该 Grant 的原版本。任一侧曾缺失、撤销或改变则使用新的授权版本;即使重新授予的摘要相同,也不能复活旧流。A 将来源 Grant 与聚合 ready 证明分别核验,租约截止、实际安装和普通 Node L3 门禁仍独立生效。
A 在安装或 ACK 前,以完整目标身份+NSD/Network 为键原子持久快照版本/摘要及各服务已见监听版本/摘要下界;检查资源 N6 与完整发布者身份的派生结果一致。相同版本不同摘要、版本回退或任何条目不合法时整份拒绝,不部分推进下界。省略资源、完整空快照及撤销均不清空历史;撤销可推进监听下界,但不抹掉同版本已知摘要。runtime/session/authority epoch 改变不重置内容版本,下界文件损坏或已初始化后缺失时拒绝应用。持久化回执只证明配置可进入应用阶段,不恢复 Active、ACK 或租约;合法重新授权仍走新的租约流程。
3. 实例与会话栅栏
Section titled “3. 实例与会话栅栏”A 对每个完整节点身份持久维护 runtime_generation,每次启动 daemon/engine 前原子递增,并生成该次 runtime_id;所有 membership 的同一运行实例使用同一对值。NSD 在已认证身份和现有所有权约束内接受更高 generation 的新实例;同 generation 只接受相同 runtime_id,低 generation 拒绝。旧实例不能借重发 authentication 回到当前。对启用新契约的身份,所有能改变active_runtime的认证入口都须校验该代次,不能由旧入口省略代次绕过;这作为B的实现检查项。客户端持久代次丢失按§2.2的runtime.floor/recovery bind流程,先持久更高代次;旧实例仍有效时不得只凭floor自动抢占,不能循环自增抢占;已有 daemon 的机器互斥继续有效。
B 持久维护 authority_epoch(服务控制权威的启动/接管代次),提供新服务控制前原子递增。所有修改事务核对当前 epoch,旧运行制品失去写资格。NSD 重启后保留声明/端口/版本下界,旧 ready 只作为历史,不能从磁盘恢复为新可用授权;重新认证和 ready 后才发新使用租约。客户端已获准且未到期的旧租约不因服务端重启被凭空延长,收到更高 epoch 后拒绝旧 epoch 消息。
A 同 runtime 的连接尝试使用单调 session_seq;B 为每个 scope/runtime 持久保存已接受最高序号和该会话 request_id。新会话经过当前认证注册且提交后才可投递配置;低序号拒绝,同序号仅同一请求幂等。session_seq 耗尽须正常重启形成新runtime,不回绕。新会话只栅栏旧会话消息,不把未到期既有监听使用许可自动作废;重连重新核对实际监听,再报当前会话 ready。
报告和 report.head 不依赖配置流,验证当前 runtime/epoch;配置投递、snapshot ACK、ready、刷新及 P 响应另验证 session_seq。epoch/实例改变时旧会话无论序号多高都不得恢复。关联关系由认证和数据库确认,不信任客户端只填一个 UUID。
4. ready 状态机与租约
Section titled “4. ready 状态机与租约”4.1 发布端本地状态
Section titled “4.1 发布端本地状态”| 当前状态/事件 | 必须执行的动作 | 新状态/许可 |
|---|---|---|
| Blocked + 有效desired | 验证身份/本地声明floor/资源版本,进入§5端口仲裁 | Preparing;无业务许可 |
| Preparing + MayInstall持久成功 | 安装N6:P/本地转发与ACL,数据门保持关闭;核对实际状态 | InstalledClosed |
| InstalledClosed + 当前会话 | 发送listener.ready,记录该逻辑请求t0/seq | AwaitGrant;仍不允许业务 |
| AwaitGrant + 有效成功回执 | 计算本地截止,未过期才开放该服务数据门 | Active |
| Active + 10秒刷新点 | 检查实际监听/ACL仍符合意图,发送新刷新请求 | 保持Active到旧截止;发送不延长许可 |
| Active + 同实例断线 | 保留已有监听/流和原截止,不续期 | Active或到期Expired |
| Active + 同实例重连 | 新会话取得当前desired,核对实际状态,相同意图重发证明 | 不重装;仅有效新回执可续期 |
| 任意 + 意图变化 | 原ready不能确认新意图;先阻止受影响新流/撤销无效授权,执行更新并重报 | 新意图AwaitGrant;未受撤销的旧流按下述规则处理 |
| Active + 到期/撤销/本地floor不满足 | 同一事件先关该服务数据门,再取消相关TCP/UDP流与后端I/O | Expired/Revoked;无新流、无旧流转发 |
| 任意 + 新runtime/进程恢复 | 不恢复磁盘里的Active;检查持久端口归属,清理遗留数据门,重新安装 | InstalledClosed/AwaitGrant |
| 任意 + 数据面应用失败 | 保持门关闭,报告apply-failed;保留可能安装的P归属 | Failed;不得回退旧授权 |
到期和显式撤销:数据面授权在每个入/出转发动作检查本地截止/撤销代次,不能只依赖可能迟调度的后台定时器。截止后不再转发旧TCP/UDP;撤销TCP连接及后端任务,丢弃已排队的服务数据,删除UDP映射。对端可发送RST但RST成功不作为完成条件。控制状态/套接字回收验收目标为截止或已收到撤销后1秒内;若平台暂停/挂起,必须在恢复转发前完成门禁,不能先放流量再追赶定时器。先完成并定向验证该E2E依赖的取消/截止门能力,再运行对应E2E。执行职责按§4.5:ns持有服务许可,复用上游nsplane的公开接口,不修改nsplane;不得以mock、定时清理或绕过服务入口替代真实实现。
仅后端更新且授权未撤销时,已建立流继续绑定其原后端;不得因新后端成为ready就给旧流永久权限。每个服务Active租约仍约束其所有仍获授权旧流;只撤销某来源时先清理该来源的流,其他授权流保持。变更期间没有有效使用许可就关闭相关转发,不能靠“原连接已建立”绕过到期。
4.2 服务端 ready 状态
Section titled “4.2 服务端 ready 状态”| 状态/事件 | 校验与事务 | 结果 |
|---|---|---|
| DesiredPending + listener.ready | 当前epoch/runtime/session、精确ServiceRef/listener_revision/摘要/提议、当前声明/策略/P归属;B已收到该会话中ready所引用目标/Network/snapshot_revision/digest的enforcement_applied成功ACK,监听/授权摘要与该快照一致 | 提交grant后Ready;回复lease.result |
| Ready + lease.refresh | 同上述绑定,刷新序号更高且实际状态证明一致 | 产生新grant和截止;旧请求不产生第二个grant |
| 同逻辑请求重传 | request_id、seq、内容摘要全相同 | 返回原grant与原截止的当前剩余时间 |
| 低seq/同seq异内容/旧session | 不改变任何grant | stale-refresh / digest-conflict / stale-session |
| 意图、runtime或权威epoch改变 | 旧ready不能授权新配置,持久推进相关代次并发出撤销/新desired | DesiredPending;保留资源/P,不借恢复换ID |
| 撤销/租约截止 | 禁止新的consumer lease,发送撤销状态 | Revoked/Expired |
| 重连但runtime/监听意图相同 | 会话更新不恢复或延长旧grant;新会话核对证明 | 重新获准后Ready,无需重装 |
listener.ready额外携带其所依据的snapshot_revision/snapshot_digest。enforcement_applied表示监听/ACL与租约数据门已安装且无租约时关闭,不要求先获使用租约;DNS失败单独报告。快照ACK可复用已成功应用且当前服务意图仍相同的快照证明,无关服务更新不使本服务失效;若当前控制状态已撤销/变更该服务,则旧快照ACK不能作为ready前提。新会话先重新核对并ACK所依据快照再ready,避免旧会话回执越权或形成循环。
服务端Ready不等于端到端健康:消费者和发布端各自必须拿到有效使用许可。目录可显示pending;缺任一侧许可时不能以目录条目放行。短暂消息到达差异允许连接失败,不能以此延长或跳过租约。
4.3 新鲜度、逻辑幂等与单调截止
Section titled “4.3 新鲜度、逻辑幂等与单调截止”租约有效期上限30000ms,每10000ms尝试刷新;每服务/runtime持久刷新序号严格递增。新runtime可从1开始但旧runtime永远不能通过当前实例检查。每次逻辑ready/刷新包含 request_id, refresh_seq, listener_revision, intent_digest, proposal_id, snapshot_revision, snapshot_digest;传输重试沿用这些字段及原t0,但使用新的控制信封nonce,避免与现有信封防重放冲突。新逻辑刷新不重用旧序号或旧t0。
B 在当前ready权威epoch内以单调时间建立 deadline = first_commit_time + 30000ms(还须受有效政策授权截止约束);先提交 epoch, grant_id, service/runtime/session, seq, request_digest, deadline 再返回。重传只重算 remaining_ms = floor(max(0, deadline - reply_time)),不创建新截止。原请求映射与刷新高水位持久保存;旧epoch的单调截止绝不跨权威重启恢复。
A 在发送逻辑请求前记录包含系统休眠时间的单调时刻t0。仅验证成功回执后设 local_deadline = t0 + remaining_ms;接收时若已经到期或remaining超30000拒绝。网络延迟只能缩短本地许可,不能从收到回执时重新起算。相同逻辑回执至多应用一次;重复/迟到回执不重新计算更晚截止。仅更高刷新序号的新逻辑请求可产生后继许可,旧回执不能覆盖较新许可。刷新失败、回执丢失按原截止停止,不因每10秒定时发送而延命。
消费者收到推送只准备视图,应用后发送 lease.acquire(包含新request_id、consumer_seq、精确快照版本/摘要与服务监听引用),记录自己的t0。B 仅在该消费者授权与发布端Ready均有效时返回lease.result,remaining不得超过当前发布端grant、策略授权的剩余期限及30000ms。消费者同样以自己的t0+remaining设截止;每10秒主动续取,推送本身不授予期限。consumer_seq按目标scope/runtime持久递增,幂等/旧序号规则同刷新。多个服务可批量请求,逐服务返回结果,不能用一个成功项放行其他项。
两端都采用单调时钟,不要求外部同步壁钟;实现必须使用包含休眠的单调计时,或在睡眠/恢复时无条件使本地服务租约失效并先关门再恢复I/O。若底层无法保证单调时钟/休眠识别,则该平台不开放此能力。服务端时钟用于grant存活,节点计时可更保守;节点可能比服务端提前停流,这是允许的可用性代价,不能用延长租约“修复”。
lease.result 字段固定为 request_id, scope, authority_epoch, runtime_generation, runtime_id, session_seq, service_ref, listener_revision, intent_digest, grant_id, refresh_seq或consumer_seq, remaining_ms, result;消费者还绑定其snapshot_revision/digest。拒绝时grant为null、remaining=0。服务端响应认证使用当前控制信任链,不能只信任剩余毫秒数字。
4.4 权威重启与多实例
Section titled “4.4 权威重启与多实例”authority_epoch由一个持久ready权威/其租约持有者分配;普通API worker重启不自行更换全局epoch。首版可部署单一NSD权威;多实例API必须转发给同一有数据库栅栏的权威。接管先取得独占权威并持久增epoch,再接收证明;旧权威事务核对epoch失败,不能发新授权。没有该机制的多写者部署不通过验收。
新权威不恢复旧grant,保留资源/P/报告/快照/会话下界;客户端用新epoch重新注册当前会话和证明。已失联节点只能在旧本地许可剩余期限内运行,无法瞬时收到远程撤销;收到新epoch即撤下该scope旧epoch的使用许可再取得新的。重启不会重新给旧grant30秒,NSD也不把旧outbox中的旧epoch租约当成新授权。配置快照可重发,但使用许可必须重新获取。
4.5 两层服务许可执行(不修改 nsplane)
Section titled “4.5 两层服务许可执行(不修改 nsplane)”本轮使用 ns 持有的两层服务许可,不以发布 nsplane 租约扩展为前置;§3–§5 的线上协议、租约期限和 ready/P 语义保持不变。以正式 nsplane v0.14.2(64109b0b0ec58aed61d1a2ca6fdd903beabd4a0b)为基线,不消费未发布补丁、fork或临时路径依赖。接口可用性、组件测试与真实运行验收分别记录。
职责。 ns维护服务许可及流生命周期;nsplane继续负责WireGuard、TCP/IP栈、通用ACL/NAT与重组。使用其公开双向PacketFilter、IPv6 NetStack、TCP abort/terminated、Transport及适用的UDP/NAT清理接口,不假定存在通用TCP流删除或可克隆终止句柄;ns的适配层只判断服务归属、许可代次和截止,不复制通用ACL匹配器、TCP/IP栈或NAT。现有FlowGate/ACL缓存只构成已有策略检查,不能证明服务租约仍有效;服务检查是附加的必要条件,普通Node授权、回复许可或observe模式均不能越过它。两层检查必须同时通过,任一层拒绝均不转发。
服务所属 Network 的普通策略。 入站发布包、消费回包与显式输出都按原许可或持久端点携带的 ServiceRef 选择普通策略,NSD 命名空间必须与实际账户 membership 一致,不从其他 Network 借用 Node 授权、回复状态或待安装标记。启用 Enforce 的 Network 必须得到明确允许,未绑定报文的 Pass 不是许可;Observe、Disabled 或未配置 Node L3 保留原有模式语义。M6 ready/acquire 仍以对应服务快照应用 ACK 和服务租约为前置,不新增 Node L3 Enforce/ACK 前置。wire ACL、服务许可及最终发送检查仍独立执行。
端点归属。 发布端以完整本地身份+P登记ServiceRef,协议分别检查,P跨协议仍按§5独占。消费者将VIP:P和发布者N6:P关联同一资源及明确选择的NSD/Network;不同来源有歧义时关闭入口,不按路由加入次序或依次尝试router选择赢家。pending、到期、撤销与墓碑端点仍属于受管服务,拒绝后不得回落宿主L3、Exit、其他服务或membership。普通非服务端口继续原有L3行为。N6:P在发布端进入服务netstack并映射至私有后端B;本地VIP只是消费者呈现,不改变线上P/B职责。上游netstack每实例最多配置一个IPv6地址,多本地身份须隔离实例或使用经验证的既有地址适配,不能只采用配置列表第一项。
三个独立判断。 先以持久归属识别受管端点,再以当前许可决定能否转发,最后以终止回执确认能否释放流关联;三者不能互相替代。端点归属保留不表示继续授权,许可关闭不表示资源已经回收,流退役也不解除跨服务 P 墓碑。
| 入口/责任方 | 必须绑定的归属 | 拒绝后的行为 |
|---|---|---|
| 消费端 N6:P / VIP:P(A) | 同一 ServiceRef、完整发布者身份、明确选择的 NSD/Network;VIP 只属本机 | 拒绝新旧流,不尝试普通节点路由或其他来源 |
| 发布端 N6:P(A) | 完整本地身份 + P 对应唯一服务;认证消费者身份与该服务 Grant 匹配 | 不交给宿主普通端口;未获准协议也不回落 L3 |
| 服务意图与授权(B) | NSD 协调 P、版本、发布/消费许可及来源授权;后端 B 只下发发布端 | 撤销同时更新两端投影并拒绝新租约;不以 ACK 代替 ready/acquire |
第一层:连接及socket操作。 发布端仅从listener.ready/lease.refresh成功回执取得或刷新许可,消费者仅从lease.acquire成功回执取得许可;snapshot.ack只证明应用,不授予使用许可,DNS失败不阻止纯发布端ready。流固定ServiceRef、完整双方身份、所选membership、协议、许可代次及原后端绑定。拨号开始/完成、每次实际TCP读写poll及UDP发送前核对当前许可和包含休眠的时钟;等待队列或socket可写后重新核对,不能仅在创建future或入队时检查。旧流只能在持续有效的许可下保持原后端;无许可的间隙永久废止其代次,新grant不能复活旧句柄。
第二层:报文交付。 在认证后的入站交付、加密前出站,以及服务stack和宿主应用之间的实际交付边界检查同一许可;覆盖新流、旧流、TCP重传、双向缓存和UDP队列。排队数据绑定流/许可代次,出队重新检查,不能仅凭同五元组查到一个新许可。连接结束须等待上游终止并隔离残留队列后才释放关联;无安全关联的报文丢弃。分片关联包含认证来源、方向及许可代次,失效时清除相关重组/映射,旧碎片不能拼入新代次;解析与重组复用上游能力。ICMP关联不得恢复已失效服务流。PacketFilter的Handled短路和inject_*跳过过滤链必须逐入口审计;本机hairpin、TUN自地址返回和本地proxy也必须经过同一服务检查,不能因没有WireGuard peer而旁路。
最终发送队列。 加密前检查后仍可能经过引擎命令或输出队列,不能把加密完成当作已经交付。直连路径使用公开 inject_outbound_on 携带不可复用、绑定原流及许可代次的本地 Path 标记,在 ns 提供的 Transport 中、实际非阻塞 UDP 发送时再次检查;检查与发送之间不得再排入不受许可控制的队列。该接口跳过出站过滤链,调用前必须完成普通策略与服务检查;没有 WireGuard 会话时丢弃,不借普通握手队列暂存服务数据,握手仍走已有连接管理。标记不是公网地址,不发布为 peer endpoint。若服务业务可进入 Relay 路径,其实际发送队列也必须满足同一约束,不能以直连测试通过推定 Relay 数据路径安全;R1 控制设施依赖不变。
TCP 终止屏障。 正式 0.14.2 的 TcpConnection::abort(self) 消耗连接,不能在 abort 后对同一个句柄等待 terminated(&self);NetStackHandle::owns 消失也不证明队列清空。A 采用每流独立的上游 NetStack 及有界队列,所有输入、输出和最终发送路径绑定同一不可复活的 ServiceFlow;一个 daemon 共用栈容量限制。撤销先在转发串行切点关闭流许可,再取消后端任务、关闭私有 stack sink,并读取/丢弃 source 至 BrokenPipe/EOF;这同时证明该 driver 结束及其输出队列已隔离。返回的退役回执与原转发错误分开;任务被强制取消或回执缺失时,不得推断退役完成或释放五元组关联。新流必须使用新栈/队列,原输出和最终 Transport 标记不能借用新许可。验收分别覆盖真实栈积压输出下撤销、同五元组重用、其他流隔离及 L01–L07;组件结果不能替代完整运行证据。
撤销与恢复顺序。 同一服务/来源的许可废止与同步转发检查必须有明确串行切点,先废止代次、禁止转发,再取消拨号/双向任务、丢弃队列、abort TCP、删除UDP/NAT/分片关联;正常FIN排空不是撤销完成。只影响被撤销服务/来源,不能靠删除共享peer或停止后端冒充成功。后台到期驱动负责唤醒空闲流及资源回收,不能作为唯一安全门。仍保留§4.1的1秒回收目标,从本地收到经认证撤销或本地许可到期起算;NSD提交撤销至节点收到推送的传播时间单独记录,断连节点以原租约截止关门,不承诺即时远程撤销。挂起期间不要求任务执行,但恢复后首次转发必须先检查,恢复后清理另行计时并记录。已在合法转发切点交给外部系统的数据不能撤回,不得把ns仍持有的排队数据视为已交付来豁免检查。
休眠恢复判定。 同一 runtime 恢复时,在任何实际 I/O 前读取包含休眠的时钟:尚未越过原截止、许可未被废止且其余绑定仍有效,才可使用剩余许可;越过截止则永久关闭旧流代次,先关门再回收,取得新许可只能建立新流。控制重连、DNS 刷新、旧成功回执均不重置原截止。进程重启不属于同 runtime 恢复,磁盘中的 Active 不恢复为许可。本轮恢复验收使用指定测试节点进程组的 cgroup.freeze,分别记录原截止前及原截止后解冻的场景;另以保留身份和数据卷的 docker stop/start 验证进程重启。确定性时钟测试继续覆盖计时与异常边界。真实整机睡眠未覆盖,作为平台验证边界单列,不阻断本轮 M6 收尾;不得把进程冻结称为实际操作系统休眠。
时钟。 两层消费相同的持续计时与许可代次:Linux/Android CLOCK_BOOTTIME、macOS mach_continuous_time、Windows QueryInterruptTimePrecise。时钟读取失败/回退永久关闭该运行实例的许可,重启不恢复磁盘Active。尚无可靠时钟的目标明确不开放此能力;本轮不以普通Instant或后台定时器替代,也不新增平台支持承诺。恢复后先判定旧截止,重连、新消息或DNS刷新不能重新起算。
复核与分工。 B已定向核对本节与ready/ACK/租约、P墓碑和NSD撤销生产的兼容性;来源级授权变化时,B须更新受影响发布端与消费者的完整快照,并拒绝新的消费者租约。沿现有含消费者身份的Grant与快照表达,不新增wire类型或把R2带入M6。A将撤销同步到对应本地许可代次并拒绝迟到成功回执,承担双层接线、归属、取消、时钟和负例证据。B的生产投影及A的消费均须实际验证,设计核对不等于实现通过。进入完整E2E前必须通过:
| 编号 | 条件 | 验收结果 |
|---|---|---|
| L01 | N6:P/VIP:P × TCP/UDP,服务pending/Active/到期/撤销;另一服务及普通L3同时工作 | Active访问同一B,其余状态拒绝;无L3/Exit/跨来源回落,独立授权不受影响;P/B不同的映射实际取证 |
| L02 | 建立双向流,延迟清理任务,分别让发布/消费许可到期;制造满队列、部分写、拨号中、TCP重传 | 两端各自许可均是必要条件;首个截止后的转发动作即拒绝,旧缓存/队列不得继续;无需等待新控制消息 |
| L03 | 撤销一个服务或来源,保持后端和共享peer在线;延迟栈driver验证终止屏障 | 先禁转发后取消,从本地认证撤销到达/许可到期起1秒内回收;单列NSD提交至推送到达时间,断连按原截止关门;TCP终止与旧输出隔离分别取证,UDP/NAT/分片关联清除;另一服务/来源的原 TCP 保持,认证更新的有界 Hold 窗口允许短暂丢包且不排队,收敛后 N6/VIP × TCP/UDP 严格通过,不宣称 UDP 零中断;故障注入超出回收目标须记录失败,不能因已禁转发而算回收通过 |
| L04 | 失效后重授、同五元组重用、旧碎片/队列迟到、同一ServiceRef重授后的旧epoch回执重放;分别暂停明文和加密后输出队列 | 旧代次永久无效;新流独立获准;不能以新许可解释旧排队数据或恢复旧连接;最终Transport发送拒绝旧标记,栈输出旧关联未安全退役前不允许五元组复用 |
| L05 | 暂停到期驱动;实际冻结指定测试节点的进程组,分别在原截止前/后解冻并恢复 I/O;注入包含休眠的时钟、读取失败/回退 | 截止前仅在原许可与全部绑定仍有效时使用剩余期限;越过截止后的首次转发拒绝,旧流永久无效;后台未先运行也成立;无时钟回退续命。记录 cgroup.events 的 frozen=1/0、原截止、运行实例和重授顺序;冻结期间无响应不算租约屏障证据。整机睡眠另列未覆盖,不阻断本轮 |
| L06 | 同runtime重连、后端更新、进程重启、两端独立刷新失败;无关服务快照更新 | 重连保留原截止;获授权旧流保持原B,新流新B;重启关闭;只发送刷新不延命;同会话中服务意图未变且仍获授权时可复用有效ACK,不能因无关更新误拒;旧会话/epoch ACK或服务已撤销/变更时拒绝 |
| L07 | 回环、TUN自地址返回、alias入口、本地proxy、PacketFilter::Handled短路、各inject_*入口、扩展头/分片及ICMP关联 | 每条可达服务路径有归属和检查证据;未知/失效关联拒绝,不以只测小报文或直连入口代替覆盖 |
核对交付。 A 提供端点到 ServiceRef/来源的归属表、各实际 I/O 与最终队列切点清单,以及 L01–L07 逐项证据;B 核对发布端 ready/refresh 与消费者 acquire 的许可来源、来源级撤销双端投影、旧会话/epoch 回执拒绝,以及 R1 名单/目录前置。每项分别标记设计核对、组件测试、生产接线和真实 E2E;不得互相代替。终止屏障仍须证明栈释放和旧输出隔离两件事,公开接口的源码可行性不是该项通过结论。未完成项保留验收门,设计核对结果不等同于实现验收。
实施顺序:上游版本与消费闭包验证 → 端点归属和许可对象 → 双层检查与取消/终止屏障 → L01–L07 → R1真实名单/目录前置 → 真实NSD+两个ns的N6/VIP业务直连E2E。发现公开接口确实无法完成某个切点时,提交具体复现与最小缺口;不得悄悄放宽验收或重新引入未发布依赖。
5. P 状态机、持久顺序与裁决
Section titled “5. P 状态机、持久顺序与裁决”5.1 双端持久记录
Section titled “5.1 双端持久记录”B 每服务保存 listener_revision, intent_digest, allocation_round, proposal_seq, proposal_id, P, candidate_index, tried_ports, state;只有一个未终止的当前提议。端口表以发布者完整身份+P为键保存归属/墓碑,覆盖该NSD可见所有Network。A 使用相同端点键保存跨membership的提议集合、确认ServiceRef、安装意图、终止下界与状态,不能按来源各维护一份互不知晓的表。
proposal_seq在ServiceRef内持续递增,不因round/runtime/重启重置。proposal_id为16字节随机ID;重复ID必须对应完全相同字段。所有结果/取消/ready同时回显完整资源、listener_revision、intent_digest、round、proposal_seq/id和P。版本或内容不匹配不改变当前状态。跨NSD拒绝只返回不可用类别,不返回竞争者身份、Network或端口清单。
| A状态 | 进入与动作 | 后续 |
|---|---|---|
| Proposed | 收到有效提议,先持久登记;无任何监听副作用 | 仲裁确认或TerminatedPure |
| MayInstall | 在端点串行事务中确认归属并写安装意图,提交同步持久化 | 才允许安装监听;同资源重试不新分配 |
| InstalledClosed / Active | 按§4安装与取得ready租约 | 更新/重启保留归属;撤销转Tombstoned |
| TerminatedPure | 事务确认从未进入MayInstall、从未开放并记录永久提议终止下界 | 可出具纯提议终止证明;迟到配置不得激活 |
| Tombstoned | MayInstall及之后弃用、删除,或恢复无法证明从未进入MayInstall | 不跨ServiceRef复用,不因TTL/ACK/重启清除 |
B 状态为 Offered → Claimed/Ready 或 Terminating → ReleasedPure/Tombstoned;超时未得到确证进入 RecoveryRequired,保持P预留,查询/取消原提议,不能另发下一个候选假定原提议没有安装。收到A的MayInstall/installed/ready任一确证就保持原资源归属;B还没收到回执不削弱A的持久归属。
5.2 可执行裁决切点
Section titled “5.2 可执行裁决切点”A 对 (完整发布身份,P) 使用串行存储事务;处理输入先持久登记Proposed。确认事务在锁内读取已登记的全部有效提议:
- 已有已确认ServiceRef:只允许该资源的更新/恢复,其他资源拒绝。
- 无已确认归属,只有一个不同ServiceRef:同资源重复幂等,原子确认归属+MayInstall后才执行安装。
- 无已确认归属,有两个或更多不同ServiceRef:全部仍为纯提议的竞争者原子TerminatedPure,分别给出不可用证明;不按来源、排序或数组位置挑一个。
这个确认事务是唯一裁决切点;在切点之后登记的提议不能抢占已确认归属。这里不承诺预知未来网络消息,也不承诺“未来出现的冲突能推翻既有确认”。该界定同时满足已知未确认冲突不选先到者、已确认P保持稳定。事务提交后即使安装尚未开始,归属也不回到未确认集合。
本地普通配置/系统监听冲突同样阻止MayInstall;只有同一ServiceRef的既有本产品归属允许恢复。内核绑定等外部资源不属于本地事务:因此必须先MayInstall再尝试,绑定失败也保留该资源归属/墓碑,不把外部操作失败误认为纯提议。
5.3 终止证明与恢复
Section titled “5.3 终止证明与恢复”B已持久Offered但发送前崩溃时,A可能查无Proposed;port.query返回不存在不是释放证明。B须发经当前认证且完整字段匹配的port.cancel;A在确认无MayInstall/该提议归属证据后,先持久写该proposal_id/seq的TerminatedPure终止栅栏,再出具证明。若A存储损坏/不完整则不能证明“从未存在”,转RecoveryRequired。这样随后到达的原快照也会被终止栅栏拒绝;不能仅因A曾回答不存在就释放预留。
port.result(result=terminated-pure) 必须由A在终止事务持久完成后发出,绑定上述全部提议字段以及本地终止下界。B仅在精确匹配、A证明未进入MayInstall、且B确认从未向消费者开放该提议时,释放这次纯候选预留。B同时持久记录终止下界,禁止旧outbox重发使其复活。释放端口预留不等于删除提议安全记录。
以下一律不能作为纯提议证明:超时、未收到ready、没有业务流量、NSD重启、A状态文件丢失/损坏、MayInstall后安装失败。只要进入MayInstall,后续取消即转墓碑;安装前崩溃也如此。A的安装意图事务必须在调用任何可安装监听/改变转发的API之前完成同步提交。
A恢复顺序:加载归属/提议下界 → 保持服务数据门关闭 → 撤除无有效本地租约的遗留监听/流 → 向B核对当前声明/提议 → 同资源重新安装和ready。B恢复保留预留/墓碑/预算,重新关联A持久结果,不能因B的Ready为历史而删除P。终止序号连续下界可压缩历史记录,但只有更低全部提议已终止/归属已永久保留时才能推进,禁止留下可被重放的空洞。
5.4 候选、重试及新轮
Section titled “5.4 候选、重试及新轮”- fixed只尝试指定P,不自动改配。auto首选可用后端端口,再从闭区间40000..50000内随机选本NSD可见未用、非墓碑、非本轮tried_ports的端口;普通PRNG足够,唯一性靠事务。候选池耗尽明确port-pool-exhausted,不盲试。
- 每轮最多8个已发出的不同候选,包含首次;候选、序号、tried_ports与计数在发出前同事务持久化。明确纯拒绝后才允许换候选;不确定是否安装则RecoveryRequired,不能消耗更多端口推进。
- 相同逻辑消息每次RPC超时2秒,最多3次传输尝试,沿用request_id/提议和原t0,信封nonce每次新建。三次仍不确定进入RecoveryRequired并停止自动重试;重新连通可查询原状态,不自动创建新round。
- 每次明确候选拒绝后,下一候选退避均匀随机于
[0,min(2000,100×2^(candidate_index-1))]毫秒;退避不保证成功,状态/预算持久。发送时刻不需跨重启精确恢复,重启后重采退避,但不能重置已尝试集合。 - 第8候选明确失败后持久Exhausted;无定时器自动开新轮。仅两类事件开启新round:本地所有者显式
service.retry-port(ServiceRef, request_id);或声明发生后端host/port、协议、fixed/auto或固定P的实质变更。显示名/描述变化、重报/重连/epoch变化、普通权限续租、进程重启不重开。 - 新轮先确保旧提议已持久终止/原P归属已安全保留;round+1且proposal_seq继续增加,新的tried_ports为空,但全局归属/墓碑仍参与排除。手工重复同request_id幂等,不连续开多轮。明确重试接口由A提供CLI/FFI,权限按本地所有者规则;B协调持久round。
- 同服务有已确认P且未弃用时,正常重启/恢复或后端更新优先沿用P,不因开启一个无关重试流程重新分配。P确需变更先禁旧新流并完成相应撤销,旧P留墓碑后再使新P可用。
6. 消息、错误与持久恢复矩阵
Section titled “6. 消息、错误与持久恢复矩阵”| kind | 方向与必要body | 成功/失败效应 |
|---|---|---|
| runtime.floor / runtime.bind / session.open | A→B:floor以身份/所有权认证读下界;bind带实例代次/ID及必要的恢复证明;session.open另带递增session_seq | 原子更新栅栏,回复当前epoch与对应下界;不授予服务租约 |
| report.head / report.replace | A→B:scope;replace另带report_revision、digest、完整声明 | §2恢复/替换;accepted给出原子结果 |
| snapshot.push / snapshot.ack | B→A完整配置;A→B精确revision/digest与分阶段结果 | 只配置/准备;不以ACK替代ready或使用租约 |
| listener.ready / lease.refresh | 发布端A→B:精确监听/提议、刷新序号、实际应用证明 | 成功回lease.result;本地收到有效结果才放行/续期 |
| lease.acquire | 消费端A→B:consumer_seq、已应用快照、服务引用列表 | 按项核对授权/发布ready,回复受限remaining |
| port.result | A→B:提议全绑定、claimed/installed/terminated-pure/apply-failed/conflict | 更新对应提议,不能改变其他提议或凭超时释放 |
| port.cancel / port.query | B→A:精确提议 | A持久终止或返回可能安装/归属状态;不可把取消请求本身当作终止证明 |
| service.retry-port | 本地所有者→A→B:ServiceRef、request_id;CLI 使用 ns service retry-port <ServiceRef> --request-id <UUID>,完整引用为三个规范 UUID,请求 ID 在结果不确定后复用 | 旧提议可终止时幂等开启新round;否则返回recovery-required |
错误码为稳定小写字符串:unsupported-version, unsupported-field, invalid-encoding, unauthorized, stale-runtime, stale-epoch, stale-session, stale-report, stale-snapshot, stale-listener, stale-proposal, stale-refresh, enforcement-not-applied, runtime-recovery-required, digest-conflict, state-inconsistent, port-unavailable, port-pool-exhausted, allocation-exhausted, recovery-required, apply-failed, lease-expired, storage-unavailable, counter-exhausted。错误不能泄露其他membership;详尽本地诊断仅由本地所有者查看。版本/摘要/状态冲突不自动重试新内容;storage-unavailable可重试同逻辑请求,但不能越过持久提交门。
| 故障点 | 恢复结论 |
|---|---|
| A报告落盘前崩溃 | 未发送,无新报告;重读完整配置正常生成版本 |
| B报告提交后回执丢失 | 重试原逻辑报告或report.head,返回同版本结果,不再删建资源 |
| A Proposed已落盘、MayInstall前崩溃 | 重新仲裁或持久终止;未经终止证明B不释放 |
| A MayInstall已落盘、安装前/后崩溃 | 可能安装,保留归属;同资源恢复或墓碑,绝不纯释放 |
| A终止事务提交后回执丢失 | 重新发送同提议的纯终止证明;迟到快照被下界拒绝 |
| B grant提交后回执丢失 | 相同逻辑请求同grant/原截止,remaining递减;不能生成新30秒 |
| B权威重启 | epoch递增,旧grant不恢复;资源/端口/计数保留;节点至多用旧本地剩余许可 |
| 消费者进程重启/单调锚点丢失 | 不恢复活动租约,重取配置与lease;名称/资源绑定可恢复 |
| A/B持久状态损坏 | 阻止该scope新授权/写入,报告state-inconsistent;不能靠清库/重新登记验收 |
7. 配套本地契约与 R1 验收入口
Section titled “7. 配套本地契约与 R1 验收入口”Short DNS格式及本地权威见 Quick 本地名字 和 账户命名;节点统一自动名固定两字母三数字,达到当前阶段80%后新分配追加[a-z0-9]。追加字符表固定为 0123456789abcdefghijklmnopqrstuvwxyz,保留前导零。本地事务保存唯一绑定/墓碑/阶段/计数;阶段只增。普通随机每次最多32次候选尝试,随后按候选编号升序执行最多4096项空闲查找;仍找不到返回可重试的name-allocation-busy,低于80%不擅自扩长。不持久PRNG状态;扫描从持久scan_hint继续并在事务内推进,scan_hint仅为有预算查找的进度,不是仿射生成器游标,不影响已分配名字。达到k=8阈值返回name-capacity-limit。计数按完整候选空间去重统计自动名、同格式手工占用及墓碑。
本地名字、选择器和服务绑定均用版本化事务表;默认不做墓碑GC。手工昵称冲突拒绝,不改自动名。显式转绑在同事务结束旧绑定、建立新绑定并清本地正负缓存;它明确表示所有者把名字指向新目标,不保证清除外部应用缓存。旧VIP/资源权限不能随昵称转移;新服务需自己的VIP/授权。TTL正向<=60秒且<=相关使用许可剩余期限,负向<=5秒;请求受管名不回落公网。多来源选择持久保存,选择失效停止有歧义入口,不自动选另一来源。
R1采用以下链路:NSD生成准入快照→受控共享卷→relayd snapshot;运营者可信目录配置→NSD认证投影→两个ns。最小设置:NSD名单每1000ms刷新、relayd每500ms读取、快照max_age=5000ms;这些符合当前源码配置区间,实际验收仍需确认生效。NSD有权写、relayd仅能读可信输入,运行时路径/身份/凭据由环境注入,不写入Git。失败刷新不延长旧名单,坏文件/过期拒绝,合法恢复重新登记。
B负责制品/名单/目录及服务端拒绝恢复证据,A负责目录消费/登记/节点记录/基础承载与业务直连证据。先验收未授权拒绝、停止刷新过期、撤销恢复、两个节点实际登记/记录取得/Relay基础通信;再验收N6:P和VIP:P的服务TCP/UDP完整nonce载荷与实际直连路径。真实NSD授权不可由fixture、手工名单、open relay或伪造服务目录替代。仅验收该共享卷部署,不推导公网自动发现/跨主机名单传输/R2/完整R1生产门。
8. 配置流的服务会话绑定
Section titled “8. 配置流的服务会话绑定”session.open 经 unary 接口持久接受后,实际 config.stream 的签名请求增加
可选 service_control 对象:nsd_id, network_id, runtime_generation, runtime_id, authority_epoch, session_seq, session_request_id。四个 ID 字段是 CBOR 16 字节串,
三个计数为 u64;session_request_id 必须是该次已接受 session.open 的 request_id。
服务端核对现有签名身份、Network 和完整持久会话元组,缺少或不匹配时不投递服务配置。
每次实际连接尝试先持久预留新 session_seq 并成功 open,不复用已尝试连接的会话。
服务事件名与外层 ControlEnvelope.api 都是 service.control.v1,SSE data 沿用
URL-safe 无 padding Base64 的完整签名信封;信封 payload 直接编码共享 Message,
不经过旧配置 JSON 包装。外层 machine_id 与内层 subject_identity 都绑定接收者;
内层 NSD、Network、runtime、epoch、session 必须精确匹配该流。目前此流只接受
SnapshotPush,RPC 回执不得作为配置事件注入。客户端重连保留运行期防重放缓存;
快照版本下界和 ready 的重连规则仍按前文执行。
9. 机器认证的服务运行实例绑定
Section titled “9. 机器认证的服务运行实例绑定”Node registration independently advertises node_local_names.v1 before runtime binding, including signed metadata refresh. NSD must not allocate or require an old node label for these requests; the old registration DNS authority tuple is absent, while internal Network tokens remain for existing Subnet consumers. An explicit legacy requested_hostname is rejected rather than silently ignored; local manual nicknames use the existing node-name operation. This capability is not a service permission and cannot substitute for the binding below.
签名发现后先完成 runtime.bind 并持久记录返回的 epoch,再执行 machine.auth,
最后 session.open 和实际 config.stream;启动代次丢失时,在 bind 前按 §2.2 读 floor
并完成恢复前置。machine.auth 不是隐式 runtime.bind。
MachineAuthRequest.service_control 的精确对象为 nsd_id, subject_identity, runtime_generation, runtime_id, authority_epoch:NSD/runtime ID 是 16 字节 CBOR 串,
subject_identity 是 32 字节串,两个计数是 u64;Network 沿用顶层 network_id。
顶层原有 runtime_id 必须等于该二进制 runtime ID 的小写 hex,用于对齐既有 presence
与 disconnect 栅栏。客户端拒绝跨身份/权威、低代次/epoch、同代次不同 ID 的绑定,并拒绝
尚未 bind 就以 service_control.v1 能力发送认证。
B 在修改 active_runtime_id 的同一数据库事务核对签名公钥、上述对象和持久绑定,
沿用权威行及机器行锁顺序。身份一旦存在 M6 runtime,缺失对象或任一字段不符的旧入口
不能覆盖当前实例;客户端收到认证失败不得回退为省略 M6 对象的请求。新实例依然须先
通过 runtime.bind 代次与恢复规则,不能凭 machine.auth 抢占。
9.1 无 Service 声明的运行实例心跳
Section titled “9.1 无 Service 声明的运行实例心跳”machine.heartbeat 可携带与 machine.auth 完全相同的 service_control CBOR 对象:
nsd_id, subject_identity, runtime_generation, runtime_id, authority_epoch。二进制字段仍为
16/32/16 字节串,generation 与 epoch 为非零 u64;不得改用文本或整数数组。
已绑定的 NS 来源每 10 秒通过既有签名信封发送当前绑定,Network 由已认证机器的来源确定;
不因一次心跳创建新代次、epoch 或 runtime ID。
NSD 在同一事务内核对信封签名身份、当前 Network/机器来源、持久 runtime 绑定、 活动实例与权威 epoch,以及权威和来源尚未过期、未停止且无需恢复。全部匹配时, 只把该来源的活性截止延长至不早于本次服务端时刻加 30 秒,并提交普通机器在线观察。 任一栅栏失败,来源活性和普通机器观察均不提交;过期实例不能凭迟到心跳复活, 客户端也不得降级为省略绑定来恢复该来源。
此续期只证明当前运行实例仍在线,不生成服务 Grant、不续用服务租约、不扩大策略权限, 也不要求声明虚构 Service。未携带对象的普通心跳保留原有每 60 秒发送及 300 秒在线观察语义, 不能续期上述 30 秒运行实例;服务授权仍须遵守各自 ready/lease 及本地截止规则。
10. 本轮恢复验收边界
Section titled “10. 本轮恢复验收边界”本轮 Linux TUN 验收使用指定测试节点的 cgroup.freeze 与保留身份、名字表和数据卷的 stop/start。分别冻结至原租约截止前、截止后,记录实际冻结和解冻完成,并保持后端及其他节点运行。截止后解冻先隔离控制重授,验证首次 I/O 拒绝;取得新 ready/租约后新连接恢复,原 TCP 不能复活。冻结期间无响应不算租约到期证明。真实整机睡眠单列未覆盖的平台运行门,不作为本轮 M6 收尾阻断;包含睡眠的截止时钟、异常时关闭许可与恢复前检查要求不变。