NSIO Docs
NSIO Docs
首页

NSIO Next · 当前开发基线

统一 L3 + L4 架构
P0 / P1 / post-v1 发布范围
产品能力目录与实现闭环
功能闭环总账
产品闭环与功能实现
实施蓝图与交付契约
发行、升级与卸载
跨组件共享契约
控制资源生命周期
控制投影与 Runtime Protocol
数据面协议
Grant 与策略编译

组件实现规格

ns · 统一节点运行时
Client · App、CLI 与门户
NSD · 身份与控制面
NSGW · 增值数据面
身份、登录与会话
注册、准入与配额协议
商业模型与 Entitlement
采购、计费与发票
服务运营与 SLA
备份与灾难恢复
信任、合规与数据权利

Last Updated: 2026/8/13 19:39:39

Previous Page发行、升级与卸载
Next Page控制资源生命周期

#NSIO Next · 跨组件共享契约

本文是 ns、NS App、NSD、NSGW 和管理控制台并行实现时必须共同遵守的协议基线。产品含义以产品闭环为权威,身份与注册分别以身份登录和注册协议为权威;管理对象状态/API 见控制资源生命周期,建流与传输见数据面协议,逐功能完备性见功能闭环总账。本文只固定跨进程、跨仓库和跨版本的可观察契约。

#1. 契约所有权

契约权威生产者消费者禁止事项
Identity / SessionIdentity Authority + NSDClient、管理 APIApp 保存密码或自行判断 MFA 强度
Device / EnrollmentNSDClient、ns仅凭显示名认领 Device
Network / MembershipNSDns、Client客户端自行分配 Node IP
Grant / Policy ProjectionNSD compilerns、ClientDart、CLI 或 NSGW 重写授权算法
Local Service Manifest节点所有者或受管授权ns、NSDNSD 越过节点本地上限发布后端
Service Directory / DNSNSD projectionns、Client读取失败渲染为空目录
Route / Exit / Gateway LeaseNSDns、NSGW、Client候选列表替代数据面的最终校验
EntitlementEntitlement issuer → NSD resolverNSD、NSGW客户端自报套餐能力
Product Allocation / Relay CapacityCommercial authority + Capacity Authorityprovisioner、NSD、Platform OperationsNSGW 自报容量成为可售库存,或把价格/订单/Inventory 下发运行时
Runtime Statusns runtimeClient、CLIClient 从日志文本猜运行状态
Local Runtime Ownershipns runtime + OS peer authenticationClient、CLI、installer/helper非 owner 读取租户状态,或 OS 管理员凭维护权限冒充 owner

共享类型放在 ns-shared-next。共享仓库只承载稳定 wire schema、标识符、签名与错误类型,不承载数据库 ORM、产品 UI 或平台路由实现。

安全判定函数也属于共享契约权威。NSD、Web、ns、App 和契约测试不得自行重写 redirect 匹配、ID Token 验签/声明校验、Browser Cookie 属性或 Authorization Code 绑定判定;必须调用所固定 ns-shared-next revision 暴露的 matcher、verifier 和 authority validate 函数。各端 adapter 只能做传输字段转换与稳定错误映射;若共享函数无法表达新需求,必须先升版共享契约并统一更新消费 revision,不得在单个仓库增加本地例外。

#2. 标识符与作用域

所有外部对象使用不可枚举、全局唯一、类型明确的 ID。显示名可修改,绝不参与授权、幂等或引用完整性。

对象建议字段最小作用域
Accountaccount_idIdentity Authority
Human/Machine Principalprincipal_idOrganization
External Identity / Sessionexternal_identity_id / session_idAccount + issuer
Organizationorganization_idDeployment
Networknetwork_idOrganization
Organization Membership / Grouporg_membership_id / group_idOrganization
Admin Role Bindingrole_binding_idOrganization + resource scope
Devicedevice_idOrganization;不跨组织复用 Device Key
Node Membershipmembership_idNetwork + Device
Serviceservice_idNetwork
Endpointendpoint_idService + Membership
Grant Set / Rulegrant_set_id / rule_idNetwork
Capability Policy / Activationcapability_policy_id / activation_idOrganization/Network
Routeroute_idNetwork
Gateway Instance / Poolgateway_instance_id / gateway_pool_idDeployment
Gateway Assignmentgateway_assignment_idOrganization + instance/pool target
Product Allocationproduct_allocation_idOrganization + commercial authority
Relay Capacity Inventory / Reservationrelay_capacity_inventory_id / relay_capacity_reservation_idDeployment + region + GatewayPool + capacity class / ProductAllocation
Gateway Profileprofile_refNetwork + Gateway Assignment
Gateway Enrollment Request / Keygateway_enrollment_request_id / gateway_enrollment_key_idDeployment
Gateway Pairing Session / Candidategateway_pairing_session_id / gateway_pairing_candidate_idDeployment + pairing authority scope
Public Application / Edge Policy / Bindingapplication_id / edge_policy_id / binding_lease_idOrganization/Network
Share Offer / Binding / Federationshare_id / share_binding_id / federation_binding_idprovider/consumer Organization
Machine Credentialoauth_client_id / credential_idMachinePrincipal
Posture Provider / Claimposture_provider_id / posture_claim_idOrganization/Device
Rotation / Reservationrotation_id / reservation_idtarget object / Entitlement subject
Request / Operationrequest_id / operation_idDeployment

