NPS-Release

English 中文版

NPS-2: Neural Web Protocol (NWP)

Spec Number: NPS-2 Status: Proposed Version: 0.13 Date: 2026-05-28 Port: 17433 (default, shared) / 17434 (optional dedicated) Authors: Ori Lynn / INNO LOTUS PTY LTD Depends-On: NPS-1 (NCP v0.7), NPS-3 (NIP v0.9), NPS-4 (NDP v0.8)

This document is the NWP detailed specification. For a suite overview see NPS-0-Overview.md.


1. Terminology

Keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHOULD”, “MAY” in this document are interpreted per RFC 2119.


2. Protocol Overview

NWP defines how AI Agents access web data and services. Agents use nwp:// addresses to reach three types of Neural Nodes (Memory / Action / Complex). Node responses are directly machine-understandable, requiring no semantic parsing layer.

2.1 Node Types

Type Responsibility Typical Data Sources
Memory Node Data storage and retrieval, no compute logic RDS, NoSQL, file systems, vector databases
Action Node Executes operations, returns results or side effects Functions, external APIs, message queues, Webhooks
Complex Node Mixed data and operations, with sub-node references All of the above + sub-node references
Anchor Node Cluster control plane and external entry point — routes inbound NWP Action/Query frames to member nodes via NOP, optionally maintains member-node topology AaaS platforms, multi-agent service gateways, sub-cluster routers
Bridge Node Translates between NPS frames and non-NPS protocols (HTTP/HTTPS, gRPC, MCP, A2A) Calls to legacy REST APIs, gRPC services, Model Context Protocol servers, Agent-to-Agent endpoints

A node MAY simultaneously carry more than one role (for example, a single deployment can be both a Memory Node and an Anchor Node when role separation is unnecessary). Multi-role declaration travels in the NDP Announce frame’s node_roles field (NPS-4 §3.1).

Anchor Node and Bridge Node were introduced together by NPS-CR-0001, replacing the original Gateway Node type:

Removed types

Gateway Node (removed in v1.0-alpha.3) — Split into Anchor Node (cluster entry / NOP routing) and Bridge Node (NPS↔non-NPS protocol translation). See NPS-CR-0001 for full rationale and migration notes. Implementations MUST reject the legacy node_type: "gateway" with NWP-MANIFEST-NODE-TYPE-REMOVED and the legacy node_roles: ["gateway"] (or legacy node_kind: "gateway") with NDP-ANNOUNCE-ROLE-REMOVED; response SHOULD include a hint pointing to NPS-CR-0001.

Node Role Resolution

Two fields participate in node-role declaration at different protocol layers. They are intentionally distinct in name and semantics:

Field Protocol Layer Cardinality Authoritative for
node_roles NDP Announce (NPS-4 §3.1) Discovery Array — all roles the node carries Full role set; used for discovery filtering and cluster membership
node_type NWP NWM (§4.1) Service String — single operative role Which role this particular /.nwm endpoint is serving

Constraint: node_type MUST be one of the values declared in the node’s node_roles. Validators SHOULD verify this against cached NDP Announce data when available.

Multi-role nodes (e.g., node_roles: ["anchor", "memory"]) MAY expose separate /.nwm endpoints on different paths or ports, each advertising a different node_type; or may choose a single dominant node_type matching the primary role served at that endpoint. In either case the constraint holds: node_type ∈ node_roles.

Anchor Node — detailed semantics

An Anchor Node MUST:

  1. Accept inbound NWP Action and Query frames addressed to the cluster (i.e. to the Anchor’s NID rather than to a specific member NID).
  2. Dispatch frames to appropriate member nodes based on their declared capabilities and current load. The reference dispatch path converts an ActionFrame into a NOP TaskFrame and delegates to a local NOP orchestrator (see NPS-AaaS-Profile §2).
  3. Aggregate outbound responses from member nodes into a single response stream toward the originating caller.
  4. Optionally maintain a registry of member nodes within its cluster (their NIDs, declared capabilities, and activation_mode per NPS-Node Profile §6). Member nodes register on cluster join via NDP Announce frames carrying cluster_anchor referencing the Anchor Node’s NID; deregistration follows standard NDP offline semantics.

A cluster MUST have at least one Anchor Node. High-availability deployments MAY operate multiple Anchor Nodes for the same cluster; the consensus protocol between Anchor Nodes is implementation-defined and is deferred to NPS-AaaS Profile L3.

Anchor Nodes that maintain a member registry MUST expose it via the reserved query types topology.snapshot and topology.stream (§12), per NPS-CR-0002. Both are mandatory at NPS-AaaS Profile L2 and above.

Bridge Node — detailed semantics

A Bridge Node MUST:

  1. Accept inbound NWP frames carrying a bridge_target parameter that identifies the external protocol and endpoint. The bridge_target object has the following standard fields: protocol (string, required — one of "http", "grpc", "mcp", "a2a"); endpoint (string URL, required); headers (object string→string, optional — extra HTTP headers the bridge passes to the upstream). Third-party adapters MAY extend with additional fields; unknown fields MUST be ignored by consumers.
  2. Produce outbound requests in the target protocol’s format.
  3. Translate target-protocol responses back into NWP frames (typically CapsFrame).

Bridge Nodes are stateless per request and do not participate in cluster topology. A single Bridge Node MAY translate to multiple distinct external protocols; deployments MAY operate dedicated Bridge Nodes per protocol for isolation.

Standard external protocols expected to be supported by reference Bridge Node implementations:

Additional protocol adapters MAY be registered through future CRs. The set of supported protocols is declared in NDP Announce.bridge_protocols (NPS-4 §3.1).

2.2 Overlay Mode

Attach a NWP interface to an existing HTTP service. The server distinguishes visitors by request headers:

Request contains X-NWP-Agent or HelloFrame  →  return application/nwp-*
Regular browser request (no above markers)  →  return text/html (normal website)

In Overlay mode, NWP uses HTTP transport with frames serialized in the HTTP body. See NPS-1-NCP.md §2.2.


3. Node Address Specification

3.1 nwp:// URL Syntax (ABNF)

nwp-url     = "nwp://" host [":" port] "/" node-path ["/" sub-path]
host        = <RFC 3986 host>
port        = 1*DIGIT               ; default 17433
node-path   = segment *("/" segment)
sub-path    = "query" / "stream" / "invoke" / "subscribe" / "actions"
            / ".schema" / ".nwm"
segment     = 1*(ALPHA / DIGIT / "-" / "_")

3.2 Sub-Path Conventions

Sub-Path Method Node Types Description
/query POST Memory Single structured query (returns CapsFrame)
/stream POST Memory Streaming query (returns StreamFrame sequence)
/invoke POST Action / Complex Operation invocation endpoint
/subscribe POST Memory Change subscription endpoint (HTTP mode, SSE)
/actions GET Action / Complex List callable operations (returns NWM actions subset JSON)
/.schema GET All Schema definition (returns AnchorFrame JSON)
/.nwm GET All Full node manifest (returns NWM JSON)

4. Neural Web Manifest (NWM)

Every node MUST expose a machine-readable manifest at /.nwm, MIME type: application/nwp-manifest+json.

4.1 Complete Field Definitions

