跳转到内容

NS · 应用通道

状态:通用应用通道与文件应用已接入,验收按应用和候选分别记录(2026-10-25)。 confirm 与 Quick 配对(kind = "pairing")保持既有实现;peer、relay PAKE、隧道内 PAKE 和受限 IPC 已作为应用会话基础接通。文件发送本轮范围已完成,C05 按用户决定延期不阻塞本轮,Windows 原生验证保留为发布门;固定候选与证据边界见文件发送和应用模式,不由单个应用的验收推断所有应用、平台或发布均已完成。应用通道是 ns 内部的通用层:Quick 配对、文件发送以及之后的语音、聊天等应用都运行在它上面。通道的两端都是 ns;relay 只看到会合协议的不透明消息,nsplane 只看到对端与端口。本页取代会合协议原 §9 的逐应用映射、Quick 协议 v2 原 §10.2 的配对载荷,以及文件发送原 §4.2 与 §5.1 的会合与密钥协商。协议引用(§n)除非另有说明均指本页。

目标推论
新应用只声明参数应用给出一份 profile(§3);会合、引入、身份交换、PSK、对端安装与隧道入口全部复用
一套引入流程,三种鉴权人工核对节点 ID(confirm)、一次性口令 PAKE(pake)、已有对端(peer),覆盖配对、票据与已配对设备之间的应用
应用之间互相隔离kind 进入每个签名与每次密钥派生,一个应用的消息不能在另一个应用里重放
relay 不感知通道只使用会合协议的信箱;不新增 relay 操作、报文类型或白名单
数据只走隧道信箱只承载引入所需的小消息;应用数据始终在 WireGuard 隧道内(会合协议 §10)

非目标:多于两方的会话、离线投递、按名字或身份查找对方、隧道内应用协议本身(各应用自己定义)。

层组件职责
应用ns:pairing、transfer,之后的语音、聊天;第三方应用经 ns 的应用接口声明 profile;命令、确认提示与界面;隧道内的应用协议
应用通道ns本页:profile、引入、身份交换、密钥派生、对端安装、隧道内入口
会合relay 的 /rendezvous/v1;消息类型在 nsshared信箱、短码、接纳、队列、上限,与应用无关
数据面nsplane安装与移除对端、隧道内连接、按来源命名空间的 ACL 与 pinhole

通道只在 ns 中实现,不进入 nsshared:两端都是 ns,第三方应用也经 ns 的应用接口使用它,不自行实现本页的协议。

AppProfile {
kind [a-z0-9-]{1,32}(会合协议 §5.1)
auth confirm | pake | peer
lifetime permanent | session | none
ttl 信箱 TTL 的默认值与上限,≤ 900 s
app_port 隧道内 TCP 端口;null 表示没有隧道内阶段
}
auth信箱用法适用场景lifetime
confirmadmission: owner,candidates 1–4双方核对对方节点 ID 后建立长期关系permanent
pakeadmission: first,candidates: 1经信箱会合,持有一次性口令即授权一次会话session
peer不使用信箱两端已是 ready 对端;可在隧道内追加 PAKE 鉴权(§5.2),不改变 peer / none 归属none

内置 profile:

应用kindauthlifetimettlapp_port
Quick 配对pairingconfirmpermanent默认 600 s,最多 900 snull
文件发送(relay 票据)transferpakesession600 s47901
文件发送(已有对端、对端票据)transferpeer;对端票据另在隧道内运行 pakenone—47901
语音、聊天待定peer 或 pakenone 或 session—待定

其他组合不允许:confirm 总是建立长期对端,pake 从不写入允许名单,peer 从不安装对端。

lifetime 描述对端安装归属,不是文件会话的完整计时规则。内置 transfer 的邀请期限与认证后文件阶段分别遵守文件发送 §5.6;已有 peer 的 none 不表示无限寿命。该规则不修改 CPace、HELLO/FINISH bytes、通用第三方 IPC 或其它应用的期限。

所有消息都是规范 CBOR 定长数组(Quick 协议 v2 §12.1),放在信箱的 intro 与 data 里;解码器在任何签名、MAC 或 AEAD 校验之前拒绝不规范的输入、多余字节、未知版本与未知类型。