每个 API、事件和审计记录都携带其权威作用域与目标 ID:Deployment 对象至少带 deployment_id,Organization/Network 对象再带必要的 organization_id、network_id。仓储层必须先按权威作用域过滤,再按对象 ID 查询;不能先查全局 ID 后在 handler 里补权限判断,也不能给 Deployment 级 GatewayInstance 伪造一个租户作用域。

#3. 命令契约

所有会改变状态的 API 使用统一命令语义:

{
  "api_version": 1,
  "request_id": "req_...",
  "idempotency_key": "client-generated-stable-key",
  "expected_revision": 41,
  "command": "service.declare",
  "subject": {
    "organization_id": "org_...",
    "network_id": "net_..."
  },
  "payload": {}
}
  • request_id 用于端到端诊断,不作为幂等键;
  • idempotency_key 在同一命令与权威作用域内唯一,重复请求必须返回同一业务结果;
  • 修改已有对象时必须带 expected_revision,版本不符返回 revision_conflict 并附当前 revision;
  • 危险操作还要带 NSD 生成的 impact_digest,提交时重新计算,不一致返回 impact_changed;
  • 命令成功只表示权威状态已提交,不表示所有终端已投影完成;响应必须分别给出 committed_revision 和投影跟踪 ID。

命令名称进入共享 registry,首版至少覆盖:identity/session、Organization/Network/Group/Role、Enrollment/Device/Membership、AccessGrant、CapabilityPolicy/Activation、Service/Publication、Route、GatewayInstance/Pool/Assignment/Selection/PublicApplication、GatewayPairingSession/Candidate、ProductAllocation、RelayCapacityInventory/Reservation、Share/Federation、MachinePrincipal/OAuth client、Posture、Entitlement/Reservation、Audit/Export/Delete/Hold。GatewayInstance/Pool 与 RelayCapacityInventory 使用 Deployment Operations authority,ProductAllocation 使用商业权威,Capacity Reservation 只能由商业 provisioner 在容量事务中写入,Assignment 使用 Organization authority,Activation/Selection 使用 Organization/Network authority;这些对象不能落进一个泛化的 Gateway update。Gateway pairing 使用 gateway.pairing.open|announce|confirm|reject|close,其中 announce 只能提交候选,不能创建 Instance、Assignment、Activation 或 Lease。每项 create/update/approve/suspend/revoke/rotate/delete 使用独立 action;完整状态和 API 以控制资源生命周期及专项文档为准。

后续版本已经保留但未进入首版发布范围的工作流,不得提前加入 v1 command/ID/error registry、OpenAPI、Client exhaustive enum 或发布门。当前范围以发布范围基线为准;启用保留能力必须先在共享仓库新增明确版本的契约,再统一升级所有生产者和消费者。

#4. 下行事件信封

每种事件独立版本,不存在一个可以推断全部能力的“产品版本”。

{
  "event_type": "services.snapshot",
  "schema_version": 1,
  "event_id": "evt_...",
  "deployment_id": "dep_...",
  "organization_id": "org_...",
  "network_id": "net_...",
  "target": {"type": "membership", "id": "nm_..."},
  "epoch": 7,
  "revision": 184,
  "generated_at": "2026-08-08T12:00:00Z",
  "not_before": "2026-08-08T11:59:55Z",
  "expires_at": "2026-08-08T12:10:00Z",
  "payload_digest": "sha256:...",
  "payload": {},
  "signature": "..."
}

事件目标使用 device、membership、gateway_instance 或 client_session 类型化引用,必须与认证身份和 payload 作用域一致。租户面对的 gateway_assignment_id 不能充当 GatewayInstance 的控制 target。客户端处理顺序固定为:验证版本可理解 → 验签 → 校验目标与作用域 → 校验 epoch/revision/有效期 → 解析 payload → 构建候选状态 → 原子替换 → 回执。任何一步失败都不能覆盖最后有效状态。具体 stream 与 payload 见Runtime Protocol v1。