Field Type Required Description
nwp string Required NWP version, currently "0.4"
node_id string Required Node NID, format: urn:nps:node:{host}:{path}
node_type string Required "memory" / "action" / "complex" / "anchor" / "bridge". The legacy value "gateway" was removed in v1.0-alpha.3 (see §2.1 Removed types and NPS-CR-0001); parsers MUST reject it with NWP-MANIFEST-NODE-TYPE-REMOVED. Any other unrecognized value MUST be rejected with NWP-MANIFEST-NODE-TYPE-UNKNOWN. This field declares the operative role this NWP service endpoint is serving — it MUST be one of the values in the node’s NDP Announce.node_roles (NPS-4 §3.1). For multi-role nodes, node_type selects which role is active at this endpoint; see §2.1 Node Role Resolution for the full constraint.
display_name string Optional Human-readable node name
manifest_version string Optional Manifest version identifier (ETag), for conditional request cache control
min_agent_version string Optional Minimum NPS version the Agent must support, format "major.minor"; Agents below this version MUST be rejected with NWP-MANIFEST-VERSION-UNSUPPORTED
min_assurance_level string Optional One of "anonymous" (default) / "attested" / "verified" (see NIP §5.1.1). Requests presenting a level lower than this MUST be rejected with NWP-AUTH-ASSURANCE-TOO-LOW (NPS-AUTH-FORBIDDEN); response SHOULD include a hint pointing to a CA enrolment URL. Default "anonymous" preserves backward compatibility with v1.0-alpha.2 nodes. Per-action override is permitted via the min_assurance_level field on an individual ActionSpec (§4.6). (NPS-RFC-0003)
wire_formats array Required Supported encoding formats: ["ncp-capsule", "msgpack", "json"]
preferred_format string Required Preferred format
schema_anchors object Optional Pre-declared schema anchors, {name: anchor_id}
capabilities object Required Node capability declarations, see §4.2
data_sources array Optional Underlying data source identifier list
auth object Required Authentication requirements, see §4.3
rate_limits object Optional Rate limit declarations, see §4.4
actions object Conditionally Required MUST be populated for Action/Complex nodes; operation registry, see §4.6
endpoints object Required URLs for each functional endpoint
graph object Optional Sub-node references (Complex Node only), see §11
tokenizer_support array Optional List of tokenizers supported by the node (see token-budget.md)
stability string Optional Lifecycle stage: "experimental" / "stable" / "deprecated". Marketplace / NeuronHub discovery clients use this to filter or warn on non-stable services. Default: "stable" (backward-compatible — pre-0.11 manifests are treated as stable). Per-action override permitted via ActionSpec.stability (§4.6).
sla object Optional SLO commitments for the node, see §4.7. Advisory only; the protocol does not enforce these. Per-action override permitted via ActionSpec.sla (§4.6).
billing object Optional Commercial metadata for the node (metering profile + price hint), see §4.8. Advisory only; the protocol does not collect or settle charges. Per-action override permitted via ActionSpec.billing (§4.6).
trust_anchors array of strings Optional NIDs of CA nodes the Anchor accepts as IdentFrame issuers (e.g. ["urn:nps:agent:ca.example.com:root"]). Consumers SHOULD use this to pre-validate their issuer before connecting. When absent, the node accepts any CA trusted by the NIP verification chain.

4.2 capabilities Field

Capability Key Type Description
query bool Supports QueryFrame (single query)
stream_query bool Supports streaming queries (StreamFrame response)
aggregate bool Supports aggregation queries (QueryFrame.aggregate)
subscribe bool Supports change subscriptions (DiffFrame push)
subscribe_filter bool Supports subscriptions with filter conditions
vector_search bool Supports vector similarity search
token_budget_hint bool Supports trimming responses based on CGN budget
ext_frame bool Supports extended frame header (large frame mode)
e2e_enc bool Supports NCP E2E encryption (ENC=1, see NPS-1-NCP §7.4)
inline_anchor bool Supports returning updated AnchorFrame inline in responses

4.3 auth Field

Field Type Required Description
required bool Required Whether authentication is required
identity_type string Conditionally Required "nip-cert" / "bearer" / "none"
trusted_issuers array Conditionally Required List of trusted CA URLs (required when identity_type is nip-cert)
required_capabilities array Optional Capabilities the Agent MUST hold, e.g. ["nwp:query"]
scope_check string Optional Scope validation mode: "prefix" (default) / "exact"
ocsp_url string Optional OCSP validation endpoint

4.4 rate_limits Field

Field Type Description
requests_per_minute uint32 Max requests per Agent per minute
requests_per_day uint32 Max requests per Agent per day
max_concurrent_streams uint32 Max concurrent streams per Agent
max_subscriptions uint32 Max concurrent subscriptions per Agent

4.4a sla Field

Advisory SLO commitments. Clients (especially marketplace / NeuronHub aggregators) display or filter on these values; the protocol does not police them. All sub-fields are Optional.

Field Type Description
p95_latency_ms uint32 Self-declared 95th-percentile end-to-end latency in milliseconds, measured at the node’s /.nwm reference endpoint
availability string Self-declared availability target as a decimal-fraction string, e.g. "0.999" for “three nines”. Format: 0\.[0-9]+; clients SHOULD interpret missing as “best effort”.
sla_tier string Optional named tier for marketplace listing: "best-effort" / "standard" / "premium". Free-form strings outside this enum are reserved for future extension and SHOULD be ignored by current clients.

4.4b billing Field

Advisory commercial metadata. The protocol does not authorize, meter, or settle charges; this field exists so marketplace listings, agent autoscalers, and budget gates can make informed decisions before invoking. All sub-fields are Optional.

Field Type Description
metering_profile string Identifier of the metering model: "free" / "metered" / "flat-rate". Free-form values outside this enum are reserved and SHOULD be treated as "metered" by conservative clients.
billing_unit string Unit string when metering_profile = "metered", e.g. "per-token" / "per-request" / "per-cgn" / "per-second".
price_hint string Indicative price in ISO-4217 currency-prefixed decimal form, e.g. "USD 0.0002" per billing_unit. Hint only — the operator’s external contract is authoritative.
currency string ISO-4217 currency code (e.g. "USD", "EUR", "CNY"). Optional convenience field; price_hint already encodes the currency prefix.

4.5 Complete NWM Example

{
  "nwp": "0.4",
  "node_id": "urn:nps:node:api.example.com:orders",
  "node_type": "complex",
  "display_name": "Order Management Node",
  "manifest_version": "etag-2026041402",
  "min_agent_version": "0.3",
  "wire_formats": ["ncp-capsule", "msgpack", "json"],
  "preferred_format": "ncp-capsule",
  "schema_anchors": {
    "order":   "sha256:a3f9b2c1...",
    "product": "sha256:b2c1d3e4..."
  },
  "capabilities": {
    "query": true,
    "stream_query": true,
    "aggregate": true,
    "subscribe": true,
    "subscribe_filter": true,
    "vector_search": false,
    "token_budget_hint": true,
    "ext_frame": false,
    "e2e_enc": false,
    "inline_anchor": true
  },
  "data_sources": ["rds:orders_db"],
  "auth": {
    "required": true,
    "identity_type": "nip-cert",
    "trusted_issuers": ["https://ca.mycompany.com"],
    "required_capabilities": ["nwp:query", "nwp:invoke"],
    "scope_check": "prefix"
  },
  "rate_limits": {
    "requests_per_minute": 300,
    "requests_per_day": 50000,
    "max_concurrent_streams": 10,
    "max_subscriptions": 5
  },
  "stability": "stable",
  "sla": {
    "p95_latency_ms": 250,
    "availability": "0.999",
    "sla_tier": "standard"
  },
  "billing": {
    "metering_profile": "metered",
    "billing_unit": "per-request",
    "price_hint": "USD 0.0002",
    "currency": "USD"
  },
  "actions": {
    "orders.create": {
      "description": "Create a new order",
      "params_anchor": "sha256:create_params...",
      "result_anchor": "sha256:order...",
      "async": true,
      "idempotent": true,
      "timeout_ms_default": 10000,
      "timeout_ms_max": 60000,
      "required_capability": "nwp:invoke",
      "stability": "stable",
      "sla": { "p95_latency_ms": 800, "sla_tier": "premium" },
      "billing": { "metering_profile": "metered", "billing_unit": "per-request", "price_hint": "USD 0.001" }
    },
    "orders.cancel": {
      "description": "Cancel an existing order",
      "params_anchor": "sha256:cancel_params...",
      "result_anchor": "sha256:cancel_result...",
      "async": false,
      "idempotent": true,
      "timeout_ms_default": 5000,
      "timeout_ms_max": 10000,
      "required_capability": "nwp:invoke",
      "stability": "experimental"
    }
  },
  "endpoints": {
    "query":     "nwp://api.example.com/orders/query",
    "stream":    "nwp://api.example.com/orders/stream",
    "invoke":    "nwp://api.example.com/orders/invoke",
    "subscribe": "nwp://api.example.com/orders/subscribe",
    "actions":   "nwp://api.example.com/orders/actions",
    "schema":    "nwp://api.example.com/orders/.schema"
  },
  "tokenizer_support": ["cl100k_base", "claude"]
}