// confirm:信箱 intro
Offer = [ver = 1, kind text, record_o, e_o bytes32, authority text, expires u64, name text|null, sig_o]
sig_o = Ed25519_Io("ns/channel-offer/v1" ‖ CBOR(前七个字段))
Answer = [ver = 1, kind text, record_p, e_p bytes32, mailbox_id bytes16, offer_hash bytes32, name text|null, sig_p]
sig_p = Ed25519_Ip("ns/channel-answer/v1" ‖ CBOR(前七个字段))
// pake:信箱 open intro;join intro 为空,Share 在接纳后经 data 交换
PakeDesc = [ver = 1, kind text, suite = 1]
// 两种模式:信箱 data
Message = [ver = 1, type u8, body]
1 ACCEPT [answer_hash bytes32, sig] // confirm;sig = Ed25519_Io("ns/channel-accept/v1" ‖ offer_hash ‖ answer_hash)
2 COMPLETE []
3 HELLO [ciphertext bytes] // pake;AEAD(k_hello_dir, nonce = 0, Hello)
4 APP [payload bytes] // 应用自己的小消息,通道建立后才允许
5..8 PAKE 的 PARAMS / NONCE / SHARE / FINISH,见精确 profile
Hello = [ver = 1, record, endpoint text|null, not_after u64, sig]
sig = Ed25519_I("ns/channel-hello/v1" ‖ kind ‖ th ‖ CBOR(前四个字段))
offer_hash = SHA-256(CBOR(Offer)),answer_hash = SHA-256(CBOR(Answer))
th = SHA-256(CBOR([C bytes, Y_o bytes32, Y_p bytes32])) // C 定义见 PAKE 精确 profile
  • record 是发送方当前的签名 NodeRecord(Quick 协议 v2 §3);接收方按其有效性与 §3.3 的代际 / 冲突规则处理。
  • 每个消息中的 kind 必须等于信箱的 kind 与本地 profile 的 kind,否则丢弃并中止通道。
  • name 是对方给出的显示名,只用于界面与默认昵称,从不参与授权。
  • APP 受信箱的 8 KiB data 上限约束;大于此的数据走隧道。
confirm: ikm = X25519(e_o, e_p)(全零结果被拒绝),t = offer_hash ‖ answer_hash
pake: ikm = K_pake(CPace,§5.2), t = th
K_ch = HKDF-SHA256(salt = 空, ikm, info = "ns/channel-key/v1" ‖ kind ‖ t)
psk = HKDF-Expand(K_ch, "ns/channel-psk/v1" ‖ I_o ‖ I_p, 32) // WireGuard PSK
k_hello_o = HKDF-Expand(K_ch, "ns/channel-hello-o/v1", 32) // 创建方发出的 HELLO
k_hello_p = HKDF-Expand(K_ch, "ns/channel-hello-p/v1", 32) // 加入方发出的 HELLO
K_confirm = HKDF-Expand(K_ch, "ns/channel-confirm/v1", 32) // 隧道内 OPEN 的 MAC(§7)

PAKE 的具体 KDF 输出长度、AAD 与完成确认见精确 profile §2–3。o 指信箱创建方,p 指被接纳的加入方。AEAD 为 ChaCha20-Poly1305;每个 HELLO 密钥只加密一条消息,因此 nonce 固定为 0。临时密钥 e_o、e_p、CPace 秘密与 K_ch 在通道结束后丢弃;psk 只交给对端安装(§6)。

  1. 创建。 创建方 open {kind, admission: owner, candidates, ttl, intro: Offer};authority 是所拨 relay 的标识,expires = now + ttl。用户分享短码(relay 短码 + 8 位数字,会合协议 §5.4)。
  2. 查看。 加入方 lookup 得到 Offer 并校验:签名与记录有效、record_o.I 与签名者一致、authority 等于所拨 relay、now < expires、对方不在本机移除记录中、地址组不与自己或已有对端冲突。界面显示对方的节点 ID 与 name,用户确认;应用可以提供”指定节点 ID 时自动确认、不匹配时失败”的参数。
  3. 加入。 加入方 join {id, intro: Answer}。
  4. 接纳。 创建方从 recv 的 waiting 读取每个 Answer,做与第 2 步相同的校验,并检查 mailbox_id 与 offer_hash。用户 admit 一个、reject 其余(同样可以按指定节点 ID 自动接纳),随后发送 ACCEPT。
  5. 安装。 创建方发送 ACCEPT 后、加入方校验 ACCEPT 后,各自派生 psk 并按 lifetime 安装对端(§6)。
  6. 结束。 加入方就绪后发送 COMPLETE 并 close;创建方收到 COMPLETE 后 close,或在自己就绪 30 s 后关闭。