#5. Snapshot、Delta 与恢复

每个投影流独立维护 (deployment_id, target, event_type, epoch, revision) 水位。本地 profile_id 只用于 Client 选择保存的控制端,不进入 NSD 签名事件:

  1. 初次连接或本地无状态时请求 snapshot;
  2. delta 的 base_revision 必须等于本地 revision;
  3. 断档、epoch 改变、digest 不符或本地状态损坏时停止应用 delta 并请求 snapshot;
  4. snapshot 在临时空间完成解析、授权交叉校验和平台计划构建后再原子切换;
  5. 新 snapshot 失败时继续使用未过期的最后有效 snapshot,并上报 projection_outdated,状态为 outdated_applied;
  6. 过期后按资源安全语义 fail-closed,不将其变成空列表或默认允许。

事件回执区分 received、applied、rejected。applied 必须包含实际生效 revision;rejected 包含稳定错误码和可重试性,但不得回传私钥或完整敏感 payload。

#6. 状态模型

跨层功能状态不得压成布尔值。所有功能至少分成四个正交轴:

轴回答的问题示例
intent用户/管理员想要什么enabled / disabled / required
authority权威是否允许authorized / denied / unknown
projection控制面是否给出可执行配置waiting / matched / mismatch / expired
runtime本机或网关是否实际生效starting / ready / degraded / blocked / failed

Client 展示由这些结构化轴推导,不得用“连接成功”覆盖部分失败,也不得用“无资源”覆盖读取故障。

#7. 错误信封

{
  "result": "error",
  "error": {
    "code": "service_projection_read_failed",
    "message_key": "service.projection_read_failed",
    "retryable": true,
    "scope": "network",
    "request_id": "req_...",
    "details": {
      "network_id": "net_..."
    }
  }
}

错误码按原因而不是页面命名。固定分类如下:

类别示例默认动作
authenticationsession_expired重新登录,不撤 Membership
authorizationnot_authorized不重试,联系管理员或修改 Grant
admissionquota_limit_reached显示真实资源与限制
conflictrevision_conflict, route_conflict刷新权威状态并让用户决定
unavailablecandidates_not_ready保持意图,等待或重试
read_failureprojection_read_failed保留缓存,显示故障
incompatibleschema_version_unsupported更新对应组件
local_policylocal_manifest_denied在节点本地处理
safetycapture_blocked, signature_invalid保持 fail-closed,禁止自动降级
local_ownershipruntime_owned_by_another_local_user, runtime_owner_inactive不泄露 owner/tenant;等待、释放或明确 reset

错误结构允许新增可选 details 和 remediation,但不能改变既有 code 的含义。remediation 是服务端根据当前对象、actor、allowed actions 和产品能力返回的结构化下一步;Client 只能渲染,不能按 error code 自行推断。

v1 remediation 是 schema_version=1 的闭合 tagged union,kind 仅允许:

kind允许的最小 payloadClient 动作
retryretry_after_ms?重试同一 operation,不改变 payload
reauthenticaterequired_assurance?重新登录或 step-up
contact_ownerowner_display_name?, owner_contact_masked?, resource_ref?展示脱敏 owner 信息或复制联系摘要
open_product_viewview_id, resource_ref?打开 Client 内置、版本识别的产品视图
open_system_settingssettings_kind调用平台受支持的系统设置入口
update_componentcomponent, minimum_version?进入签名更新流程
use_allowed_scopeallowed_scope把服务端返回的规范范围作为明确的新草稿,用户仍需再次保存
view_billingquota_ref?打开内置 Usage & Billing 视图
copy_support_inforequest_id, diagnostic_ref?复制脱敏诊断信息

view_id、settings_kind、component、resource_ref.kind 和 allowed_scope.kind 都是共享 registry 的闭合枚举;payload 不允许携带任意 URL、任意相对路径、shell command 或可直接执行的客户端动作。owner 信息只在当前 actor 被允许披露时返回,并保持脱敏。首版不包含 request_access。未来增加自助申请必须发布新的共享 schema revision,不能让旧 Client 把 not_authorized 自动解释成“可申请”。用户文案由 Client 本地化,服务端 message 仅用于诊断。

#8. Capability Manifest

能力发现分两层:

  • /.well-known/nsio-capabilities 用于匿名预检,短缓存、带 ETag,不作为安全权威;
  • 已认证握手交换各组件的能力集合和协议版本区间,协商结果进入认证 transcript。