NWM Conditional Requests

Agents SHOULD cache the NWM and use manifest_version for conditional requests: in HTTP mode via the If-None-Match: {manifest_version} header; if the manifest is unchanged the server returns 304 Not Modified.

4.6 NWM Action Registry

The actions field is an {action_id: ActionSpec} dictionary. Action/Complex/Anchor Nodes MUST declare all callable operations here.

ActionSpec Field Definitions

Field Type Required Description
description string Optional Human-readable description of the operation
params_anchor string Optional anchor_id of the parameter Schema (Agent uses this to validate ActionFrame.params)
result_anchor string Optional anchor_id of the result Schema (CapsFrame uses this anchor_ref on success)
async bool Required Whether async execution is supported (if true, ActionFrame may set async=true)
idempotent bool Optional Whether the operation is idempotent (Agents may safely retry if true)
timeout_ms_default uint32 Optional Default timeout in milliseconds
timeout_ms_max uint32 Optional Maximum allowed timeout in milliseconds
required_capability string Optional NIP capability required to invoke this operation, e.g. "nwp:invoke"
min_assurance_level string Optional Per-action assurance-level override: "anonymous" / "attested" / "verified". When present, takes precedence over the top-level NWM min_assurance_level for requests targeting this action. Requests presenting a lower level MUST be rejected with NWP-AUTH-ASSURANCE-TOO-LOW. (NPS-RFC-0003)
stability string Optional Per-action lifecycle stage override ("experimental" / "stable" / "deprecated"). When present, takes precedence over the top-level NWM stability for this action. Marketplace clients SHOULD surface deprecated actions even when the node-level stability is "stable".
sla object Optional Per-action SLO override; same shape as the top-level sla field (§4.4a). When present, fields supplied here override the matching top-level fields for this action only; unsupplied sub-fields fall through to the top-level.
billing object Optional Per-action commercial-metadata override; same shape as the top-level billing field (§4.4b). Field-level fallback semantics match sla.

/actions Endpoint

An Agent sends GET /actions; the node returns the full NWM actions field as JSON (for dynamic discovery without downloading the entire NWM):

{
  "node_id": "urn:nps:node:api.example.com:orders",
  "actions": {
    "orders.create": { ... },
    "orders.cancel": { ... }
  }
}

5. Schema Retrieval Flow

Agents retrieve a Node’s schema via the following flow (AnchorFrames are published by Nodes; Agents are read-only consumers):

Agent                              Node
  │                                  │
  │── GET /.nwm ─────────────────→   │  1. Read manifest, get schema_anchors
  │←── NWM JSON ──────────────────   │     { "order": "sha256:a3f9..." }
  │                                  │
  │── GET /.schema ──────────────→   │  2. Fetch complete AnchorFrame (on demand)
  │←── AnchorFrame JSON ──────────   │     Agent caches locally
  │                                  │
  │── QueryFrame(anchor_ref) ────→   │  3. Query carries only anchor_ref
  │←── CapsFrame(anchor_ref) ─────   │

Agents SHOULD preload AnchorFrames for all schema_anchors declared in the NWM on first connection, reducing latency for subsequent requests.


6. QueryFrame (0x10)

Used for structured data queries on Memory Nodes.

6.1 Field Definitions

Field Type Required Description
frame uint8 Required Fixed value 0x10
type string Optional Reserved query type identifier per §12. When set, type-specific fields apply and anchor_ref semantics are defined by the type. Absent: per-anchor query behavior below
anchor_ref string Conditionally Required Schema anchor_id; may be omitted for aggregation queries or when type selects a reserved type that does not require it
auto_anchor bool Optional If true and the anchor is stale, the Node automatically attaches the latest AnchorFrame in the response. Default: true
stream bool Optional If true, triggers streaming query mode (see §6.6); response is a StreamFrame sequence rather than a CapsFrame
aggregate object Optional Aggregation operation (see §6.7); when set, anchor_ref may be omitted
filter object Optional Filter conditions, see §6.2
fields array Optional List of fields to return; omit to return all fields
limit uint32 Optional Maximum records to return, default 20, max 1000; for streaming queries, max records per frame
cursor string Optional Pagination cursor from the previous response’s next_cursor
order array Optional Sort rules, see §6.3
vector_search object Optional Vector similarity search, see §6.4
token_budget uint32 Optional CGN budget limit (native mode equivalent of X-NWP-Budget)
tokenizer string Optional Tokenizer identifier in use (native mode equivalent of X-NWP-Tokenizer)
depth uint8 Optional Node graph traversal depth, default 1, max 5 (native mode equivalent of X-NWP-Depth)
request_id string Optional UUID v4 for request tracing; echoed back by the node in response and logs

6.2 Filter Syntax

Operator Meaning Example
$eq Equal { "status": { "$eq": "active" } }
$ne Not equal { "status": { "$ne": "deleted" } }
$lt Less than { "price": { "$lt": 500 } }
$lte Less than or equal { "price": { "$lte": 500 } }
$gt Greater than { "stock": { "$gt": 0 } }
$gte Greater than or equal { "rating": { "$gte": 4.0 } }
$in In list { "category": { "$in": ["phone", "tablet"] } }
$nin Not in list { "tag": { "$nin": ["discontinued"] } }
$contains String contains (case-sensitive) { "name": { "$contains": "Pro" } }
$between Range (inclusive on both ends) { "price": { "$between": [100, 500] } }
$exists Field exists check { "thumbnail": { "$exists": true } }
$regex Regex match (UTF-8) { "sku": { "$regex": "^PROD-[0-9]{4}$" } }
$and Logical AND { "$and": [ {...}, {...} ] }
$or Logical OR { "$or": [ {...}, {...} ] }
$not Logical NOT { "$not": { "status": { "$eq": "deleted" } } }

$regex Security Constraints: Pattern length ≤ 256 characters; nested quantifiers (e.g. (a+)+) are prohibited; nodes MUST perform ReDoS detection and return NWP-QUERY-REGEX-UNSAFE on violation.

Filter nesting depth MUST be ≤ 8 levels.

6.3 Sort Rules

{ "order": [{ "field": "price", "dir": "ASC" }, { "field": "name", "dir": "ASC" }] }
{
  "vector_search": {
    "field": "embedding",
    "vector": [0.12, -0.34, 0.56],
    "top_k": 10,
    "threshold": 0.85,
    "metric": "cosine"
  }
}

Supported metric values: cosine (default), euclidean, dot_product. Nodes declare support via capabilities.vector_search=true; unsupported nodes return NWP-QUERY-VECTOR-UNSUPPORTED.

6.5 Single Query Complete Example

