CR ID: NPS-CR-0001 Target version: v1.0-alpha.3 Status: Implemented (v1.0.0-alpha.3, 2026-04-26) Type: Breaking change (spec-level role split) Author: Ori, LabAcacia Affected components: NWP spec, .NET SDK, conformance tests, public docs
The current NWP node taxonomy includes a single Gateway Node type whose definition has, on inspection, been carrying two distinct roles that should not share one name:
This CR splits Gateway Node into two distinct node types:
Anchor Node — replaces the cluster-control-plane semanticsBridge Node — replaces the protocol-translation semanticsThe original term Gateway Node is retired.
The conflation produces concrete harm even at alpha stage:
Gateway Node doing protocol translation is per-request scalable; one acting as cluster control plane is a singleton with HA concerns. Operators cannot reason about scale-out without the role being specified.docs/services.md) originally introduced a process named nps-gateway for Internet ingress duty. That process-level daemon has since been renamed to nps-ingress, keeping the deployment name aligned with the retired Gateway Node terminology.The cost of fixing this now (alpha.3, no production users) is near zero. The cost of fixing it after v1.0 freeze would be measured in deprecation cycles, downstream SDK breakage, and ecosystem confusion.
Add to spec/NPS-2-NWP.md, Node Types section:
Anchor Node
An Anchor Node is the control plane and external entrypoint of an NPS cluster. It MUST:
- Maintain a topology of member nodes within its cluster, including their NIDs, declared capabilities, and
activation_mode.- Accept inbound NWP
ActionandQueryframes addressed to the cluster (rather than to a specific member NID).- Dispatch frames to appropriate member nodes based on capability declaration and current load.
- Aggregate outbound responses from member nodes into single response streams toward the originating caller.
Member nodes register with their Anchor Node on cluster join via NDP
Announceframes carrying acluster_anchorfield 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; consensus protocol between Anchor Nodes is implementation-defined and outside this specification (deferred to NPS-AaaS Profile L3).
An Anchor Node MAY simultaneously carry other node-type roles (e.g. Memory Node) in deployments where role separation is unnecessary.
Add to spec/NPS-2-NWP.md, Node Types section:
Bridge Node
A Bridge Node translates between NPS frames and non-NPS protocols. It MUST:
- Accept inbound NWP frames carrying a
bridge_targetparameter identifying the external protocol and endpoint.- Produce outbound requests in the target protocol’s format.
- Translate target protocol responses back into NWP frames.
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:
- HTTP/HTTPS (REST and streaming)
- gRPC (unary and streaming)
- MCP (Model Context Protocol)
- A2A (Agent-to-Agent protocol)
Additional protocol adapters MAY be registered through future CRs.
In spec/NPS-2-NWP.md:
Gateway Node section in its entirety.Gateway Node (removed in v1.0-alpha.3) — Split into Anchor Node and Bridge Node. See NPS-CR-0001.
Migration note (NDP v0.8 / NWP v0.13): The
node_kindfield introduced by this CR was renamed tonode_roles(array-only form) as part of the M1 naming-disambiguation fix. Parsers MUST acceptnode_kindas an alias through alpha.5. References tonode_kindbelow are historical and describe the state at the time this CR was implemented (NDP v0.8 / NWP v0.13).
In NDP Announce frame, the node_kind field:
| Old wire value | Status | New wire value(s) |
|---|---|---|
"gateway" |
Removed | "anchor" and/or "bridge" (a node MAY declare multiple) |
The node_kind field is redefined to accept either a string (single role) or an array of strings (multiple roles). Implementations MUST support both forms when parsing.
New optional fields in Announce:
cluster_anchor (string, NID): For non-Anchor nodes joining a cluster, identifies the Anchor Node they register with. Absent for standalone nodes and for Anchor Nodes themselves.bridge_protocols (array of strings): For nodes declaring "bridge" in node_kind, lists supported external protocols. Standard values: "http", "grpc", "mcp", "a2a".| File | Change |
|---|---|
spec/NPS-2-NWP.md |
Add Anchor Node section, add Bridge Node section, remove Gateway Node section, update wire format tables, update examples |
spec/NPS-4-NDP.md |
Update Announce frame schema with node_kind array form, cluster_anchor, bridge_protocols |
spec/services/NPS-AaaS-Profile.md |
Update L1/L2/L3 conformance requirements to reference Anchor and Bridge separately |
spec/services/conformance/NPS-Node-L1.md |
Add separate test items for Anchor Node basic registry and Bridge Node basic translation (where applicable to L1 scope) |
spec/services/conformance/L2.md, L3.md |
Same alignment |
README.md |
Update node type list |
CHANGELOG.md |
Record breaking change under v1.0-alpha.3 |
Type renames:
| Old | New |
|---|---|
GatewayNode (class) |
Removed; replaced by AnchorNode and BridgeNode classes |
NodeKind.Gateway (enum) |
Removed; replaced by NodeKind.Anchor and NodeKind.Bridge |
[Flags] semantics on NodeKind |
Newly required — a node may declare multiple kinds |
New types:
[Flags]
public enum NodeKind
{
None = 0,
Memory = 1 << 0,
Action = 1 << 1,
Complex = 1 << 2,
Anchor = 1 << 3,
Bridge = 1 << 4,
}
public sealed record AnchorNodeDescriptor(
Nid Nid,
IReadOnlyList<Nid> ClusterMembers,
/* HA group, dispatch policy etc. — TBD per L3 spec */
);
public sealed record BridgeNodeDescriptor(
Nid Nid,
IReadOnlySet<string> SupportedProtocols /* "http", "grpc", "mcp", "a2a" */
);
Serialization:
NodeKind flag combinations serialize to JSON arrays in NDP Announce:
NodeKind.Anchor → "node_kind": "anchor" (single string preserved when only one flag set)NodeKind.Anchor | NodeKind.Memory → "node_kind": ["anchor", "memory"]Deserializer MUST accept both string and array forms.
Deprecation aids:
For one alpha release window (alpha.3 only), the SDK SHOULD include:
[Obsolete("Gateway Node has been split. Use AnchorNode for cluster control plane, BridgeNode for protocol translation. See NPS-CR-0001.")] on a stub GatewayNode type that throws on instantiation."node_kind": "gateway" (or "node_roles": ["gateway"] post-M1 rename), throws with a clear message referencing this CR.This stub is removed in alpha.4.
Error codes (M2 fix):
The wire-level rejection MUST use the specific error codes registered in spec/error-codes.md v1.1:
node_roles: ["gateway"] in NDP AnnounceFrame → NDP-ANNOUNCE-ROLE-REMOVEDnode_type: "gateway" in NWP NWM → NWP-MANIFEST-NODE-TYPE-REMOVEDnode_roles → NDP-ANNOUNCE-ROLE-UNKNOWNnode_type → NWP-MANIFEST-NODE-TYPE-UNKNOWNBoth -REMOVED responses SHOULD include a hint field (string) containing a reference to NPS-CR-0001 and the migration guidance ("gateway" → "anchor" or "bridge").
conformance/L1-test/:
gateway wire value support.AnchorNode_BasicRegistry_Test — verifies an Anchor Node accepts Announce from member nodes and responds to topology queries.BridgeNode_HttpTranslation_Test — verifies a Bridge Node correctly translates a simple NWP Action to an HTTP GET and back.node_kind field must accept both string and array forms.L1-CERTIFIED.md template: split the Gateway Node checkbox into two:
Implementations MAY claim L1 with one but not both, and MUST declare which.
External impact: None known. v1.0-alpha.2 has no production deployments. No third-party SDKs or implementations exist as of CR submission.
Internal impact:
nps-daemon (in development under labacacia/nps-daemon): not yet implementing Gateway Node logic, so no migration burden. Updates align with new CR before first L1 release.nps-claude-bridge (planned): unaffected — does not depend on Gateway Node.Migration window: Single release. alpha.3 introduces the split with the deprecation stub described in §4. alpha.4 removes the stub.
To prevent scope creep, the following are explicitly NOT part of this CR:
nps-ingress (formerly nps-gateway) and remains a deployment-layer decision. Anchor Node and nps-ingress are orthogonal layers (logical role vs deployment form) and may co-locate in a single process or be split across processes; that is a deployment concern, not a spec concern.Announce field additions in §3.4.This CR is considered accepted and ready to merge when:
dotnet test passesCHANGELOG.md entry written## [v1.0-alpha.3] - YYYY-MM-DD
### Breaking changes
- **NWP**: Removed `Gateway Node` type. Split into `Anchor Node` (cluster
control plane + external entrypoint) and `Bridge Node` (NPS↔non-NPS
protocol translation). The two roles previously conflated under
Gateway Node have distinct semantics, state requirements, and
conformance criteria, and are now independently declarable. See
NPS-CR-0001 for full rationale and migration notes.
- **NDP**: `Announce` frame `node_kind` field now accepts both string
(single role) and array (multiple roles) forms. New optional fields
`cluster_anchor` and `bridge_protocols` introduced. Wire value
`"gateway"` removed; `"anchor"` and `"bridge"` introduced.
- **.NET SDK**: `NodeKind` enum updated with `[Flags]` semantics.
`GatewayNode` type removed (deprecation stub present in alpha.3, to
be removed in alpha.4). New `AnchorNodeDescriptor` and
`BridgeNodeDescriptor` types.
### Migration
No production deployments affected. Implementations consuming alpha.2
must update wire field values and SDK type references before
upgrading to alpha.3. The deprecation stub in alpha.3 will throw
informative errors on legacy usage to aid migration.
All three OQs resolved at implementation time (v1.0-alpha.3):
node_kind array vs. single-string — Resolved: array form is canonical; single-string accepted as a parse-time alias. In NDP v0.6 (alpha.6) the field was further renamed from node_kind to node_roles, with node_kind retained as a backward-compatibility alias through alpha.5.
cluster_anchor mandatory vs. optional — Resolved: optional with implicit standalone default. Single-node deployments omit the field.
bridge_protocols open-ended vs. enum — Resolved: open-ended with reserved standard values ("http", "grpc", "mcp", "a2a"). Third-party adapters may register additional values via future CRs.
Shipped in v1.0.0-alpha.3 (2026-04-26). Corrections to the framing in §1/§2:
Gateway Node was historically the cluster entry point + NOP routing role — what §1.1 calls “cluster control plane and external entrypoint”. Accordingly, the existing implementation was renamed Gateway → Anchor.Bridge Node is a genuinely new role (NPS ↔ non-NPS protocol translation). Phase 1 shipped only the spec definition and a skeleton NPS.NWP.Bridge package; concrete adapters per protocol were deferred to follow-up CRs (HTTP adapter shipped in alpha.7 via NPS.NWP.Bridge.HttpBridgeDispatcher; gRPC / MCP / A2A deferred).compat/{mcp,a2a,grpc}-bridge packages carry the inverse direction (external → NPS) and were renamed compat/{mcp,a2a,grpc}-ingress to free the “Bridge” label; sibling GitHub/Gitee repos renamed accordingly.