Manifest 只能关闭双方不共同支持的可选功能,不能降低签名、Device Key 绑定、确认页防钓鱼、租户隔离、授权或 fail-closed 基线。客户端自报“不支持安全能力”时,服务端拒绝对应流程,不切到更宽松路径。

#9. 密钥与签名

  • Device Key、Node Key、WireGuard 私钥、Gateway Key 和 Staging Key 只在对应执行端生成并保存;
  • NSD 只保存公钥、摘要、状态和轮换关系;
  • Device Key 证明设备与注册请求,Node/WG Key 用于 Network Membership 数据面;
  • 不同 Organization 使用独立 Device Key,不能从同一安装主密钥确定性派生;
  • 控制事件由部署级签名键签名,签名键轮换必须提供带交叉签名的信任链;
  • Secret、Token、Enrollment Key 只以哈希或密文形式持久化,日志和审计禁止明文。

首版共享 credential registry 至少包含:EnrollmentGrant、GatewayEnrollmentGrant、control credential、PeerPathLease、RelayRouteLease、RelayBudgetLease、ServiceFlowCredential、RouteAccessCredential、Capability Lease、EgressOriginLease、ExitTunnelLease、IngressBackendCredential、FederatedAccessLease、OAuth Access Token、EdgeSessionCredential 和 signed EdgeIdentityContext。Capability Lease 必须显式绑定 activation_id、gateway_instance_id 和 generation;不能使用含义不明的通用 gateway_id。RelayBudgetLease 只分配 opaque budget subject 的本地 QoS 份额,不能授权 route、peer、tenant capability 或数据面资源;过期回落 fallback profile,不复用 Capability/Route Lease 的关闭语义。每类 credential 有独立 audience/claims/schema/TTL/revoke key,不能用一个通用 bearer token 跨数据面。

#9.1 Gateway 浏览器配对契约 v1

浏览器配对用于托管控制下少量、有人值守的客户自建 NSGW。管理员必须先在已认证控制台打开一个短期、有界的 GatewayPairingSession,再允许未注册进程提交候选;不存在可被任意匿名进程填充的全局 pending 列表。gateway_pairing_session_id 是至少 128 bit 随机相关句柄,只允许向该 Session 提交候选,既不是 bearer Enrollment Key,也不能批准、兑换或创建任何正式 Gateway 对象。窗口外的 announce 不创建产品/业务对象,但仍进入匿名安全计数、限速和告警。

权威 transcript 使用闭合、版本化的类型,而不是字符串拼接:

GatewayPairingTranscriptV1 {
  protocol_domain,
  schema_version,
  authority_scope: deployment | deployment_and_organization,
  gateway_pairing_session_id,
  server_challenge,
  gateway_public_key,
  enrollment_capability_ceiling_digest
}

authority_scope 是闭合 tagged union,必须包含 deployment_id,客户自建轨还必须包含 organization_id。transcript 必须复用共享仓库的 production canonical serializer 与 domain-separated digest 路径(与 reflexive_value_digest<T: Serialize> 同一规范),禁止裸字段串联、手写 JSON、依赖 map 顺序或使用显示字符串。NSGW 用 Gateway 私钥签署 exact canonical transcript;NSD 在创建候选前验证 Session、challenge、scope、签名、ceiling digest、TTL 和 candidate limit。

双方显示的四词比较短语是版本化固定词表对 domain-separated canonical transcript digest 的确定性映射。词表版本、digest-to-index 算法、Unicode/ASCII 展示和 golden fixtures 由 ns-shared-next 的单一 helper 持有,NSD、NSGW 和 Web 不得各自实现。它同时绑定 Deployment、Organization、Session、challenge、Gateway Key 和待冻结的 capability ceiling;不能只从公钥、随机短语或 Session ID 生成。短语仅帮助人确认两个界面表示同一 transcript,不替代管理员身份、step-up 或服务端签名校验。

确认命令必须原子完成:重新验证 Session/revision/candidate/transcript → step-up 与管理权限 → 单次消费 Session → 创建 GatewayInstance → 创建或加入同 ownership 的 GatewayPool → 创建/复用 customer_owned GatewayAssignment → 保存不可变 enrollment provenance → 写审计/outbox。任一步失败不留下部分正式对象;Session 已消费后任何 candidate 都不能再次确认。不同 Organization/ownership 的实例不能进入同一客户池。拒绝只关闭候选并写安全事件,不生成正式对象。

