| English Version | 中文版 |
Spec Number: NPS-1
Status: Proposed
Version: 0.12
Date: 2026-08-31
Port: 17433(默认,全协议族共用)
Authors: Ori Lynn / INNO LOTUS PTY LTD
本文档为 NCP 详细规范。套件总览见 NPS-0-Overview.cn.md。
本文档中的关键字 “MUST”、”MUST NOT”、”REQUIRED”、”SHOULD”、”SHOULD NOT”、”MAY” 按照 RFC 2119 解释。
NCP 是 NPS 的帧格式与通信基础层,定义 AI 节点间的帧结构、编码层级和语义压缩机制。所有上层协议(NWP/NIP/NDP/NOP)均以 NCP 帧为载体传输。
NCP 支持两种传输模式,帧格式完全一致,仅承载方式不同:
| 模式 | 承载 | 适用场景 | 推荐阶段 |
|---|---|---|---|
| HTTP 模式 | NCP 帧序列化在 HTTP Body 中,配合 X-NWP-* 请求头 |
Overlay 部署、与现有 Web 基础设施兼容、防火墙友好 | Phase 1 推荐 |
| 原生模式 | 直接 TCP/QUIC 连接,NCP 帧为线上格式,无 HTTP 开销 | 高性能 Agent-to-Agent、低延迟场景 | Phase 2+ |
HTTP 模式
POST /nwp/products/query HTTP/1.1
Host: api.example.com:17433
Content-Type: application/nwp-frame
X-NWP-Agent: urn:nps:agent:ca.innolotus.com:550e8400
[NCP Frame bytes / JSON]
原生模式
TCP connect → api.example.com:17433
→ HelloFrame (握手,0x06)
← CapsFrame (能力协商,0x04)
→ QueryFrame (0x10)
← CapsFrame (响应,0x04)
两种模式的帧 Payload 完全相同。原生模式下,错误通过 ErrorFrame (0xFE) 返回;HTTP 模式下,同时返回 HTTP 状态码(映射见 status-codes.cn.md)。
NPS 全协议族默认共用 端口 17433。不同协议帧通过帧类型码(Frame Type)路由:
| 帧类型范围 | 协议 |
|---|---|
| 0x01–0x0F | NCP |
| 0x10–0x1F | NWP |
| 0x20–0x2F | NIP |
| 0x30–0x3F | NDP |
| 0x40–0x4F | NOP |
| 0xF0–0xFF | Reserved(含 ErrorFrame 0xFE) |
实现 MAY 为各协议分配独立端口用于隔离部署,但默认行为 MUST 支持单端口复用。
| 模式 | 描述 | 典型帧序列 |
|---|---|---|
| 请求-响应 | 单次查询或操作 | QueryFrame → CapsFrame |
| 流式推送 | 大数据集或实时数据 | QueryFrame → StreamFrame × N → StreamFrame(is_last=true) |
| 增量订阅 | 变更通知 | 订阅请求 → DiffFrame × N |
| 多 Agent 同步 | 任务状态对齐 | AlignStream × N(见 NPS-5) |
┌──────────┐ connect() ┌──────────────────┐ anchor_ok ┌─────────────┐
│ CLOSED │ ──────────→ │ ANCHOR_NEGOTIATE │ ──────────→ │ ESTABLISHED │
└──────────┘ └──────────────────┘ └─────────────┘
│ timeout/error │ close()
↓ ↓
┌──────────┐ ┌──────────────┐
│ FAILED │ │ CLOSING │
└──────────┘ └──────────────┘
│
↓
┌──────────┐
│ CLOSED │
└──────────┘
原生模式下,连接建立 MUST 遵循以下握手流程:
Client (Agent) Server (Node)
│ │
│── TCP/QUIC connect ──────────────→ │
│── 8 字节 "NPS/1.0\n"(前导)─────→ │ 校验;不匹配则静默关闭(不发 ErrorFrame)
│── HelloFrame (0x06) ─────────────→ │ 声明客户端版本与能力
│ │ 版本协商 & 能力交集计算
│ ←──────────────── CapsFrame (0x04) │ 返回协商后的服务端能力
│ │ (若不兼容则返回 ErrorFrame + 断开)
│ [可选] 预加载 AnchorFrame │
│── GET /.schema ──────────────────→ │
│ ←──────────────── AnchorFrame(s) │
│ │
│ ── ESTABLISHED ───────────────── ESTABLISHED
版本协商规则
NCP-VERSION-INCOMPATIBLE 并关闭连接client.supported_encodings 按优先级选择,并受服务端支持集合约束。服务端在多个编码都可用时 SHOULD 尊重客户端优先级。binary_vector.v1;启用它并不表示 Tier-3 成为会话默认编码。max_frame_payload = min(client.max_frame_payload, server.max_frame_payload)身份与授权绑定
HelloFrame 只是传输 / 会话层的能力协商帧。除可选的 agent_id 路由提示外,它 MUST NOT 携带 bearer token、授权 scope 或任何其他凭据。CapsFrame 之后、第一个需认证的 NWP/NOP 帧之前,每连接一次地出示 NIP IdentFrame。服务端把该 IdentFrame 中验证通过的 NID、capabilities、scope 与 assurance level 绑定到该 NCP 会话。当该 Anchor 随后发生集群所有权转移(多 Anchor 高可用,NPS-CR-0009)时,服务端 MAY 关闭该原生连接;在连接丢失、或恢复(resumption)尝试后收到 NCP-NID-MISMATCH 的情况下,客户端 MUST 重新解析该集群(NDP §9,取最高 cluster_epoch)或改用 NWP anchor_failover 事件(NPS-2 §12.2)给出的 successor_nid,并对新的活跃 Anchor 重新建立会话。原生模式传输依 NPS-RFC-0006(Accepted)为规范性内容。QueryFrame、ActionFrame 与 SubscribeFrame 不含逐消息的 auth-token 字段。原生模式下消息的授权,基于会话绑定的身份再加上逐消息的 target / scope 数据来判定。HelloFrame;使用 bearer 凭据的部署在 HTTP 传输信封中出示凭据(例如 Authorization: Bearer ...),与 NWP 请求头并列。bearer 凭据不嵌入 NWP 帧 payload 内部。错误处理
若握手失败(版本不兼容、能力集为空等),Server MUST 返回:
{
"frame": "0xFE",
"status": "NPS-PROTO-VERSION-INCOMPATIBLE",
"error": "NCP-VERSION-INCOMPATIBLE",
"message": "No compatible NPS version",
"details": { "server_version": "0.4", "client_min_version": "0.5" }
}
状态:必选(NCP v0.7 起,由 NPS-RFC-0001 引入)。
原生模式下,每条 NCP 连接 MUST 在传输层(TCP / QUIC)握手完成之后、首个 HelloFrame 发出之前,由客户端发出一段 8 字节常量前导。HTTP 模式不受影响(Content-Type: application/nwp-frame 头部承担同样的识别职责)。
线上格式
偏移 0 1 2 3 4 5 6 7
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ N │ P │ S │ / │ 1 │ . │ 0 │\n │
└───┴───┴───┴───┴───┴───┴───┴───┘
0x4E 50 53 2F 31 2E 30 0A
十六进制:4E 50 53 2F 31 2E 30 0A。前导不是帧 —— 没有头部、没有 Flags、没有长度字段。帧类型字节 0x4E 已保留(见 frame-registry.yaml reservations: 段),MUST NOT 分配给任何 NCP 帧。
客户端行为
HelloFrame 紧跟前导一起 pipeline(无需等待 round-trip),因为服务端是按字节比较常量。服务端行为
b"NPS/1.0\n" 完全匹配,进入帧解析流程。ErrorFrame —— 对端尚未确认是否说 NCP,发出帧字节会向扫描器泄露协议细节。主版本语义
b"NPS/2.0\n" 等未来主版本前导已保留。NPS/2.0\n 时 MUST 关闭连接(未知 major)。MAY 在关闭之前写一条 32 字节的诊断 NPS-PREAMBLE-UNSUPPORTED-VERSION\n —— 因为对端已自我标识为 NPS 说话方,所以允许这次”破例”发数据。1.0 中的 0)已保留;v1 客户端 MUST 发 0,v1 服务端 MUST 仅接受 0。未来 RFC 可能定义次版本协商。错误
| 错误码 | 触发条件 |
|---|---|
NCP-PREAMBLE-INVALID(NPS-PROTO-PREAMBLE-INVALID) |
首 8 字节不匹配常量;500 ms 内静默关闭。 |
| (超时) | 10 秒内字节不足 8;静默关闭。线上不发任何编码。 |
服务端状态机(原生模式)
[LISTEN] ──accept──→ [PREAMBLE-WAIT] ──8 字节匹配──→ [FRAMING] ──HelloFrame──→ [HANDSHAKE] ──CapsFrame──→ [ESTABLISHED]
│
├──不匹配──→ [CLOSING](≤ 500 ms)
└──10 秒超时──→ [CLOSING](静默)
完整设计动机、备选方案、SDK 推进 phase,参见 NPS-RFC-0001。
本 Profile 定义可移植的原生模式 NCP 服务端在外部可观测层面的最小行为要求,对 TCP 与 QUIC 流式实现同等适用。
conformance/ncp/native_server_handshake_vectors.json
中的语言无关用例为规范性内容。
传输与读取边界
max_hello_payload 默认 65,535 字节,并 MUST 可配置为更低的部署上限。HelloFrame,且 EXT=0。帧类型错误、编码 Tier 为保留值、
ENC=1、EXT=1、头部/Payload 格式错误、Payload 超限或 Hello 超时,均导致静默关闭。
这些准入前失败 MUST NOT 发出 ErrorFrame。CapsFrame 并建立会话,要么发送一个 ErrorFrame
并关闭连接。在连接仍处于握手状态时,服务端 MUST NOT 继续读取应用帧。服务端 MUST 对外暴露有界的前导与 Hello 超时。部署 MAY 调低默认值,但 MUST NOT 在不受信任的监听端口上同时禁用这两个上限。
确定性协商
服务端按其配置的 Profile 对解码后的 Hello 求值:
min_version-nps_version 范围。省略 min_version 等同于该端的 nps_version。
版本使用严格的 major.minor 形式。会话版本取交集中的最高版本。无交集则返回 NCP-VERSION-INCOMPATIBLE。supported_encodings 列表中第一个同时被服务端启用、且为 msgpack 或 json
的 token。无稳定交集则返回 NCP-ENCODING-UNSUPPORTED。binary_vector.v1 仅在双端都声明时启用。它在 enabled_encodings 中追加在稳定默认之后,且永不成为默认值。supported_protocols 取双方列表的交集,按客户端偏好顺序排列。其中 MUST 含 ncp,否则服务端返回
NCP-VERSION-INCOMPATIBLE。max_frame_payload 与 max_concurrent_streams 取双端通告值的最小值。上限为零属非法。ext_support 仅在双端都通告 true 时启用。握手 CapsFrame 的 Payload 由握手状态区分,使用以下可移植字段。既有字段保持增量且向后兼容:
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
node_id |
string | 必填 | 服务端 NID |
caps |
array[string] | 必填 | 服务端应用能力 |
session_version |
string | 必填 | 协商出的协议版本 |
negotiated_encoding |
string | 必填 | 稳定默认编码:msgpack 或 json |
enabled_encodings |
array[string] | 必填 | 稳定默认编码,其后跟随协商出的扩展编码 |
supported_protocols |
array[string] | 必填 | 协商出的上层协议交集 |
max_frame_payload |
uint32 | 必填 | 协商出的 Payload 上限 |
ext_support |
bool | 必填 | 协商出的扩展帧头支持 |
max_concurrent_streams |
uint32 | 必填 | 协商出的并发流上限 |
anchor_ref |
string | 可选 | 首次提供的 Schema 引用 |
payload |
object | 可选 | 实现自定义的元数据 |
本 Profile 不新增任何帧类型或错误码。
默认帧头(4 字节)
Byte 0 Byte 1 Byte 2–3
┌───────────────┬───────────────┬───────────────────────────┐
│ Frame Type │ Flags │ Payload Length │
│ (1 byte) │ (1 byte) │ (2 bytes, BE) │
└───────────────┴───────────────┴───────────────────────────┘
扩展帧头(8 字节,EXT=1 时)
Byte 0 Byte 1 Byte 2–5 Byte 6–7
┌───────────────┬───────────────┬───────────────────────┬──────────────┐
│ Frame Type │ Flags │ Payload Length │ Reserved │
│ (1 byte) │ (1 byte) │ (4 bytes, BE) │ (2 bytes) │
└───────────────┴───────────────┴───────────────────────┴──────────────┘
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
┌───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┐
│ EXT │ RSV │ RSV │ RSV │ ENC │ FINAL │ T1 │ T0 │
└───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┘
| 位 | 名称 | 描述 |
|---|---|---|
| 0–1 | T0, T1 | 编码 Tier:00=Tier-1 JSON,01=Tier-2 MsgPack,10=Tier-3 BinaryVector v1,11=Reserved |
| 2 | FINAL | 流式帧最终块标志(StreamFrame 专用,其他帧固定为 1) |
| 3 | ENC | Payload 使用应用层 E2E 加密(见 §7.4);TLS 传输层加密独立,开发模式可为 0 |
| 4–6 | RSV | 保留,MUST 为 0,接收方 MUST 忽略 |
| 7 | EXT | 扩展帧头标志:0=默认 4 字节帧头(Payload ≤ 64KB),1=扩展 8 字节帧头(Payload ≤ 4GB) |
| 配置项 | 默认值 | 最大值 | 说明 |
|---|---|---|---|
max_frame_payload |
65,535 (64KB) | 4,294,967,295 (4GB) | 单帧最大 Payload 字节数 |
max_frame_payload 时,发送方 MUST 使用 StreamFrame 分片传输max_frame_payload 的协商通过 CapsFrame 在连接建立时完成分片规则
max_frame_payload 限制Schema 锚点帧,由 Node 发布,用于建立全局 Schema 引用,消除后续重复传输。
字段定义
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
frame |
uint8 | 必填 | 固定值 0x01 |
anchor_id |
string | 必填 | Schema 的 SHA-256 摘要,格式:sha256:{64位hex} |
schema |
object | 必填 | Schema 定义对象 |
schema.fields |
array | 必填 | 字段描述数组,见下 |
ttl |
uint32 | 可选 | 缓存有效期(秒),默认 3600,0 表示不缓存 |
supersedes |
string | 可选 | 本帧替代的旧 AnchorFrame 的 anchor_id。生产端 MAY 设置;当新旧两帧都在缓存中时,消费端 SHOULD 优先使用新帧(见 §5.5) |
valid_from |
string | 可选 | ISO-8601 UTC 时间戳,标记时序有效性窗口起点(如 2026-05-10T00:00:00Z)。在该时刻之前,锚点 MUST 视为无效(见 §5.5) |
valid_until |
string | 可选 | ISO-8601 UTC 时间戳,标记时序有效性窗口终点。在该时刻之后,锚点 MUST 视为无效(见 §5.5) |
schema.fields 元素
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
name |
string | 必填 | 字段名 |
type |
string | 必填 | 数据类型:string/uint64/int64/decimal/bool/timestamp/bytes/object/array |
semantic |
string | 可选 | 语义标注,格式:{domain}.{concept}(见语义类型系统) |
nullable |
bool | 可选 | 是否可为 null,默认 false |
anchor_id 计算规则
schema 字段按 RFC 8785(JSON Canonicalization Scheme, JCS) 序列化为规范化 JSONsha256:{lowercase hex}注:JCS 参考实现列表见 https://www.rfc-editor.org/rfc/rfc8785#appendix-A
示例
{
"frame": "0x01",
"anchor_id": "sha256:a3f9b2c1d4e5f6789012345678901234567890abcdef1234567890abcdef12",
"schema": {
"fields": [
{ "name": "id", "type": "uint64", "semantic": "entity.id" },
{ "name": "name", "type": "string", "semantic": "entity.label" },
{ "name": "price", "type": "decimal", "semantic": "commerce.price.usd" },
{ "name": "stock", "type": "uint64", "semantic": "commerce.inventory.count" }
]
},
"ttl": 3600
}
语义类型系统(部分)
| Semantic | 含义 |
|---|---|
entity.id |
实体唯一标识符 |
entity.label |
实体人类可读名称 |
entity.description |
实体描述文本 |
entity.timestamp.created |
创建时间 |
entity.timestamp.updated |
更新时间 |
commerce.price.usd |
美元价格 |
commerce.price.cny |
人民币价格 |
commerce.inventory.count |
库存数量 |
geo.latitude |
纬度 |
geo.longitude |
经度 |
增量数据帧,仅传输变更字段的 patch,适用于订阅或轮询场景。
字段定义
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
frame |
uint8 | 必填 | 固定值 0x02 |
anchor_ref |
string | 必填 | 基准 Schema 的 anchor_id |
base_seq |
uint64 | 必填 | 本 Diff 基于的版本序号 |
patch_format |
string | 可选 | 补丁格式:json_patch(默认)或 binary_bitset(Tier-2 推荐) |
patch |
array | bytes | 必填 | 补丁数据(格式由 patch_format 决定,见下) |
entity_id |
string | 可选 | 被更新实体的 ID |
patch_format: json_patch(默认,Tier-1 JSON 场景)
patch 为 JSON Patch 操作数组(RFC 6902),每个元素含 op、path、value。
patch_format: binary_bitset(Tier-2 MsgPack 场景,减少 15–20% 体积)
patch 为二进制字节流(MsgPack bin 类型),结构如下:
┌──────────────────────────────────────────────────────┐
│ Changed Fields Bitset(ceil(N/8) bytes,N=Schema字段数)│
│ Bit i=1 表示第 i 个字段有变更(按 schema.fields 顺序) │
├──────────────────────────────────────────────────────┤
│ Values(仅包含变更字段的新值,按字段顺序,MsgPack 编码) │
└──────────────────────────────────────────────────────┘
规则:
binary_bitsetbinary_bitset,MUST 返回 NCP-DIFF-FORMAT-UNSUPPORTEDpatch_format 省略时默认为 json_patch示例(json_patch)
{
"frame": "0x02",
"anchor_ref": "sha256:a3f9b2c1...",
"base_seq": 42,
"patch_format": "json_patch",
"entity_id": "product:1001",
"patch": [
{ "op": "replace", "path": "/price", "value": 299.00 },
{ "op": "replace", "path": "/stock", "value": 48 }
]
}
流式数据块帧,用于大数据集、实时推送或超帧大小的 Payload 分片。
字段定义
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
frame |
uint8 | 必填 | 固定值 0x03 |
stream_id |
string | 必填 | 流的唯一标识符(UUID v4) |
seq |
uint32 | 必填 | 块序号,从 0 递增,接收方按 seq 重组 |
is_last |
bool | 必填 | true 表示此为最后一块 |
anchor_ref |
string | 可选 | Schema anchor_id(首块携带,后续块可省略) |
data |
array | 必填 | 本块数据,符合 anchor_ref 所指 Schema |
window_size |
uint32 | 可选 | 接收方可缓冲的剩余帧数(见流量控制语义) |
error_code |
string | 可选 | 非空时表示流异常终止,is_last 强制为 true |
流量控制语义
NCP 提供应用层语义背压,补充 TCP/QUIC 传输层流量控制:
window_size,声明接收方剩余可接受帧数data=[], is_last=false),携带新的 window_size 以刷新窗口window_size=0 的更新帧,发送方 MUST 暂停,直到收到非零 window_sizewindow_size 未设置时,表示不启用应用层流量控制(依赖底层传输)NCP-STREAM-WINDOW-OVERFLOW 并可终止流封装完整响应体,最常见的响应帧类型。也用于连接建立时的能力协商。
字段定义
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
frame |
uint8 | 必填 | 固定值 0x04 |
anchor_ref |
string | 必填 | Schema anchor_id |
count |
uint32 | 必填 | data 数组长度,MUST 等于 len(data) |
data |
array | 必填 | 数据记录数组,每条记录符合 anchor_ref Schema |
next_cursor |
string | 可选 | 下一页游标,Base64-URL 编码,null 表示最后一页 |
token_est |
uint32 | 可选 | 本响应预估 CGN 消耗(见 token-budget.cn.md) |
tokenizer_used |
string | 可选 | 实际使用的 tokenizer 标识 |
cached |
bool | 可选 | true 表示本响应来自服务端缓存 |
request_id |
string | 可选 | 将 unary 响应与原始请求关联。同步 NWP 操作的生产端 SHOULD 复制请求帧的 request_id |
inline_anchor |
object | 可选 | Schema 已更新时内联返回最新 AnchorFrame,避免额外 RTT(见 §5.4) |
cached 描述的是完整 NWP 响应缓存,不得解释为模型 prefix/KV cache 命中。
底层 runtime 能报告模型缓存使用量时,协议专属 payload MAY 提供独立的 cache usage 对象。
连接协商 CapsFrame
在原生模式连接建立时,Node 通过 CapsFrame 返回协商后的能力声明:
{
"frame": "0x04",
"anchor_ref": "nps:system:caps",
"count": 1,
"data": [{
"nps_version": "0.4",
"session_version": "0.4",
"max_frame_payload": 65535,
"negotiated_encoding": "msgpack",
"enabled_encodings": ["msgpack", "binary_vector.v1"],
"supported_protocols": ["ncp", "nwp", "nip"],
"ext_support": true,
"max_concurrent_streams": 32,
"e2e_enc_algorithms": ["aes-256-gcm", "chacha20-poly1305"]
}]
}
数据响应示例
{
"frame": "0x04",
"anchor_ref": "sha256:a3f9b2c1...",
"count": 2,
"data": [
{ "id": 1001, "name": "iPhone 15 Pro", "price": 999.00, "stock": 42 },
{ "id": 1002, "name": "MacBook Air M3", "price": 1299.00, "stock": 15 }
],
"next_cursor": "eyJpZCI6MTAwM30",
"token_est": 180
}
⚠️ AlignFrame 已在 NCP v0.7 中标记为 Deprecated。 请使用 NOP AlignStream (0x43),见 NPS-5-NOP.cn.md。 AlignFrame 将在 NPS v1.0 中移除。
原生模式客户端握手帧,由 Agent(客户端)在连接建立后首先发送,声明自身协议版本与能力。Server 以 CapsFrame 响应(见 §2.6)。
命名说明:NIP 层已定义 IdentFrame (0x20) 用于身份证书交换。NCP HelloFrame 仅负责版本与能力协商,不携带身份信息,两者职责明确分离。
字段定义
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
frame |
uint8 | 必填 | 固定值 0x06 |
nps_version |
string | 必填 | 客户端支持的最高 NPS 版本,格式 "major.minor" |
min_version |
string | 可选 | 客户端支持的最低 NPS 版本,默认与 nps_version 相同 |
supported_encodings |
array[string] | 必填 | 支持的编码格式列表,如 ["binary_vector.v1", "msgpack", "json"],按优先级降序 |
supported_protocols |
array[string] | 必填 | 支持的上层协议列表,如 ["ncp", "nwp", "nip"] |
agent_id |
string | 可选 | Agent 的 NID(NIP 身份标识符),格式 urn:nps:agent:{domain}:{id} |
max_frame_payload |
uint32 | 可选 | 客户端可接受的最大帧 Payload 字节数,默认 65535 |
ext_support |
bool | 可选 | 是否支持扩展帧头(EXT=1),默认 false |
max_concurrent_streams |
uint32 | 可选 | 客户端可处理的最大并发流数,默认 32 |
e2e_enc_algorithms |
array[string] | 可选 | 客户端支持的 E2E 加密算法列表(见 §7.4),如 ["aes-256-gcm"] |
ping_interval_ms |
uint32 | 可选 | 期望的 keepalive 间隔(毫秒);0 = 禁用(默认)。非零时双端 SHOULD 按该间隔发送 NopFrame,并 MUST 在 3 × ping_interval_ms 内发出一个(NCP v0.8 §7.6)。 |
规则
X-NWP-* 请求头传递)HelloFrame 示例
{
"frame": "0x06",
"nps_version": "0.4",
"min_version": "0.3",
"supported_encodings": ["binary_vector.v1", "msgpack", "json"],
"supported_protocols": ["ncp", "nwp"],
"agent_id": "urn:nps:agent:ca.innolotus.com:550e8400",
"max_frame_payload": 65535,
"ext_support": false,
"max_concurrent_streams": 16,
"e2e_enc_algorithms": ["aes-256-gcm", "chacha20-poly1305"]
}
NPS 统一错误帧,所有协议层共用。在原生模式下承载错误响应。
字段定义
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
frame |
uint8 | 必填 | 固定值 0xFE |
status |
string | 必填 | NPS 状态码(见 status-codes.cn.md) |
error |
string | 必填 | 协议级错误码(如 NCP-ANCHOR-NOT-FOUND) |
message |
string | 可选 | 人类可读错误描述 |
details |
object | 可选 | 错误相关的结构化数据(如 anchor_ref、stream_id) |
示例
{
"frame": "0xFE",
"status": "NPS-CLIENT-NOT-FOUND",
"error": "NCP-ANCHOR-NOT-FOUND",
"message": "Schema anchor not found in cache, please resend AnchorFrame",
"details": { "anchor_ref": "sha256:a3f9b2c1..." }
}
无 Payload 的空帧。用作空闲连接上的 keepalive 探测,防止 NAT / 防火墙超时断链,并用于探测对端失活(NCP v0.8)。
字段定义
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
frame |
uint8 | 必填 | 固定值 0x07 |
规则
ping_interval_ms > 0 时,双端 SHOULD 按声明的间隔发送 NopFrame。若在 3 × ping_interval_ms 内未收到任何类型的帧,该端 SHOULD 发送 NCP-KEEPALIVE-TIMEOUT 并关闭连接NCP-FRAME-PAYLOAD-TOO-LARGE 拒绝(按 0 字节上限违规处理)AnchorFrame 由 Node(数据模型拥有者) 发布。Agent 通过读取 Node 清单或 Schema 端点获取 AnchorFrame,本地缓存后在后续请求中引用。
Agent Node
│ │
│── GET /.nwm ─────────────────→│ 读取节点清单,获取 schema_anchors
│←── NWM(schema_anchors) ───────│
│ │
│── GET /.schema ──────────────→│ 获取完整 AnchorFrame(可选,按需)
│←── AnchorFrame(schema) ───────│ Agent 本地缓存 anchor_id → schema
│ │
│── QueryFrame(anchor_ref) ────→│ 请求只携带 anchor_ref
│←── CapsFrame(anchor_ref) ─────│ 响应也只引用 anchor_ref
│ │
│── QueryFrame(anchor_ref) ────→│ 后续请求无需重传 Schema
│←── CapsFrame(anchor_ref) ─────│
anchor_id 为 keyttl 秒若 Agent 引用了已过期的 anchor_ref,Node 发现该 Schema 版本已更新时,处理行为取决于请求端 auto_anchor 标志(在 NWP QueryFrame 中定义):
inline_anchor 字段,包含最新 AnchorFrame,避免额外 RTT:{
"frame": "0x04",
"anchor_ref": "sha256:new_anchor...",
"count": 2,
"data": [ ... ],
"inline_anchor": {
"frame": "0x01",
"anchor_id": "sha256:new_anchor...",
"schema": { "fields": [ ... ] },
"ttl": 3600
}
}
Agent 收到含 inline_anchor 的响应后,MUST 更新本地缓存(替换旧 anchor_id 对应的 Schema)。
NCP-ANCHOR-STALE,由 Agent 主动请求新 AnchorFrame。若 Agent 引用了 Node 完全不认识的 anchor_ref(如手工构造、跨节点误引用),Node MUST 返回:
{
"frame": "0xFE",
"status": "NPS-CLIENT-NOT-FOUND",
"error": "NCP-ANCHOR-NOT-FOUND",
"details": { "anchor_ref": "sha256:..." }
}
注:
NCP-ANCHOR-STALE表示 anchor 存在但已更新;NCP-ANCHOR-NOT-FOUND表示 anchor 从未被此 Node 注册。
Schema 会随时间演进。NCP 定义了一套兼容性分类法,并规定了 AnchorFrame 跨版本生命周期内生产端与消费端的义务。§4.1 中的可选字段 supersedes、valid_from、valid_until 承载本节使用的版本血缘和时序有效性元数据。
任何 Schema 变更 MUST 归入下列兼容性分类之一。生产端 SHOULD 在发布说明中记录类别;消费端 MAY 据此判断是否必须重新拉取锚点。
| 分类 | 描述 | 向后兼容 |
|---|---|---|
additive_optional |
在 schema.fields 中新增可选字段(即 nullable=true 或带文档化默认值) |
是 |
additive_required |
在 schema.fields 中新增必填字段(无默认值) |
否 |
type_widening |
在不丢失既有合法值的前提下,扩大已有字段的类型(如 uint32 → uint64、string 枚举 → 开放 string) |
是 |
type_narrowing |
收窄已有字段的类型或取值域(如 string → 枚举、int64 → uint32) |
否 |
semantic_change |
线格式不变但已有字段的含义发生变化(如单位、币种、所指对象) | 视情况 |
field_remove |
从 schema.fields 中移除已有字段 |
否 |
任何归类为 additive_required、type_narrowing 或 field_remove 的变更都属于破坏性变更,MUST 产生新的 anchor_id。生产端 MUST 在新 AnchorFrame 上将 supersedes 设置为旧的 anchor_id,以便消费端追溯版本血缘。
对于 semantic_change,除非生产端能通过外带文档证明无消费端依赖原有含义,否则 MUST 视同破坏性变更处理。
当设置了 valid_from 和/或 valid_until 时,AnchorFrame 仅在半开区间 [valid_from, valid_until) 内有效。窗口之外:
valid_until 已过,消费端 SHOULD 立即从缓存中淘汰该锚点,无视剩余 ttlanchor_ref 返回 NCP-ANCHOR-NOT-FOUND若设置了 valid_until,生产端 MUST 在响应时间落在过期 60 秒内(即 now ≥ valid_until − 60s)的所有 CapsFrame 中携带 inline_anchor(包含后继 AnchorFrame)。这一约束界定了消费端仍在使用即将过期锚点的最坏窗口,确保后继锚点在当前锚点失效前抵达消费端。
ttl 窗口内引入破坏性变更(additive_required、type_narrowing、field_remove 或未缓解的 semantic_change)。后继锚点的 valid_from MUST ≥ 前序锚点的 valid_until(如已设置),否则 MUST ≥ prior_publish_time + prior.ttl。这保证缓存了前序锚点的消费端不会在窗口中段观测到破坏性切换。supersedes,无论兼容性分类如何additive_optional 变更——即引用已知锚点的负载中出现未知额外字段时,即使消费端尚未刷新锚点,也 MUST NOT 因此失败supersedes 关联)同时存在于缓存中时优先使用更新的 AnchorFrame,并 SHOULD 主动淘汰被替代项valid_until 已过,MUST 在发起新的引用请求前刷新NPS 采用两级错误体系:
| 错误码 | NPS 状态码 | 描述 |
|---|---|---|
NCP-ANCHOR-NOT-FOUND |
NPS-CLIENT-NOT-FOUND |
anchor_ref 引用的 Schema 不存在 |
NCP-ANCHOR-SCHEMA-INVALID |
NPS-CLIENT-BAD-FRAME |
AnchorFrame 中 Schema 格式不合法 |
NCP-ANCHOR-ID-MISMATCH |
NPS-CLIENT-CONFLICT |
相同 anchor_id 收到不同 Schema(锚点污染攻击防御) |
NCP-FRAME-UNKNOWN-TYPE |
NPS-CLIENT-BAD-FRAME |
未知帧类型码 |
NCP-FRAME-PAYLOAD-TOO-LARGE |
NPS-LIMIT-PAYLOAD |
Payload 超过协商的 max_frame_payload |
NCP-FRAME-FLAGS-INVALID |
NPS-CLIENT-BAD-FRAME |
Flags 字段中保留位非零 |
NCP-BINARY-VECTOR-MALFORMED |
NPS-CLIENT-BAD-FRAME |
Tier-3 BinaryVector Payload 格式不合法 |
NCP-BINARY-VECTOR-DIM-MISMATCH |
NPS-CLIENT-BAD-FRAME |
BinaryVector marker 的维度与向量段不匹配 |
NCP-BINARY-VECTOR-INDEX-INVALID |
NPS-CLIENT-BAD-FRAME |
BinaryVector marker 引用了不存在的向量段 |
NCP-BINARY-VECTOR-DTYPE-UNSUPPORTED |
NPS-CLIENT-BAD-FRAME |
BinaryVector marker 使用了不支持的 dtype |
NCP-BINARY-VECTOR-TRUNCATED |
NPS-CLIENT-BAD-FRAME |
BinaryVector 向量段被截断 |
NCP-STREAM-SEQ-GAP |
NPS-STREAM-SEQ-GAP |
StreamFrame 序号不连续 |
NCP-STREAM-NOT-FOUND |
NPS-STREAM-NOT-FOUND |
stream_id 引用的流不存在 |
NCP-STREAM-LIMIT-EXCEEDED |
NPS-STREAM-LIMIT |
超出单连接最大并发流数 |
NCP-ENCODING-UNSUPPORTED |
NPS-SERVER-ENCODING-UNSUPPORTED |
请求的编码 Tier 不被支持 |
NCP-ANCHOR-STALE |
NPS-CLIENT-CONFLICT |
anchor_ref 存在但 Schema 已更新(配合 inline_anchor 使用) |
NCP-DIFF-FORMAT-UNSUPPORTED |
NPS-CLIENT-BAD-FRAME |
DiffFrame 使用了接收方不支持的 patch_format |
NCP-VERSION-INCOMPATIBLE |
NPS-PROTO-VERSION-INCOMPATIBLE |
客户端 min_version 高于 Server 支持版本 |
NCP-STREAM-WINDOW-OVERFLOW |
NPS-STREAM-LIMIT |
发送方在窗口耗尽后继续发送 |
NCP-PREAMBLE-INVALID |
NPS-PROTO-PREAMBLE-INVALID |
原生模式连接首 8 字节非 b"NPS/1.0\n";服务端 500 ms 内静默关闭(不发 ErrorFrame)—— 见 §2.6.1 |
NCP-REKEY-REQUIRED |
NPS-PROTO-VERSION-INCOMPATIBLE |
E2E 加密信道已达到 rekey 阈值(2^32 帧或 24 小时);对端 MUST 在继续发送加密帧之前发起密钥轮换 |
NCP-KEEPALIVE-TIMEOUT |
NPS-SERVER-TIMEOUT |
在 3 × ping_interval_ms 内未收到任何帧(含 NopFrame);连接将被关闭 |
NCP-EARLY-DATA-REJECTED |
NPS-PROTO-VERSION-INCOMPATIBLE |
NPS 帧出现在 QUIC/TLS 0-RTT early data 中;NPS 应用数据只能在握手确认后开始 |
HTTP 模式下的状态码映射见 status-codes.cn.md。
CapsFrame 和 QueryFrame 应在 TLS 层由传输协议防御重放。若在非 TLS 场景(开发模式)下,Agent SHOULD 在请求中携带 nonce 字段。
Schema 由 Node 发布,Agent 只读引用。Node MUST 对 anchor_id 做幂等校验——相同 anchor_id 对应的 Schema MUST 始终一致。Agent 若检测到同一 anchor_id 返回了不同 Schema,SHOULD 断开连接并报告安全事件。
Node SHOULD 限制单连接最大并发流数(推荐默认值:32,通过 CapsFrame max_concurrent_streams 协商)。超出限制时返回 NCP-STREAM-LIMIT-EXCEEDED。
密钥轮换(Rekeying,NCP v0.7):使用 ENC=1 时,发送方 MUST 在触及以下任一阈值之前发起 rekey:(a) 当前密钥已发送 2^32 帧,(b) 密钥建立至今已满 24 小时。触发方发送
error: "NCP-REKEY-REQUIRED"的 ErrorFrame 作为优雅信号;对端 MUST 在继续发送加密帧之前完成密钥轮换以作确认。密钥轮换沿用与初始密钥派生相同的 ECDH + HKDF 算法,实现 SHOULD 使用 X25519。
当帧头 Flags ENC=1 时,帧 Payload 使用应用层端到端加密。E2E 加密独立于 TLS 传输层,适用于多跳 Relay 场景中 Relay 不可信的情况。
算法选择
通过握手 CapsFrame 的 e2e_enc_algorithms 字段协商,优先级从高到低:
| 算法 | 标识符 | 密钥长度 | Nonce | Tag |
|---|---|---|---|---|
| AES-256-GCM | aes-256-gcm |
256-bit | 12 bytes | 16 bytes |
| ChaCha20-Poly1305 | chacha20-poly1305 |
256-bit | 12 bytes | 16 bytes |
ENC=1 时 Payload 布局
┌────────────────┬──────────────────────────────────────┬─────────────┐
│ Nonce │ Encrypted Payload │ Auth Tag │
│ (12 bytes) │ (原始 Payload 密文) │ (16 bytes) │
└────────────────┴──────────────────────────────────────┴─────────────┘
Payload Length(帧头中)= 12 + len(encrypted) + 16实现要求
e2e_enc_algorithms 为空,表示不支持 E2E 加密,两端 MUST NOT 设置 ENC=1NCP-ENC-NOT-NEGOTIATED 并丢弃该帧新增错误码
| 错误码 | NPS 状态码 | 描述 |
|---|---|---|
NCP-ENC-NOT-NEGOTIATED |
NPS-CLIENT-BAD-FRAME |
ENC=1 但会话未协商 E2E 加密算法 |
NCP-ENC-AUTH-FAILED |
NPS-CLIENT-BAD-FRAME |
E2E 加密 Auth Tag 验证失败(可能被篡改) |
原生模式的传输安全层由 NPS-RFC-0006 §6(Proposed)规范性定义。概括而言,非 local-dev 的原生模式连接 MUST:
nps/1.0;禁止 STARTTLS 式的带内升级。NCP-NID-MISMATCH 拒绝证书 NID 与 IdentFrame NID 不一致的情况。该绑定即传输层准入闸门,由 nps-ingress(L2)守护进程终结。
长连接形态的原生模式连接(TCP/QUIC)可能被 NAT 设备或防火墙静默切断。NopFrame(§4.8)提供应用层 keepalive:
ping_interval_ms > 0,则在没有其他流量时,双方 SHOULD 按大致该间隔发送 NopFrame3 × ping_interval_ms 内未收到任何类型帧的一端,MUST 发送 NCP-KEEPALIVE-TIMEOUT 并在 500 ms 内关闭连接ping_interval_ms,另一方 SHOULD 采用该声明值作为共同间隔SO_KEEPALIVE)与 QUIC idle timeout 属互补机制;实现 SHOULD 在应用层 NopFrame 协议之外,同时在传输层配置这两者以下要求 ID 冻结
runtime_hardening_vectors.json
所验证的可移植 runtime 行为:
C、S 为 client 与 server 提供的非零
ping_interval_ms。两者都为零时禁用应用层 keepalive;否则先忽略零值,再取
max(1000, min(C, S))。3 × effective_interval 未收到合法入站帧时,
连接原子进入 closing,最多发送一个 NCP-KEEPALIVE-TIMEOUT,取消所有 stream,
并在 500 ms 内关闭 carrier。timeout error 之后不得再发送应用帧。NCP-FRAME-PAYLOAD-TOO-LARGE 拒绝,且不得重置任一时钟。NCP-EARLY-DATA-REJECTED 拒绝;握手确认后允许重试。NCP-NID-MISMATCH 拒绝并关闭连接。所有帧类型支持以下编码层级,通过帧头 Flags T0/T1 位标识:
| Tier | 格式 | Flag | 适用场景 |
|---|---|---|---|
| Tier-1 | JSON | 00 |
开发调试、兼容模式 |
| Tier-2 | MsgPack(Binary) | 01 |
生产环境,~60% 体积压缩 |
| Tier-3 | BinaryVector v1 | 10 |
向量密集型帧;MessagePack 元数据 + little-endian float32 原始向量段 |
| — | Reserved | 11 |
保留 |
默认值是策略级默认:生产环境使用 Tier-2,开发/兼容场景使用 Tier-1。实现 SHOULD 在握手或配置阶段选择稳定的会话编码策略,并持续一致地应用;只有当策略明确允许某类帧或某个阶段使用不同 Tier 时,该帧 MAY 使用不同 Tier。
Tier-3 是可选能力。除非会话已协商 binary_vector.v1,发送方 MUST NOT 发出 Flags.T1T0 = 10;不支持或未协商 Tier-3 的接收方 MUST 以 NCP-ENCODING-UNSUPPORTED 拒绝该帧。Flags.T1T0 = 11 仍为保留值,MUST 以 NCP-FRAME-FLAGS-INVALID 拒绝。
Tier-3 BinaryVector v1 是帧 Payload 的编码格式,不替代 NWP 语义。帧类型字节仍决定逻辑帧类型;Tier-3 Payload 只负责把该帧的元数据与稠密向量段编码到线上格式。
协商 token:binary_vector.v1。
二进制布局:
| Offset | Size | 字段 | 编码 |
|---|---|---|---|
| 0 | 4 | Magic | ASCII NPBV |
| 4 | 1 | Version | 0x01 |
| 5 | 1 | Flags | v1 中固定 0x00;接收方 MUST 拒绝非零值 |
| 6 | 2 | vector_count |
uint16,大端 |
| 8 | 4 | metadata_len |
uint32,大端 |
| 12 | 4 | Reserved | MUST 为 0 |
| 16 | metadata_len |
Metadata | MessagePack map,字段名与 Tier-2 相同 |
| … | variable | Vector segments | 重复的 dim:uint32_be + dim 个 little-endian float32 |
Metadata MUST 保留逻辑帧对象。被移入二进制段的向量字段用 marker 对象替换:
{
"$nps_binary_vector": 0,
"dtype": "float32",
"dim": 1536
}
NCP v0.9 的标准绑定是 NWP QueryFrame.vector_search.vector。marker index 从 0 开始,MUST 指向后续向量段之一。dtype MUST 为 float32;向量值为 IEEE-754 binary32 little-endian。MatrixTensor、float16、量化 int8、多向量 schema 绑定留给后续 CR。
inline_anchor 后必须先验证 anchor_id(JCS+SHA-256),验证通过后再更新缓存Tier2MsgPackCodec 建议使用 MessagePack AOT 代码生成,避免高频帧解析的反射开销(实现建议,不影响协议互操作性)| 版本 | 日期 | 变更 |
|---|---|---|
| 0.12 | 2026-08-31 | alpha.19 连接加固 Profile:确定性的有效 keepalive 间隔与收发时钟、单次 timeout-close 迁移、无 payload Nop 强制、拒绝 QUIC/TLS 0-RTT NPS 帧、保持身份绑定的 QUIC migration,以及 carrier/application 双层背压组合。新增共享 runtime hardening 向量与 NCP-EARLY-DATA-REJECTED。 |
| 0.11 | 2026-07-29 | 新增 §2.6.2 原生服务端互操作 Profile:认证先于前导的顺序要求、前导与 Hello 分别有界读取、分配安全的 Hello 上限、准入前失败的静默行为、确定性的版本/编码/协议/上限协商、可移植的握手 Caps 字段,以及共享的 native_server_handshake_vectors.json。属增量变更;无新增帧类型与错误码。 |
| 0.10 | 2026-07-05 | 原生模式传输正式成为规范性内容(NPS-RFC-0006 Accepted)。新增原生模式下跨 Anchor 故障转移的会话连续性(NPS-CR-0009):多 Anchor 所有权转移后,若连接丢失或恢复尝试返回 NCP-NID-MISMATCH,客户端经 NDP §9(取最高 cluster_epoch)重新解析,或使用 NWP anchor_failover 的 successor_nid,并重新建立会话。无新增帧与错误码(复用 NCP-NID-MISMATCH)。 |
| 0.9 | 2026-06-27 | 激活 Tier-3 BinaryVector v1(Flags.T1T0 = 10),协商 token 为 binary_vector.v1;定义 NPBV Payload 布局、MessagePack metadata marker、float32 little-endian 向量段,以及 NWP QueryFrame.vector_search.vector 绑定;0b11 仍为保留值。 |
| 0.8 | 2026-06-12 | 新增 §7.5 原生模式 TLS 绑定与双向认证,归纳 NPS-RFC-0006 §6(草案→提议):套件级 ALPN nps/1.0(取代临时值 ncp/1)、原生模式 over TCP 强制 TLS 封装、与 NIP 证书的 mTLS + session-NID 绑定、TLS 1.3 会话恢复票据;新增错误码 NCP-NID-MISMATCH。gate nps-ingress(L2)守护进程。 |
| 0.8 | 2026-06-03 | NopFrame (0x07) keepalive/心跳帧:空 Payload、双向;HelloFrame.ping_interval_ms 声明期望间隔(0 = 禁用,默认);在 3 × 间隔内未收到任何帧时返回 NCP-KEEPALIVE-TIMEOUT 错误码;§7.6 对端失活检测规则。 |
| 0.7 | 2026-05-31 | 通过 HelloFrame/CapsFrame 协商 max_concurrent_streams(uint32,默认 32);超限时返回 NCP-STREAM-LIMIT-EXCEEDED;拒绝进行中的流时 MUST 发送 mid-stream ErrorFrame(此前为 MAY);E2E rekey 协议(§7.4):2^32 帧或 24 小时触发,以 NCP-REKEY-REQUIRED 作为信号;QUIC 流映射:每个 NCP 信道一条双向流,HelloFrame 走 stream 0。详见 NPS-RFC-0006。 |
| 0.6 | 2026-04-25 | 新增 §2.6.1 原生模式连接前导(8 字节常量 b"NPS/1.0\n");在 frame-registry.yaml 中保留帧类型字节 0x4E;新增错误码 NCP-PREAMBLE-INVALID 和状态码 NPS-PROTO-PREAMBLE-INVALID。详见 NPS-RFC-0001。 |
| 0.4 | 2026-04-14 | 新增 IdentFrame (0x06) 握手帧;新增 §2.6 连接握手序列及版本协商规则;anchor_id 计算明确引用 RFC 8785 JCS;DiffFrame 新增 patch_format 字段(json_patch / binary_bitset);CapsFrame 新增 inline_anchor;StreamFrame 流量控制语义正式化(window_size 协议);§7.4 E2E 加密节(ENC 标志、AES-256-GCM / ChaCha20-Poly1305、Payload 布局);§5.4 auto-anchor 协议(NCP-ANCHOR-STALE + inline_anchor);新增错误码 NCP-ANCHOR-STALE、NCP-DIFF-FORMAT-UNSUPPORTED、NCP-VERSION-INCOMPATIBLE、NCP-STREAM-WINDOW-OVERFLOW、NCP-ENC-NOT-NEGOTIATED、NCP-ENC-AUTH-FAILED |
| 0.3 | 2026-04-12 | 传输双模(HTTP/原生);统一端口 17433;可配置帧大小(EXT 位);ErrorFrame (0xFE);NPS 状态码体系;Tier-3 标记 Reserved;AnchorFrame 所有权明确为 Node 发布;Token 估算改用 CGN |
| 0.2 | 2026-04-10 | AnchorFrame/DiffFrame/StreamFrame/CapsFrame/AlignFrame 定义;Flags 字段逐位定义;AlignFrame 标记 Deprecated |
| 0.1 | 2026-03-01 | 初始帧格式与编码层级定义 |
归属:LabAcacia / INNO LOTUS PTY LTD · Apache 2.0