ns · 统一节点运行时实现规格ns 是 NSIO Next 在每台设备上的唯一网络运行时。它既能作为 L3 节点访问其他节点,也能发布和消费 L4 Service;是否启用 Service Host、Subnet Router、Connector 或 Gateway 能力由节点 Capability 和授权决定,不再分成 NSC/NSN 两套产品。
完整产品入口、状态和跨组件责任以产品能力目录与实现闭环为检查总账;精确建流、Relay、ServiceFlow、RouteFlow 和 Exit 传输以数据面协议为权威,控制对象状态/API 以控制资源生命周期为权威。本文件只定义 ns 的执行责任。
ns 必须负责:
ns 不负责:
一个安装包含一个长期运行的 daemon/service、一个 ns CLI、平台 helper 和可选 FFI。所有入口调用同一 runtime,不允许 CLI、App、helper 各自创建第二个网络引擎。独立二进制、App 内携带 artifact 与包管理器安装的仲裁、更新和卸载以发行生命周期为权威。
| 模块 | 输入 | 输出 | 关键不变量 |
|---|---|---|---|
identity_store | Enrollment / rotation | 本地密钥、公钥证明 | 私钥不出节点;按 Organization 隔离 |
enrollment | login proof / key proof | Device + Membership | Request/Grant 可恢复且一次提交 |
control | authenticated profile | signed snapshots/deltas | 来源、目标、epoch、revision 全校验 |
projection_store | verified events | last-valid projections | 原子切换;失败不覆盖 |
policy_runtime | Grant projection | L3/L4 decisions | 只执行服务端编译结果与本地上限交集 |
path_engine | peer endpoints/relay lease | active encrypted path | direct → relay → WSS,切换不改身份 |
tun | Node/Route/Exit plan | platform network state | ownership journal、幂等、可恢复 |
dns_proxy | Node/Service DNS | local resolver/proxy | 不递归解析回 Service VIP |
service_host | Local Manifest + grants | Endpoint listeners/forwarders | 默认不发布;后端边界本地决定 |
service_client | directory + grants | Service VIP/proxy route | 无授权不可见不可达 |
gateway_client | candidates/selections/config | Egress/Exit/Ingress backend | 候选不替代最终校验 |
local_api | Client/CLI commands + OS peer credentials | typed status/operations | owner/maintenance namespace 分离;稳定 schema,不暴露内部日志文本 |
diagnostics | runtime/platform facts | redacted bundle | 不包含私钥、token、完整目录 |
本地状态分四类,不能混在一个 JSON 中:
| 存储 | 内容 | 生命周期 | 保护 |
|---|---|---|---|
| Installation | 安装 ID、平台能力、daemon endpoint | 安装级 | 普通完整性 |
| Runtime Owner Binding | owner mode、OS principal 摘要、state、revision | 安装级 | OS ACL + 完整性保护;非所有者不可读租户字段 |
| Organization Identity | Device Key、device_id、登录 profile | 组织级 | Keychain/DPAPI/0600 |
| Network Membership | membership、Node/WG Key、Node IP、revision | Network 级 | Keychain/DPAPI/0600 |
| Runtime Journal | TUN/DNS/route ownership、phase、rollback | 运行级 | 原子写、崩溃恢复 |
Staging Key 使用与正式 Device Key 相同的保护后端,按 request ID 存储。请求恢复必须复用原 key;终态或 min(server TTL, 24h hard limit) 后清理。正式状态提交成功后采用原子 rename 晋升,不能复制后留下两份私钥。
| 状态 | 含义 | 可执行操作 |
|---|---|---|
uninitialized | 无 Organization 身份 | enroll、doctor、version |
enrolled_disconnected | 有身份/配置,网络未运行 | connect、publish edit、reset |
connecting | 正在建控制与数据面 | cancel、status、diagnostics |
connected_degraded | 私网部分可用或可选能力失败 | retry feature、disconnect |
connected_ready | 当前请求能力均就绪 | 全部运行命令 |
stopping | 正在按依赖逆序拆除 | status;其他命令返回 busy |
recovering | 根据 journal 恢复/清理 | status、diagnostics |
owner_inactive | user-owned runtime 因 OS owner 切换而撤除网络投影 | owner status、diagnostics、明确 reconnect/reset |
blocked | 安全冲突,保持 fail-closed | 用户确认恢复、disconnect |
进程启动顺序:锁定单实例 → 加载/校验本地状态 → 恢复未完成 journal → 启动 maintenance local API → 判定 Runtime Owner Binding。user_session 在 owner 尚未登录或 peer/client credential 未验证时停在 owner_inactive,不建立控制连接或系统网络投影;验证 owner 后才继续。managed_device 校验 system/service identity 与受管策略后可以无人值守继续:建控制连接 → 获取/恢复 projections → 规划 DNS/TUN/route → 建 peer path → 原子启用转发 → 发布 readiness。
停止顺序:停止新连接 → 撤转发 → 撤 capture/route → 撤 DNS → 关闭 path/control → 完成 journal。任何中间失败保留足够旁路和 journal,让下一次启动可继续恢复。
user-owned 桌面 runtime 启动网络前必须同时持有单实例锁和 Runtime Owner Binding。OS active-console principal 变化或出现并发交互会话时,走与安全停止相同的 journaled network teardown,再进入 owner_inactive;只锁 App UI、没有 principal 变化时可以保持。平台不能证明切换事件先于新用户网络使用时,禁止建立 user-owned 系统级投影。managed_device 由 system/service principal 持有,不能被任意交互用户 claim,也不宣称逐人数据面归因。
每个已登录 Organization profile 独立维护认证、event stream、revision 和错误。首版一个运行实例只激活一个 Profile;切换时先计算 DNS/route 影响并让用户确认,不合并不同 NSD 的系统级投影。
事件应用器必须:
applied。控制断线时继续使用未过期的最后有效投影。过期后的 Node/Service/Route/Gateway 行为分别按租约安全语义收敛,不能统一“保持连接”或统一“全部清空”。
关键实现:
1280,路径从直连切到 Relay/WSS 时不得动态改变接口 MTU;分片/PMTU 事实在 transport 内处理。候选来源包括本地 LAN、STUN reflexive endpoint、历史有效 endpoint、基础 Relay 和被授权 Managed Relay。状态机:
路径评分考虑握手、RTT、丢包、成本与稳定性;切换需要滞回,避免 Wi-Fi 抖动时来回闪烁。EndpointCandidate 上报、候选探测、NAT rebinding、roaming、Node/WG key rotation 与 Relay attach 严格执行数据面协议 §4-5。Relay 只见外层密文和轮换 opaque session ID;WSS/TLS 是 UDP 受阻的最后回退,不与应用 HTTP 代理复用连接。
节点本地 Manifest 只表示本地同意和实际 backend;远端 CapabilityPolicy 只表示“可以申请”。service_host 的每个受管实例必须取得 CapabilityActivation/Lease 后才向数据面生效,自动批准也不能绕过配额、计量和审计。远端 desired manifest 与本地授权取交集,任一方拒绝都不发布。
Local Manifest 是节点可发布范围的本地上限,来源按优先级合并:
approval_pending,拒绝返回 local_manifest_denied。声明流程:验证 backend 格式和本地可达性 → 生成 endpoint declaration/revision → NSD 审批/分配 Service → 收到 publisher role projection → staging listener/forwarder → 健康上报。publisher 只获得本 Service 的允许消费者/credential 验签材料,不获得完整用户目录。NSD 删除 Service 不删除本地 manifest;用户可重新发布或明确清理本地声明。
后端支持本机 socket、LAN IP、远程域名和 Unix socket(按平台能力)。远程域名必须使用 origin resolver,不得走 NS split DNS 再解析到 Service VIP。
消费路径有两种:
ns 按 Host/SNI/目标匹配 Service。两条路径共享同一 Service Directory、Grant 和 endpoint selection,不各自维护 ACL。ServiceFlow 通过 peer WG path 内、只由 daemon 执行的 runtime_transport 建立,不依赖 Node L3 Grant。平台必须实现 userspace pre-injection demux 或 daemon exclusive listener + owner/firewall verification;保留端口冲突时整轴 not_ready,不能把 runtime packet 交给普通本机进程。runtime/Relay QUIC 明确禁用 0-RTT。消费端使用 consumer projection 签发/携带 ServiceFlowCredential,publisher 按 audience、Service/Endpoint、source、action、protocol/port、generation/digest、revision 和 expiry 本地验签;OPEN/ACCEPT/REJECT、TCP 双向 half-close/RST、UDP idle ownership 与 failover 以数据面协议 §6为准。自定义域名在 TUN 下由 split DNS 映射 Service VIP,在 Proxy 下由 Host/SNI 匹配;TLS 目标域名保持原值。
目录状态至少区分:available、unauthorized、offline、disabled、pending approval、projection outdated、read failed、incompatible。Client 只展示当前用户有权知道的最小状态。
Resolver 按明确优先级处理:本机保留 → 当前 Network Node short/FQDN → Service short/FQDN/custom domain → 管理 Route domain → origin/public resolver。同名 Node/Service 时短名 withheld,FQDN 仍确定可用。
DNS projection 与路由 revision 必须协调提交,避免 DNS 已指向 VIP 而路由尚未就绪。读取失败保留未过期缓存并返回 SERVFAIL/明确诊断,不回答 NXDOMAIN 来伪装“没有资源”。
Route planner 输入为权威 Route projection、本机接口、现有路由和 runtime ownership journal。应用前必须完成 CIDR overlap、精确 destination owner、控制端点旁路和 DNS 依赖检查。
安装顺序:本机地址 → 控制/relay bypass → leak guard → capture/target route → forwarding。拆除严格逆序并保留逃生通道。外部 VPN 占用相同 route 时拒绝覆盖,返回冲突所有者和 CIDR。
App Connector 的域名解析使用受控 origin resolver,解析结果有 TTL/上限/审计,不能把任意 DNS 响应永久变成企业路由。
客户端 Route 投影必须携带签名 RouteAccessCredential;Connector 端持有同 generation/digest 的 ConnectorRouteProjection,本地校验 source membership、connector audience、精确目的地、协议/端口、expiry 和 revision。RouteFlow 复用 ServiceFlow 的建流、背压、half-close/RST/UDP idle 和稳定错误语义,但目标是 Connector projection 中的精确 CIDR/域名解析结果;建流不查询 NSD,控制面断线只在凭据和投影未过期时继续。
切换 generation 时各代目的地集合独立。security_first 按 activation epoch 原子拒绝旧代,未准备 Connector 不进入候选;bounded_drain 只在明确 Network 策略下保留不超过凭据 TTL 的 previous 代。Runtime 绝不把 current/previous 的目的地做 union。
Gateway Egress 是 Service publisher/backend,使用同一 ServiceFlowCredential,只有访问该 Service 的流量走指定 NSGW。Gateway Exit 是独立 tunnel 能力,按 ExitTunnelLease 的 Default Route 或授权 CIDR 捕获流量。
Exit 执行轴:local config、selection、projection、gateway/WG readiness、capture、IPv6 routing。只有这些轴共同满足时才宣称 forwarding ready。候选列表用于选择,runtime 每次投影仍执行授权、profile、scope/CIDR、健康和材料校验。
Exit capture 已生效但 forwarding 未就绪时保持阻断。Client 只能在用户明确选择“关闭出口”后以普通连接重启。IPv6 由外部 VPN 管理时必须上报 external_unmanaged,不能宣称全部流量已走公司出口。
本地 API 使用版本化 schema 和 OS 访问控制,至少提供:
| 类别 | 命令/查询 |
|---|---|
| lifecycle | start、stop、restart、status、watch |
| identity | profiles、enroll、cancel、logout-session |
| directory | nodes、services、routes、gateways |
| publication | manifests、declare、accept、deny、health |
| policy facts | selection support、readiness、conflicts |
| gateway | candidates、configure、disable |
| support | doctor、export diagnostics、repair platform state |
| distribution | version handshake、update check/apply/status、uninstall plan、cleanup status |
写命令返回 operation ID,长操作通过 watch 推送状态。FFI/CLI 只做 schema 绑定;错误码必须独立字段贯穿 helper/XPC/FFI,不从 message 解析。
本地 API 分成两个授权 namespace:
非所有者查询只返回 owner mode/state 和稳定错误码,不返回 owner principal、NSIO identity 或租户元数据。caller identity 必须来自命名管道/XPC/Unix socket 的 OS 证明,payload 中的 UID/SID 只可作为诊断字段,不能参与授权。
| 平台 | 数据面 | 密钥 | 服务生命周期 |
|---|---|---|---|
| Windows | Wintun/平台驱动 + service | DPAPI/CNG | Windows Service + signed installer |
| macOS | Network Extension 或受控 helper | Keychain | LaunchDaemon/helper,路由 journal |
| Linux | TUN;服务器可选 kernel WG | 0600 root/user state | systemd + capability 最小化 |
| iOS | Network Extension | Keychain | App-managed tunnel provider |
| Android | VpnService | Keystore | foreground VPN service |
平台差异只能影响实现和 capability,不能改变授权语义。缺少安全必需平台能力时拒绝启用对应功能。
Runtime status 提供版本、local owner、active profile、control、identity、membership、path、DNS、L3、Service、Route、Exit 和 platform ownership 轴。每轴包含 state、reason code、revision、last_success 和 retryable;非 owner 只能得到脱敏 local owner/maintenance 状态。
日志采用结构化事件;高频包不逐包记录。诊断包对路径、DNS、route、lease 和 projection 做关联,但对用户目录、域名和 IP 按支持策略脱敏。
必须覆盖:
完成证据必须包含共享契约测试、组件测试、双节点 local-stack、所需 NAT/Relay 场景和平台真机记录。