浏览器配对与 Gateway Enrollment Key/Workload Identity 最终写入同一份不可变 provenance:enrollment_method、proof_source_id、proof_source_revision、accepted_at、enrollment_capability_ceiling 及其 digest。配对轨的 ceiling 就是管理员确认 transcript 中的 enrollment_capability_ceiling_digest 对应值;Key 轨来自被接受的 Key revision;attestation 轨来自被批准的 workload policy revision。注册后不得重算、放宽或改写这份历史快照。

#10. 时间与租约

Enrollment Grant、Session、控制事件和短 Lease 继续由服务端时间判定,客户端倒计时只用于提示。P1 的限时 AccessGrant 使用绝对 UTC 毫秒,不使用“收到后再存活 N 分钟”的相对 TTL:

validity: absent | {
  not_before_ms?: uint64,
  not_after_ms?: uint64
}

整个 validity 缺省时不序列化;对象存在时至少有一个边界,两个边界同时存在时必须满足 not_before_ms < not_after_ms。P0 永久 Grant 不携带该字段,因此不改变现有 canonical digest。

共享时间常量固定为:

MAX_TRUSTED_CLOCK_SKEW_MS_V1 = 300_000
TIME_ANCHOR_INITIAL_SKEW_MS_V1 = MAX_TRUSTED_CLOCK_SKEW_MS_V1
TIME_ANCHOR_REFRESH_SKEW_MS_V1 = MAX_TRUSTED_CLOCK_SKEW_MS_V1

执行端在验签、作用域、epoch/revision 和 payload digest 全部通过后,使用签名 envelope 的服务端时间建立 TimeAnchor(server_time_ms, suspend_inclusive_elapsed_ms, event_id, revision)。后续可信时间只按“服务端绝对时间 + 包含系统睡眠的 elapsed”推进;不得使用会在 suspend/hibernate 期间停止的普通进程计时器,也不得因系统 UTC 回拨延长授权。

首次锚点与本地 UTC 的差异不得超过 TIME_ANCHOR_INITIAL_SKEW_MS_V1;刷新锚点与上一可信时间估值的差异不得超过 TIME_ANCHOR_REFRESH_SKEW_MS_V1。超过边界、平台不能提供包含睡眠的 elapsed、进程重启后尚未取得新锚点,或时间值越界/不可表示时,带 validity 的 Grant fail-closed,永久 Grant 不受影响。唤醒并恢复控制连接时刷新锚点;从缓存重载投影不得重置绝对 not_after_ms 或重新获得完整 TTL。

可信 now < not_before_ms 时尚未生效,now >= not_after_ms 时立即拒绝。NSD 同时保留定时重编译、删除投影、管理端展示和撤销推送,但不是到期的唯一执行者。

time_bound_grant_v1 按每条 Grant 的实际执行端集合求能力交集:L3 为源/目标节点,Service 为 consumer/publisher(公网路径再包含 NSGW),Route/Exit 为相关客户端与 Gateway/Connector。创建或扩大 Grant 时必须先校验当前执行端,不支持时拒绝保存并列出组件及“改为永久授权/升级组件”动作,不能先返回成功再在投影阶段静默丢弃。之后 selector 新增的不兼容执行端只对该端 fail-closed、不获得该 Grant;其他兼容执行端继续生效,不能为一个旧节点撤销整条 Grant。管理端显示已执行数量、未执行端和升级入口。

撤销不依赖“立即收到一条删除事件”这一单点。Peer、Route、Gateway 和跨组织分享都使用短租约,使丢失撤销事件时也能在 SLA 内自然失效。

#11. 审计与可观测性

所有跨组件操作共享 request_id,关键状态变化另有稳定 operation_id。日志字段至少包含 component、deployment、organization、network、resource、revision、result 和 error code。指标只记录必要元数据,不记录业务内容。

必须可回答:

  • 谁在何时请求了什么;
  • 哪个权威 revision 批准或拒绝;
  • 哪个组件收到并应用了哪个 revision;
  • 当前失败是身份、权限、投影、平台执行还是网络路径;
  • 最后一次有效配置何时到期。

#12. 契约测试门

每个共享 schema 都必须具备:

  1. 真实生产类型序列化后对发布 schema 校验;
  2. 新增可选字段时旧消费者仍可解析;
  3. 缺少必需字段、未知 major、错误签名和回滚 revision 明确失败;
  4. snapshot/delta 断档触发 snapshot 恢复;
  5. 把读取失败注入为空结果时测试必须失败;
  6. 把授权过滤放宽或作用域移除时测试必须失败;
  7. 跨仓库 fixture 只从共享类型生成,不手工复制 JSON。

共享契约未通过这些门,任何组件都不能宣布对应功能完成。