{
  "frame": "0x10",
  "anchor_ref": "sha256:a3f9b2c1...",
  "auto_anchor": true,
  "filter": {
    "$and": [
      { "category": { "$eq": "electronics" } },
      { "price": { "$lt": 500 } },
      { "stock": { "$gt": 0 } }
    ]
  },
  "fields": ["id", "name", "price", "stock"],
  "limit": 20,
  "order": [{ "field": "price", "dir": "ASC" }],
  "token_budget": 800,
  "tokenizer": "cl100k_base",
  "request_id": "550e8400-e29b-41d4-a716-446655440001"
}

6.6 Streaming Query Protocol

When QueryFrame contains stream: true (or uses the /stream sub-path), the node responds with a StreamFrame (0x03) sequence rather than a single CapsFrame. Requires capabilities.stream_query=true.

Streaming Query Flow

Agent                              Node
  │                                  │
  │── QueryFrame(stream:true) ────→  │
  │                                  │  Query in batches, limit records per batch
  │  ←── StreamFrame(seq=0) ───────  │  First frame, contains anchor_ref and estimated_total
  │  ←── StreamFrame(seq=1) ───────  │  Subsequent frames, data is the next batch of records
  │       ...                        │
  │  ←── StreamFrame(is_last=true) ─ │  Final frame, is_last=true, data may be empty

First Frame Additional Fields (StreamFrame Extension)

For streaming queries, the first frame (seq=0) SHOULD include metadata:

Field Type Description
estimated_total uint64 Estimated total records matching the filter; -1 means unknown
request_id string Echo of the QueryFrame’s request_id

Pagination vs. Streaming

6.7 Aggregation Queries

When QueryFrame contains an aggregate field, the node returns aggregated results rather than raw records. Requires capabilities.aggregate=true.

aggregate Field Definitions

Field Type Required Description
operations array Required List of aggregation operations, see below
group_by array Optional Grouping field list, e.g. ["category", "status"]
having object Optional Post-grouping filter (same syntax as filter, but field names are aliases)

operation Element

Field Type Required Description
func string Required COUNT / SUM / AVG / MIN / MAX / COUNT_DISTINCT
field string Conditionally Required Field to aggregate (COUNT may omit, meaning row count)
alias string Required Result field name

Aggregation Query Example

{
  "frame": "0x10",
  "filter": { "status": { "$eq": "active" } },
  "aggregate": {
    "operations": [
      { "func": "COUNT", "alias": "total" },
      { "func": "SUM",   "field": "price",  "alias": "revenue" },
      { "func": "AVG",   "field": "rating", "alias": "avg_rating" }
    ],
    "group_by": ["category"],
    "having": { "total": { "$gt": 10 } }
  },
  "order": [{ "field": "revenue", "dir": "DESC" }],
  "request_id": "550e8400-e29b-41d4-a716-446655440002"
}

Aggregation Response (CapsFrame)

Aggregation responses do not use a business schema; anchor_ref is fixed as "nps:system:aggregate:result":

{
  "frame": "0x04",
  "anchor_ref": "nps:system:aggregate:result",
  "count": 3,
  "data": [
    { "category": "electronics", "total": 142, "revenue": 89450.00, "avg_rating": 4.3 },
    { "category": "clothing",    "total": 87,  "revenue": 12300.00, "avg_rating": 4.1 },
    { "category": "books",       "total": 56,  "revenue": 3200.00,  "avg_rating": 4.6 }
  ]
}

7. ActionFrame (0x11)

Used for operation invocation on Action Nodes and Complex Nodes.

7.1 Field Definitions