本模式严格采用PAKE 精确 profile:固定 CPace suite、字节编码、时序、两方向完成确认、失败预算和秘密清除。先取得信箱 ID,接纳后交换 nonce 和 Share,加入方先发送 HELLO,创建方验证后才回复;两端完成认证确认后才允许安装/交付。短码最多三次失败尝试,重试刷新全部临时秘密,不复用 Share。

隧道内模式在双向 peer OPEN(mac=null)后由连接方发送 LOCATE,在已唯一确定的本地授权来源内定位邀请,再运行同一 profile。定位值精确等于 app_context,并纳入握手绑定,不能作为来源或授权证明;不同邀请可隔离并发,同一邀请的 attempt 与失败预算统一管理,见邀请定位。握手还绑定 channel_id 与承载流的已认证身份,只增加应用口令门,不安装新 peer、不更换既有 PSK。

两端已是 ready 对端(已配对,或同一账户内由 NSD 投影)。不使用会合与信箱:发起方直接在隧道内连接对方的 app_port 并发送 OPEN(§7),身份就是承载这条流的 WireGuard 对端。接纳必须同时通过本地 app grant、精确选定来源的有效策略和应用 OPEN 验证;pinhole 只是端口原语,普通宽 ACL 不能绕过应用门,见来源与会话授权。

lifetime写入AllowedIPs入站过滤生命周期
permanentQuick 来源的 Allow {I, added_by: kind, psk} 与对方记录(Quick 协议 v2 §10.1)N6/128 + N4/128发往本机的任意流量直到移除
session仅限本次会话的对端:不进允许名单,不分配别名或名字,不留下移除记录只有 N6/128只接受 app_port 上来自 N6(peer) 的 TCP 及其返回流,加上隧道内控制端口 47900直到会话完成、失败或过期;自创建起 ≤ ttl,加上应用报告有进展的时间,最长 24 h
none不安装;使用已有对端—对端来源命名空间允许的 app_port pinhole,随会话关闭随会话
  • 已存在的长期对端被同一身份重新 confirm 时,只替换 PSK 与记录,保留别名与昵称。
  • 从安装起 5 s 内 WireGuard 路径可用并完成本地发布,否则报告 prepare_timed_out;长期对端保留供正常重连,会话对端删除。
  • 仅限本次会话的对端计入每个网络 1 024 个对端的上限,另有每个节点默认最多 8 个并发的单独上限;超过任一上限时,通道在会合之前以容量错误失败。
  • 两台设备之后经 confirm 建立长期关系时,先删除双方之间的会话对端;会话 PSK 从不复用。
  • 崩溃遗留的会话对端在节点下次启动时删除。

有 app_port 的应用,在隧道内的 TCP 流上使用带长度前缀的帧 u32 length ‖ u8 class ‖ u8 type ‖ payload,每帧最大 1 MiB。length 包含 class/type/payload、不包含长度字段本身,最小为 2;class 0 为通道控制(type 0=OPEN、1=PAKE、2=LOCATE),class 1 的 type 由应用独立定义,见精确 framing:

OPEN = [ver = 1, kind text, channel_id bytes16, mac bytes32 | null]
mac = HMAC-SHA256(K_confirm, "ns/channel-open/v1" ‖ channel_id ‖ I_sender ‖ I_receiver)
  • 连接方先发 OPEN,被连接方校验后回一个 OPEN(I_sender 与 I_receiver 对调),在本地要求的额外 PAKE 完成后(如适用)才交换应用帧。
  • 经信箱建立的通道:channel_id = mailbox_id,mac 必须存在;MAC 中的两个身份必须与承载这条流的 WireGuard 对端一致。这样隧道证明对端身份,MAC 证明对端就是完成引入的那一方。
  • peer 通道:channel_id 为发起方生成的 16 字节随机值,mac 为 null;需要口令时随后用 LOCATE 定位邀请并运行隧道内 pake(§5.2)。channel_id 标识本次流,不能代替邀请定位或重置邀请失败预算。
  • kind 不匹配、MAC 错误或第一帧不是 class=0/type=0 的 OPEN 时立即关闭连接。
  • relay 看到临时参与方密钥、kind、短码、消息大小与时间。confirm 的 Offer / Answer 明文携带双方的签名记录与临时 DH 公钥;pake 的身份只出现在加密的 HELLO 中。relay 看不到口令、K_ch、PSK 或应用数据。
  • relay 不能伪造 Offer、Answer、ACCEPT 或 HELLO,也不能伪造”已接纳”:加入方只认创建方签名的 ACCEPT 或能解开的 HELLO。
  • confirm 中 relay 用自己的身份替换整份 Offer 或 Answer 时,用户核对的节点 ID 会不一致,这是唯一需要人工的一步;节点 ID 有 96 位,无法碰撞出相同的值。
  • pake 中知道短码的 relay 运营方,每次尝试最多在线猜一次口令;第 3 次失败后信箱关闭。失败预算也由创建端本地维护,隧道内按同一邀请累计;口令熵由应用按自身安全需求决定(文件发送约 33 位),不能只信任 relay 的计数。
  • kind 进入签名、CPace 的 CI 与 K_ch 的派生:为一个应用产生的消息在另一个应用里无法通过校验。
  • 信箱过期、被拒绝或取消时什么也不安装。
  1. 选择 kind,以及 §3 中允许的 auth 与 lifetime 组合。
  2. 有隧道内阶段时分配 app_port,并定义 OPEN 之后的应用帧。
  3. 需要会合时定义票据或短码的界面;口令长度满足自己的安全需求。
  4. 在 ns 的应用接口中登记 profile 与命令。relay、nsshared、nsplane 都不需要改动。
项目值
消息版本1
签名上下文ns/channel-offer/v1、ns/channel-answer/v1、ns/channel-accept/v1、ns/channel-hello/v1
派生标签ns/channel-key/v1、ns/channel-psk/v1、ns/channel-hello-o/v1、ns/channel-hello-p/v1、ns/channel-confirm/v1、ns/channel-open/v1
CPacesuite 1:draft-21 X25519/SHA-512;sid/CI 绑定会话标识与双方新鲜 nonce,见精确 profile
AEADChaCha20-Poly1305,nonce 0,每个密钥一条消息
隧道内帧u32 length ‖ u8 class ‖ u8 type ‖ payload,最大 1 MiB;class 0 为控制(type 0=OPEN、1=PAKE、2=LOCATE),class 1 为应用,各自独立编号
会话对端每个节点默认最多 8 个并发;最长 24 h

签名为 RFC 8032 pure Ed25519,严格校验;被签名的消息是 ASCII 上下文紧接后续字节,没有分隔符(同 Quick 协议 v2 §12.2)。

原设计应用通道
配对 Invite / Join(ver = 2)confirm 的 Offer / Answer(ver = 1),增加 kind 字段
ns/pairing-invite/v2、ns/pairing-join/v2、ns/pairing-accept/v2ns/channel-offer/v1、ns/channel-answer/v1、ns/channel-accept/v1
ns/pairing-psk/v2ns/channel-key/v1 + ns/channel-psk/v1
文件发送 TransferHellopake 的 HELLO
ns-transfer/psk/v1、ns-transfer/confirm/v1ns/channel-psk/v1、ns/channel-confirm/v1 + 隧道内 OPEN
ns-transfer/cpace/v1ns/channel-cpace/v1
  • 第三方接入与临时 peer 权限已按应用模式 §3.1界定;NSD 缺少投影 peer 不回退 Quick。实现已在分支推进,完整来源与平台验收尚未完成。
  • 语音、聊天的 kind、端口与是否需要 pake。

最小 owner grant/capability、IPC 封包、超时和关闭契约见应用模式 §3.2。本地 IPC 的 CANCEL 仅是本地控制,不能转发或混用为隧道 LOCATE;隧道 OPEN 仍严格遵循本文的 class/type、规范 CBOR、kind/channel 与 peer 模式 mac=null 规则。