Field Type Required Description
frame uint8 Required Fixed value 0x11
action_id string Required Operation identifier, format: {domain}.{verb}; system-reserved operations see §7.3
params object Optional Operation parameters; schema defined by NWM actions.{action_id}.params_anchor
idempotency_key string Optional Idempotency key (UUID v4), valid for 24 hours
timeout_ms uint32 Optional Timeout in milliseconds, default 5000, max 300000
async bool Optional If true, execute asynchronously; response returns task_id
callback_url string Optional Callback URL for async task completion (https:// only)
priority string Optional Task priority: "low" / "normal" (default) / "high"
request_id string Optional UUID v4 for request tracing (echoed in response and task status)

7.2 Async Task State Machine

PENDING → RUNNING → COMPLETED
                  ↘ FAILED
                  ↘ CANCELLED

For async execution, the initial response (CapsFrame):

{
  "task_id": "uuid-v4",
  "status": "pending",
  "poll_url": "nwp://api.example.com/orders/actions/status/uuid-v4",
  "estimated_ms": 3000,
  "request_id": "550e8400-..."
}

7.3 System-Reserved Operations

All nodes supporting async Actions MUST implement:

action_id Description Required Params Response
system.task.status Poll task status { "task_id": "uuid" } Task status object (see below)
system.task.cancel Cancel a task { "task_id": "uuid" } { "cancelled": true } or error

system.task.status Response

{
  "task_id": "uuid-v4",
  "status": "running",
  "progress": 0.42,
  "created_at": "2026-04-14T10:00:00Z",
  "updated_at": "2026-04-14T10:00:05Z",
  "request_id": "550e8400-...",
  "result": null,
  "error": null
}

7.4 Complete Example

{
  "frame": "0x11",
  "action_id": "orders.create",
  "params": { "product_id": 1001, "quantity": 2 },
  "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
  "timeout_ms": 10000,
  "async": true,
  "callback_url": "https://agent.myapp.com/callbacks/nwp",
  "priority": "normal",
  "request_id": "550e8400-e29b-41d4-a716-446655440003"
}

8. SubscribeFrame Overview (0x12)

Used to establish change subscriptions on Memory and Anchor Nodes. The authoritative v0.13 wire shape is defined in §13 (CR-0006): subscription_id, filter, heartbeat_interval_ms, max_events, and opaque cursor.

Earlier alpha drafts used action, stream_id, heartbeat_interval, and resume_from_seq. Those names are retired for NWP v0.13 and MUST NOT be emitted by conformant alpha.11+ producers. Consumers MAY accept them only as a pre-alpha.11 compatibility fallback, but MUST normalize internally to the §13 fields.

8.1 Subscription in HTTP Mode (SSE)

POST /nwp/orders/subscribe HTTP/1.1
Content-Type: application/nwp-frame

[SubscribeFrame bytes]

HTTP/1.1 200 OK
Content-Type: text/event-stream

data: [DiffFrame bytes, base64]

data: [DiffFrame bytes, base64]

9. HTTP Headers (HTTP Mode)

9.1 Request Headers

Header Required Description
X-NWP-Agent Conditionally Required Agent NID (required when auth.required=true)
Authorization Conditionally Required HTTP bearer credential when auth.identity_type = "bearer". Not used in native mode; native authentication is session-bound via NCP/NIP IdentFrame.
X-NWP-Budget Optional CGN budget limit (uint32)
X-NWP-Tokenizer Optional Tokenizer identifier used by the Agent
X-NWP-Depth Optional Node graph traversal depth, default 1, max 5
X-NWP-Encoding Optional Request encoding tier: json/msgpack, default msgpack
X-NWP-Request-ID Optional UUID v4 request tracing ID; echoed back by the node in the response header
If-None-Match Optional NWM conditional request; value is manifest_version
Content-Type Required application/nwp-frame

9.2 Response Headers

Header Description
X-NWP-Schema anchor_id used in the response
X-NWP-Tokens Actual CGN consumed
X-NWP-Tokens-Native Native token consumption
X-NWP-Tokenizer-Used Tokenizer actually used
X-NWP-Cached true indicates a cache hit
X-NWP-Node-Type Node type
X-NWP-Request-ID Echo of the requester’s X-NWP-Request-ID (node MAY auto-generate if not provided)
X-NWP-Rate-Limit Max requests per minute
X-NWP-Rate-Remaining Remaining requests this minute
X-NWP-Rate-Reset Rate limit window reset time (Unix timestamp)
Content-Type application/nwp-capsule (normal response) / application/nwp-error+json (error response)

9.3 Native Mode Field Mapping

HTTP Header QueryFrame Field ActionFrame Field
X-NWP-Agent — (declared as HelloFrame agent_id hint; authenticated identity is the session-bound NIP IdentFrame) Same
X-NWP-Budget token_budget
X-NWP-Tokenizer tokenizer
X-NWP-Depth depth
X-NWP-Request-ID request_id request_id

9.4 HTTP Mode Error Response Format

In HTTP mode, error responses use the following format, Content-Type: application/nwp-error+json:

{
  "status": "NPS-CLIENT-NOT-FOUND",
  "error": "NWP-ACTION-NOT-FOUND",
  "message": "Action 'orders.ship' is not registered on this node",
  "details": { "action_id": "orders.ship" },
  "request_id": "550e8400-e29b-41d4-a716-446655440003"
}

The HTTP status code is determined by the NPS status code mapping; see status-codes.md.

Field Descriptions

Field Type Required Description
status string Required NPS status code
error string Required Protocol-level error code (e.g. NWP-ACTION-NOT-FOUND)
message string Optional Human-readable description
details object Optional Structured additional error information
request_id string Optional Echo of the X-NWP-Request-ID from the request

10. Complete Request/Response Example

HTTP Mode Query Request

POST /nwp/orders/query HTTP/1.1
Host: api.example.com:17433
X-NWP-Agent: urn:nps:agent:ca.innolotus.com:550e8400
X-NWP-Budget: 1200
X-NWP-Tokenizer: cl100k_base
X-NWP-Request-ID: 550e8400-e29b-41d4-a716-446655440001
Content-Type: application/nwp-frame

[QueryFrame: { anchor_ref, filter, fields, limit, auto_anchor }]

Success Response

HTTP/1.1 200 OK
X-NWP-Schema: sha256:a3f9...
X-NWP-Tokens: 380
X-NWP-Request-ID: 550e8400-e29b-41d4-a716-446655440001
X-NWP-Rate-Limit: 300
X-NWP-Rate-Remaining: 248
Content-Type: application/nwp-capsule

[CapsFrame]

Error Response

HTTP/1.1 404 Not Found
X-NWP-Request-ID: 550e8400-e29b-41d4-a716-446655440001
Content-Type: application/nwp-error+json

{ "status": "NPS-CLIENT-NOT-FOUND", "error": "NWP-QUERY-FIELD-UNKNOWN", ... }

11. Complex Node — Node Graph

Complex Nodes declare sub-node references in the NWM:

{
  "graph": {
    "refs": [
      { "rel": "user",    "node": "nwp://api.myapp.com/users" },
      { "rel": "payment", "node": "nwp://pay.myapp.com/transactions" }
    ],
    "max_depth": 2
  }
}

Agents control traversal depth via the X-NWP-Depth header (HTTP mode) or the QueryFrame depth field (native mode). Nodes MUST detect circular references (return NWP-GRAPH-CYCLE) and maintain a sub-node URL whitelist (SSRF prevention).


12. Reserved Query Types

The type field on QueryFrame (§6.1) and SubscribeFrame (§13.1) opts a request into a reserved query type with specification-defined semantics. Identifiers in the topology.* namespace are reserved for cluster topology operations on Anchor Nodes; all reserved namespaces below are mandatory for the node roles indicated.

Namespace Owning role Mandatory at Operations
topology.* Anchor Node (§2.1) NPS-AaaS Profile L2 (services/NPS-AaaS-Profile.md §4.3) topology.snapshot (§12.1), topology.stream (§12.2)

When type is absent, default per-anchor query/subscribe semantics apply (§6, §8). When type is set, the per-type fields documented below apply and any conflicting standard fields (e.g. anchor_ref, top-level filter) are ignored unless the per-type schema explicitly carries them.

Implementations that do not recognize a reserved type value MUST reject the request with NWP-RESERVED-TYPE-UNSUPPORTED (§13) so the caller can distinguish “unknown reserved operation” from “action_id not found” (NWP-ACTION-NOT-FOUND) and fall back or fail explicitly.

12.1 topology.snapshot

Single-shot retrieval of an Anchor Node’s cluster topology.

Property Value
Frame QueryFrame (0x10) with type = "topology.snapshot"
Required of All Anchor Nodes (mandatory at NPS-AaaS Profile L2 and above)
Idempotent Yes
Caching Responses MAY be cached client-side; the version field correlates the snapshot with subsequent topology.stream events

Request fields (top-level on QueryFrame, supplementing §6.1):

Field Type Required Description
type string Required Constant "topology.snapshot"
topology object Required Container for topology-specific parameters per below
topology.scope string Required "cluster" for the Anchor’s own cluster; "member" for a single member (requires topology.target_nid)
topology.include array of strings Optional Subset of ["members", "capabilities", "tags", "metrics"]. Default ["members"]. The capabilities and metrics schemas are implementation-defined and MAY be empty
topology.depth uint8 Optional For sub-Anchor members, controls recursion. 1 (default) lists sub-Anchors as references only; 2+ recurses. Anchor Nodes MAY cap depth and set truncated: true when exceeded. Sub-Anchor recursion at depth ≥ 2 is OPTIONAL at L2; clients SHOULD recurse manually by issuing one snapshot per sub-Anchor
topology.target_nid string Conditionally Required Required when topology.scope = "member"; the NID of the member to introspect

Response: CapsFrame (0x04) with anchor_ref = "nps:system:topology:snapshot" and a single-element data array carrying the snapshot payload below.

{
  "frame": "0x04",
  "anchor_ref": "nps:system:topology:snapshot",
  "count": 1,
  "data": [{
    "version": 142,
    "anchor_nid": "urn:nps:node:labacacia:host-abc123",
    "cluster_size": 23,
    "members": [
      {
        "nid": "urn:nps:agent:labacacia:host-abc123-sess-aaa",
        "node_roles": ["memory"],
        "activation_mode": "ephemeral",
        "tags": ["dev", "library"],
        "joined_at": "2026-04-15T10:23:00Z",
        "last_seen": "2026-04-26T14:55:00Z"
      },
      {
        "nid": "urn:nps:node:labacacia:host-def456",
        "node_roles": ["anchor"],
        "activation_mode": "resident",
        "child_anchor": true,
        "member_count": 7,
        "tags": ["sub-cluster", "training"]
      }
    ],
    "truncated": false
  }]
}

Snapshot payload fields:

Field Type Required Description
version uint64 Required Monotonically increasing topology version. Resets only on Anchor restart / rebase (§12.3)
anchor_nid string Required NID of the responding Anchor Node
cluster_size uint32 Required Total direct members, regardless of depth truncation
members array of member objects Required Per the member object schema below
truncated bool Optional True iff topology.depth cap was hit; otherwise omitted or false

Member object schema:

Field Type Required Description
nid string Required Member NID
node_roles array of strings Required NDP node_roles — the full role set declared by this member (NPS-4 §3.1)
activation_mode string Required NDP activation_mode — one of ephemeral / resident / hybrid (NPS-4)
child_anchor bool Optional True if this member is itself an Anchor Node of a sub-cluster; implies member_count
member_count uint32 Conditionally Required Required when child_anchor = true; count of the sub-Anchor’s direct members
tags array of strings Optional NDP-declared tags
joined_at string Optional RFC 3339 timestamp; first observed
last_seen string Optional RFC 3339 timestamp; most recent NDP Announce
capabilities object Optional Returned only if requested via topology.include; schema implementation-defined
metrics object Optional Returned only if requested via topology.include; schema implementation-defined

12.2 topology.stream

Continuous topology change feed for an Anchor Node’s cluster.

Property Value
Frame SubscribeFrame (0x12) with type = "topology.stream"
Required of All Anchor Nodes (mandatory at NPS-AaaS Profile L2 and above)
Cancelable Yes — by closing the subscription transport or receiving/sending a terminal ErrorFrame

Request fields (top-level on SubscribeFrame, supplementing §13.1):

Field Type Required Description
type string Required Constant "topology.stream"
subscription_id string Required Client-generated UUID v4 used to correlate the topology stream and pushed DiffFrames
cursor string Optional Opaque server-issued resume cursor from a previous subscription. When present, cursor takes precedence over topology.since_version
topology object Required Container for topology-specific parameters per below
topology.scope string Required "cluster" (default for Anchor’s own cluster); future scopes reserved
topology.filter object Optional Reduces event volume. Supported keys: tags_any (array, match-any), tags_all (array, match-all), node_roles (array — filter by role, matches members whose node_roles intersects the given values). Anchor Nodes MUST reject unsupported filter keys with NWP-TOPOLOGY-FILTER-UNSUPPORTED
topology.since_version uint64 Optional Topology-specific initial resume hint for clients that have a snapshot version but do not yet have an opaque cursor. Anchor Node MUST replay missed events when feasible; if the version is outside the retention window, MUST emit a resync_required event and the client MUST issue a fresh topology.snapshot

For type = "topology.stream", cursor is the canonical v0.13 resume mechanism. topology.since_version is accepted only as a topology-specific bootstrap hint when no cursor is available; when both fields are present, cursor takes precedence.

Events are pushed as DiffFrame (0x02) with the §13.2 subscription event envelope. For topology.stream subscriptions, subscription_id identifies the stream, event_type carries one of the topology event types below (extending the default "create" / "update" / "delete" enum), seq is the post-event topology version (§12.3), and payload carries the event-specific data.

event_type Trigger payload shape
member_joined NDP Announce from a node naming this Anchor as cluster_anchor Full member object (§12.1)
member_left Member explicitly leaves OR exceeds NDP liveness TTL { "nid": "urn:nps:..." }
member_updated Existing member’s metadata changes (tags, activation_mode, capabilities, etc.) { "nid": "urn:nps:...", "changes": { "<field>": <new value>, ... } } — field-level diff only; reassembly is the client’s responsibility
anchor_state Anchor cluster status change relevant to subscribers. Payload carries a discriminator field selecting one of the sub-types below { "field": "<sub-type>", "details": { ... } }
resync_required Subscriber MUST tear down its local view and issue a fresh topology.snapshot followed by a new topology.stream subscription. Triggers: (a) topology.since_version is no longer replayable; (b) any anchor_state sub-type that invalidates the prior version counter (e.g. version_rebased); (c) server-side state loss requiring re-subscription { "reason": "<version_too_old | anchor_rebased | server_state_lost>" }. This event MAY omit seq and the subscriber MUST issue a fresh topology.snapshot

anchor_state sub-types (selected by the payload field discriminator):

field Phase Trigger details shape
version_rebased Phase 1–2 Anchor restarted and reset its monotonic version counter (§12.3). Subscribers MUST treat as equivalent to resync_required { "previous_version": <uint64>, "new_version": <uint64> }
anchor_failover Phase 3 (reserved slot) Active Anchor handed cluster ownership to a peer Anchor (multi-Anchor HA). Phase 1–2 implementations MUST NOT emit this sub-type; subscribers receiving it MAY ignore it { "successor_nid": "urn:nps:..." } (placeholder; finalised in Phase 3 CR)
anchor_quorum_lost Phase 3 (reserved slot) Anchor cluster lost quorum and is operating in degraded read-only mode. Phase 1–2 implementations MUST NOT emit this sub-type { "quorum_size": <uint32>, "available": <uint32> } (placeholder; finalised in Phase 3 CR)

Implementations MUST treat unknown anchor_state.field values as forward-compatible and ignore them rather than tearing down the subscription, so future Phase 3 sub-types can be introduced without a wire break.

Standard SubscribeFrame heartbeats (§13.2) operate unchanged. Cancellation is transport-level for v0.13: either side MAY close the subscription stream after emitting a terminal ErrorFrame when an error reason is available.

12.3 Versioning and Consistency Model

Guaranteed:

Not guaranteed:

Restart and rebase: An Anchor Node MAY rebase its version counter on restart. When rebasing, the Anchor MUST emit an anchor_state event with field: "version_rebased" to all active subscribers; subscribers MUST treat this as equivalent to resync_required and issue a fresh topology.snapshot.

12.4 Out of Scope


13. SubscribeFrame (0x12) — Formal Specification

SubscribeFrame opens a server-push subscription on a Memory or Anchor Node. The server streams matching events as DiffFrame messages until the subscription is closed.

13.1 Request fields

Field Type Required Description
frame uint8 Required Fixed value 0x12
subscription_id string Required Client-generated UUID v4; used to correlate events and cancel the subscription
type string Optional Reserved subscribe type identifier per §12. When set, type-specific fields apply and anchor_ref semantics are defined by the type. Absent: per-anchor subscribe behavior below
anchor_ref string Conditionally Required anchor_id of the subscribed data. Required for default per-anchor subscriptions; omitted when a reserved type defines its own target semantics (for example topology.stream)
filter object Optional Same filter syntax as QueryFrame filter (§6); if absent, all events match
heartbeat_interval_ms uint32 Optional If set, server MUST emit a heartbeat DiffFrame (empty payload, event_type = "heartbeat") at this interval; default 0 (no heartbeat)
max_events uint32 Optional Server closes the subscription after delivering this many events; 0 = unlimited
cursor string Optional Resume from a prior position; if the cursor is expired the server MUST return NWP-SUBSCRIBE-SEQ-TOO-OLD

13.2 Lifecycle

  1. Client sends SubscribeFrame → server responds with CapsFrame (subscription_id echoed, status = "open")
  2. Server streams DiffFrame events; each carries the subscription_id in an EXT header or equivalent transport metadata
  3. Client cancels by closing the subscription transport; server MAY also close when max_events is reached
  4. On server-side error, server MUST send a terminal ErrorFrame with the appropriate NWP-SUBSCRIBE-* code before closing

Subscription-pushed DiffFrames add the following event-envelope fields to the standard DiffFrame fields:

Field Type Required Description
subscription_id string Required Associated subscription ID
seq uint64 Required except terminal resync_required Monotonically increasing event sequence number within the subscription; reserved subscribe types MAY bind this to a domain-specific version, such as topology version (§12.3)
event_type string Required "create" / "update" / "delete" / "heartbeat" / "error" for default subscriptions. Reserved subscribe types (§12) MAY define additional values
timestamp string Optional Time of change (ISO 8601)
payload object Optional Event-specific payload. Heartbeats use an empty payload
cgn_est uint32 Optional Estimated CGN token cost of this push event’s payload. Nodes SHOULD populate this field on each pushed DiffFrame so subscribers can perform Agent-side cumulative-budget accounting per token-budget.md §7.2. Absent means the node does not provide a per-event estimate; Agents MAY estimate locally via UTF-8/4 fallback

Cursor semantics

13.3 Error codes

The following error codes (defined in §14) apply to SubscribeFrame operations:


14. Error Codes

Error Code NPS Status Code Description
NWP-AUTH-NID-SCOPE-VIOLATION NPS-AUTH-FORBIDDEN Agent scope does not cover the target node
NWP-AUTH-NID-EXPIRED NPS-AUTH-UNAUTHENTICATED NID certificate has expired
NWP-AUTH-NID-REVOKED NPS-AUTH-UNAUTHENTICATED NID has been revoked
NWP-AUTH-NID-UNTRUSTED-ISSUER NPS-AUTH-UNAUTHENTICATED NID issuer not in trusted_issuers
NWP-AUTH-NID-CAPABILITY-MISSING NPS-AUTH-FORBIDDEN Agent lacks a capability required by the node
NWP-AUTH-ASSURANCE-TOO-LOW NPS-AUTH-FORBIDDEN Agent’s assurance_level is below the node’s min_assurance_level (or the per-action override). Response SHOULD include a hint pointing to a CA enrolment URL. (NPS-RFC-0003)
NWP-AUTH-REPUTATION-BLOCKED NPS-AUTH-FORBIDDEN The receiving Node’s reputation_policy (Phase 2 NWM field — see NPS-RFC-0004 §4.4) matched a reject_on rule against the requesting subject_nid. Response SHOULD include the matching incident + severity + log entry seq for traceability. The field shape that produces this error lands at NWP v0.13 (Phase 2); the error code itself is reserved at NWP v0.13 (Phase 1) so SDKs can begin recognising it without round-tripping through a future spec bump. (NPS-RFC-0004)
NWP-QUERY-FILTER-INVALID NPS-CLIENT-BAD-PARAM Filter syntax is invalid or nesting is too deep
NWP-QUERY-FIELD-UNKNOWN NPS-CLIENT-BAD-PARAM fields references a non-existent field
NWP-QUERY-CURSOR-INVALID NPS-CLIENT-BAD-PARAM cursor value cannot be decoded or has expired
NWP-QUERY-REGEX-UNSAFE NPS-CLIENT-BAD-PARAM $regex pattern rejected (ReDoS risk or too long)
NWP-QUERY-VECTOR-UNSUPPORTED NPS-SERVER-UNSUPPORTED Node does not support vector search
NWP-QUERY-AGGREGATE-UNSUPPORTED NPS-SERVER-UNSUPPORTED Node does not support aggregation queries
NWP-QUERY-AGGREGATE-INVALID NPS-CLIENT-BAD-PARAM aggregate structure is invalid (unknown func, duplicate alias, etc.)
NWP-QUERY-STREAM-UNSUPPORTED NPS-SERVER-UNSUPPORTED Node does not support streaming queries
NWP-ACTION-NOT-FOUND NPS-CLIENT-NOT-FOUND action_id does not exist
NWP-ACTION-PARAMS-INVALID NPS-CLIENT-UNPROCESSABLE Operation parameter schema validation failed
NWP-ACTION-IDEMPOTENCY-CONFLICT NPS-CLIENT-CONFLICT A request with the same idempotency_key is already in progress
NWP-TASK-NOT-FOUND NPS-CLIENT-NOT-FOUND task_id does not exist
NWP-TASK-ALREADY-CANCELLED NPS-CLIENT-CONFLICT Task has already been cancelled
NWP-TASK-ALREADY-COMPLETED NPS-CLIENT-CONFLICT Task has already completed; cannot cancel
NWP-TASK-ALREADY-FAILED NPS-CLIENT-CONFLICT Task has already failed; cannot cancel
NWP-SUBSCRIBE-STREAM-NOT-FOUND NPS-CLIENT-NOT-FOUND subscription_id referenced by a subscription operation does not exist or is already closed
NWP-SUBSCRIBE-LIMIT-EXCEEDED NPS-LIMIT-EXCEEDED Exceeded maximum concurrent subscriptions
NWP-SUBSCRIBE-FILTER-UNSUPPORTED NPS-SERVER-UNSUPPORTED Node does not support filtered subscriptions
NWP-SUBSCRIBE-INTERRUPTED NPS-SERVER-UNAVAILABLE Subscription stream terminated due to underlying data source interruption
NWP-SUBSCRIBE-SEQ-TOO-OLD NPS-CLIENT-CONFLICT cursor is outside the node’s retention window; full re-query or reserved-type resync required
NWP-BUDGET-EXCEEDED NPS-LIMIT-BUDGET Response would exceed the token budget
NWP-DEPTH-EXCEEDED NPS-CLIENT-BAD-PARAM depth exceeds the node’s allowed max_depth
NWP-GRAPH-CYCLE NPS-CLIENT-UNPROCESSABLE Node graph contains a circular reference
NWP-NODE-UNAVAILABLE NPS-SERVER-UNAVAILABLE Underlying data source temporarily unavailable
NWP-MANIFEST-VERSION-UNSUPPORTED NPS-CLIENT-BAD-PARAM Agent NPS version is below the node’s min_agent_version
NWP-MANIFEST-NODE-TYPE-REMOVED NPS-CLIENT-BAD-FRAME NWM node_type contains the removed legacy value "gateway" (NPS-CR-0001); response SHOULD include a hint pointing to NPS-CR-0001
NWP-MANIFEST-NODE-TYPE-UNKNOWN NPS-CLIENT-BAD-FRAME NWM node_type contains an unrecognized value (see NWP-MANIFEST-NODE-TYPE-REMOVED for the "gateway" case)
NWP-RATE-LIMIT-EXCEEDED NPS-LIMIT-RATE Rate limit exceeded
NWP-RESERVED-TYPE-UNSUPPORTED NPS-SERVER-UNSUPPORTED QueryFrame or SubscribeFrame type is an unrecognized reserved-type identifier (§12). Distinct from NWP-ACTION-NOT-FOUND — the unknown operand is type, not action_id.
NWP-TOPOLOGY-UNAUTHORIZED NPS-AUTH-FORBIDDEN Caller lacks permission to read this Anchor’s topology (§12). Authorization policy is implementation-defined per §12.4
NWP-TOPOLOGY-UNSUPPORTED-SCOPE NPS-CLIENT-BAD-PARAM topology.scope value is not implemented by this Anchor
NWP-TOPOLOGY-DEPTH-UNSUPPORTED NPS-CLIENT-BAD-PARAM Requested topology.depth exceeds this Anchor’s maximum
NWP-TOPOLOGY-FILTER-UNSUPPORTED NPS-CLIENT-BAD-PARAM topology.filter contains an unrecognized key

15. Security Considerations

15.1 Scope Enforcement

Nodes MUST validate the Agent NID’s scope on every request. Requests exceeding scope MUST return NWP-AUTH-NID-SCOPE-VIOLATION and MUST NOT return any data.

15.2 SSRF Protection

When Complex Nodes resolve sub-node references, they MUST maintain an allowlist of permitted node URL prefixes and MUST NOT access internal network addresses (RFC 1918).

15.3 Token Budget Enforcement

When the budget is exceeded, nodes SHOULD prefer trimming response content (field reduction → summary → record truncation); if trimming is not possible, they MUST return NWP-BUDGET-EXCEEDED and MUST NOT silently truncate data. See token-budget.md §4.3.

15.4 Rate Limiting

Nodes SHOULD enforce per-Agent NID rate limiting. On limit exceeded, return NWP-RATE-LIMIT-EXCEEDED with an X-NWP-Rate-Reset header. Unauthenticated requests SHOULD be limited by IP.

15.5 Filter Injection Protection

15.6 callback_url Abuse Prevention

15.7 Topology Read-back

Anchor Nodes implementing §12 MUST treat topology.snapshot and topology.stream as authenticated surfaces. The minimum authorization binding is defined in §12.4: at Phase 1–2, the requesting NID MUST declare topology:read in IdentFrame.capabilities (primary gate); NDP node_roles cross-check is a defense-in-depth SHOULD. Unauthorized callers MUST receive NWP-TOPOLOGY-UNAUTHORIZED rather than silent empty responses to prevent oracle attacks against cluster membership.


16. Changelog

Version Date Changes
0.13 2026-05-28 §13 SubscribeFrame (0x12) formal specification (closes CR-0006): field table (subscription_id, filter, heartbeat_interval_ms, max_events, cursor), lifecycle (open→active→heartbeat→close), error code reference. §12.4 topology:subscribe enforcement promoted SHOULD → MUST; nodes that cannot enforce MUST document non-enforcement in NWM stability metadata. NWM gains optional trust_anchors field (array of CA NID URNs). BridgeNode bridge_target object schema standardized (protocol + endpoint + headers).
0.12 2026-05-11 NPS-CR-0002 Phase 2 spec gaps closed. §8.2 DiffFrame extension table gains optional cgn_est field (uint32) for per-event CGN reporting on push streams per token-budget.md §7.2; columns reformatted to include Required. §12.2 topology.stream events table: anchor_state row gains explicit sub-type discriminator schema (version_rebased defined for Phase 1–2; anchor_failover and anchor_quorum_lost reserved as Phase 3 placeholder slots — implementations MUST NOT emit Phase 3 sub-types pre-stable and MUST ignore unknown sub-types for forward compatibility); resync_required trigger and reason enum broadened (version_too_old / anchor_rebased / server_state_lost). §12.4 Phase 1–2 authorization model expanded: (a) capability gate split per surface — topology.snapshot requires topology:read; topology.stream requires topology:read AND SHOULD additionally require topology:subscribe in Phase 2 (MUST in Phase 3); (b) new mid-stream rejection rule — server MUST emit terminal NWP-TOPOLOGY-UNAUTHORIZED event then close the stream on capability revocation; (c) new reputation interaction — for active subscriptions, Anchors with a declared reputation_policy SHOULD emit terminal NWP-AUTH-REPUTATION-BLOCKED and close the stream when the subscriber’s reputation drops below threshold. No new error codes; existing NWP-TOPOLOGY-UNAUTHORIZED and NWP-AUTH-REPUTATION-BLOCKED reused. No Depends-On change. See issue #41.
0.11 2026-05-10 NWM gains optional top-level stability (experimental/stable/deprecated), sla (object: p95_latency_ms, availability, sla_tier), and billing (object: metering_profile, billing_unit, price_hint, currency) fields (§4.1, §4.4a, §4.4b). ActionSpec (§4.6) gains matching per-action stability / sla / billing overrides with field-level fallback to the top-level values. All fields are advisory (no protocol-level enforcement) and backward-compatible — pre-0.11 manifests are treated as stability="stable" with no SLO/billing metadata. Enables marketplace / NeuronHub clients to filter, warn, or rank services by lifecycle stage and commercial profile per AaaS-Profile discovery requirements. No new error codes; no Depends-On change. See issue #36.
0.10 2026-05-01 §12.4 authorization model replaced “implementation-defined” with a Phase 1–2 minimum binding: Anchor Nodes MUST require topology:read in IdentFrame.capabilities (capability gate, self-declared but signed); SHOULD cross-check NDP node_roles contains "anchor" as defense-in-depth; Phase 3 [RFC-0002 stable] adds CA-attested id-nps-node-roles cert extension. §14.7 updated to reference §12.4 defined minimum instead of the previous hedging “SHOULD restrict” language. Depends-On NIP bumped to v0.6 (defines topology:read capability).
0.9 2026-05-01 Breaking rename (pre-1.0): Topology member object field node_kind renamed to node_roles (§12.1); topology stream filter key node_kind renamed to node_roles (§12.2). §2.1 updated: node_kind reference to node_roles. New §2.1 Node Role Resolution section: node_roles (NDP, discovery-layer, array) and node_type (NWM, service-layer, string) are distinct fields — node_type MUST be one of the values in node_roles; validators SHOULD verify against cached NDP data. §4.1 node_type description updated with the cross-protocol constraint and pointer to §2.1. §14.7 node_kind reference updated to node_roles. Depends-On NDP bumped to v0.6. Fixes M1 naming-disambiguation issue.
0.8 2026-04-27 New §12 Reserved Query Types introducing the topology.* namespace mandatory at NPS-AaaS Profile L2: topology.snapshot (QueryFrame, type="topology.snapshot") and topology.stream (SubscribeFrame, type="topology.stream"). Both QueryFrame §6.1 and SubscribeFrame §8.1 gain an optional top-level type field for opting into reserved types. DiffFrame §8.2 event_type enum extended via reserved subscribe types — topology.stream adds member_joined / member_left / member_updated / anchor_state / resync_required. Five new error codes: NWP-TOPOLOGY-UNAUTHORIZED, NWP-TOPOLOGY-UNSUPPORTED-SCOPE, NWP-TOPOLOGY-DEPTH-UNSUPPORTED, NWP-TOPOLOGY-FILTER-UNSUPPORTED (table §13). New §14.7 Topology Read-back security section. Existing §12 Error Codes / §13 Security / §14 Changelog renumbered to §13 / §14 / §15 to accommodate the new section. See NPS-CR-0002.
0.7 2026-04-26 Breaking. §2.1 Node Types: Gateway Node removed; replaced by Anchor Node (cluster control plane + NOP routing — inherits the existing role) and Bridge Node (NPS↔non-NPS protocol translation — new). NWM node_type enum updated; legacy "gateway" MUST be rejected. Anchor Node detailed semantics (§2.1 inline) cover member dispatch + optional registry; Bridge Node semantics cover HTTP/gRPC/MCP/A2A target adapters. Depends-On upgraded to NDP v0.8 for the node_kind array form + cluster_anchor + bridge_protocols fields. See NPS-CR-0001.
0.6 2026-04-25 NWM gains optional top-level min_assurance_level field (anonymous / attested / verified), with min_assurance_level per-action override on individual ActionSpecs (§4.6). New error code NWP-AUTH-ASSURANCE-TOO-LOW (NPS-AUTH-FORBIDDEN). Depends-On upgraded to NCP v0.7 (NPS-RFC-0001) and NIP v0.9 (NPS-RFC-0003). See NPS-RFC-0003.
0.4 2026-04-14 §3.2 added /actions sub-path; §4.1 NWM added actions field; §4.2 capabilities added stream_query and aggregate; §4.6 NWM Action Registry (ActionSpec, params_anchor/result_anchor/async/idempotent); QueryFrame §6.1 added stream, aggregate, request_id; §6.6 Streaming Query Protocol (StreamFrame sequence, estimated_total, early termination); §6.7 Aggregation queries (COUNT/SUM/AVG/MIN/MAX/COUNT_DISTINCT, group_by, having); ActionFrame §7.1 added request_id; SubscribeFrame §8.1 added resume_from_seq; §8.2 DiffFrame extension fields (monotonic seq, event_type, timestamp) and reconnection semantics; §9.1/9.2 added X-NWP-Request-ID; §9.4 HTTP mode error response format (application/nwp-error+json); §10 updated complete examples (including error response); §13.6 callback_url abuse prevention security section; 5 new error codes (AGGREGATE-UNSUPPORTED/-INVALID, STREAM-UNSUPPORTED, SUBSCRIBE-SEQ-TOO-OLD, task cancel series)
0.3 2026-04-14 SubscribeFrame (0x12); auto_anchor; Filter $not/$exists/$regex; ActionFrame callback_url/priority; system.task.*; NWM min_agent_version/rate_limits; §14.4/14.5 security sections
0.2 2026-04-12 Unified port 17433; AnchorFrame owned by Node; CGN metering; NPS status code mapping
0.1 2026-04-10 Initial specification

Attribution: LabAcacia / INNO LOTUS PTY LTD · Apache 2.0