NPS-Release

English 中文版

RFC Number: NPS-RFC-0001 Title: Add NCP connection preamble for native-mode traffic identification Status: Accepted (Phase 1 — spec + .NET reference implementation landed) Author(s): Ori Lynn iamzerolin@gmail.com (LabAcacia) Shepherd: Ori Lynn (pre-1.0 fast-track per spec/cr/README.md) Created: 2026-04-21 Last-Updated: 2026-04-25 Accepted: 2026-04-25 (pre-1.0 fast-track; see spec/cr/README.md) Activated: (set when first reference SDK ships, target v1.0-alpha.3) Supersedes: none Superseded-By: none Affected Specs: NPS-1 NCP, spec/error-codes.md, spec/status-codes.md Affected SDKs: .NET, Python, TypeScript, Java, Rust, Go —

NPS-RFC-0001: Add NCP connection preamble for native-mode traffic identification

1. Summary

Define an 8-byte constant preamble b"NPS/1.0\n" that every NCP native mode client MUST send once, immediately after the transport (TCP / QUIC) handshake and before its first HelloFrame. Servers that see any other byte sequence in those 8 bytes close the connection without emitting an ErrorFrame. HTTP mode is not affected. Adds one error code (NCP-PREAMBLE-INVALID) and one status code (NPS-PROTO-PREAMBLE-INVALID).

2. Motivation

NCP native mode currently relies on HelloFrame (0x06) as the first-byte-after-handshake signal. This works for well-behaved peers but has two operational weaknesses:

  1. Misrouted traffic is hard to reject cheaply. If a non-NPS client (HTTP/1.x probe, Redis client, port scanner, misconfigured reverse proxy) hits the native-mode port, the server parses the first byte as a frame type, reads a bogus length field, then eventually fails on a downstream parse. A constant preamble lets the server reject in the first read(8) with zero frame-parser exposure.

  2. Major-version bumps have no wire-level gate. An NPS 2.0 client connecting to a 1.x server gets a CapsFrame negotiation failure mid-handshake instead of a clean “wrong protocol version” rejection before any frame parsing happens. Embedding the major version in the preamble gives us a cheap, explicit compatibility check at the byte level.

This also answers a review comment from 2026-04-20 arguing NCP “needs a magic code to handle TCP packet-boundary ambiguity”. The commenter conflated two concerns (length-prefix framing vs. traffic identification) — NCP’s length-prefixed header already handles framing (see §5.2), but connection-level traffic identification is a legitimate separate concern that this RFC addresses.

3. Non-Goals

This RFC does NOT:

4. Detailed Design

4.1 Wire Format / Frame Changes

Preamble byte sequence (sent once per connection, before any frame):

Offset  0   1   2   3   4   5   6   7
       ┌───┬───┬───┬───┬───┬───┬───┬───┐
       │ N │ P │ S │ / │ 1 │ . │ 0 │\n │
       └───┴───┴───┴───┴───┴───┴───┴───┘
       0x4E 50  53  2F  31  2E  30  0A

8 bytes, ASCII, ends with 0x0A (LF). Hex: 4E 50 53 2F 31 2E 30 0A.

Why ASCII + LF:

Handshake flow (native mode):

Client                                        Server
  │                                              │
  │──── TCP/QUIC handshake ───────────────────── │
  │                                              │
  │──── 8 bytes "NPS/1.0\n"  (preamble) ──────→ │
  │                                              │  validate
  │                                              │  (close on mismatch,
  │                                              │   no ErrorFrame)
  │──── HelloFrame (0x06) ────────────────────→ │
  │                                              │
  │ ←─── CapsFrame (0x04) ────────────────────── │
  │                                              │
  │──── application frames ←→ ───────────────── │

The preamble is not a frame — it has no header, no Flags, no length field. It is raw constant bytes.

Server validation rules:

  1. On accepting a native-mode connection, server reads exactly 8 bytes.
  2. If the 8 bytes match b"NPS/1.0\n", proceed to frame parsing.
  3. If they do not match:
    • The server MUST close the connection within 500 ms.
    • The server MUST NOT send an ErrorFrame (the peer is not known to speak NCP; an ErrorFrame write would leak framing details to scanners).
    • The server MAY log the first 32 bytes of received traffic for operational diagnosis.
  4. If fewer than 8 bytes arrive within 10 s, server treats it as a timeout and closes silently.

Client behavior:

Future major-version semantics:

4.2 Manifest / NWM Changes

None. The preamble is transport-level, below any NWM surface.

4.3 Error Codes

New error code in spec/error-codes.md:

Error Code NPS Status Description
NCP-PREAMBLE-INVALID NPS-PROTO-PREAMBLE-INVALID Client sent invalid or malformed preamble; connection closed without frame-level response

New status code in spec/status-codes.md:

NPS Status HTTP Mapping Description
NPS-PROTO-PREAMBLE-INVALID 400 Bad Request (not emitted; native-mode only) Native-mode preamble mismatch

Note: this status is never transmitted on the wire because the server closes silently. It exists so that SDK-internal telemetry (logs, metrics) can classify the close reason consistently.

Frame-type namespace reservation in spec/frame-registry.yaml:

Add a note that the byte value 0x4E (ASCII N), when seen as the first byte of a native-mode connection, is interpreted as the start of the preamble, not as a frame type. Frame type 0x4E MUST NOT be assigned to any NCP frame.

4.4 State Machines / Flows

Native-mode connection state (server side):

[LISTEN] ──accept──→ [PREAMBLE-WAIT] ──8 bytes match──→ [FRAMING]
                           │                                 │
                           │──mismatch──→ [CLOSING]          │
                           │──timeout(10s)──→ [CLOSING]     │
                                                             │
                                           [FRAMING] ──────→ [HANDSHAKE]
                                                      HelloFrame
                                                             │
                                           [HANDSHAKE] ────→ [ESTABLISHED]
                                                      CapsFrame sent

Timeouts:

4.5 Backward Compatibility

Breaking-change rationale: native mode is still Phase 2+ per spec/NPS-Roadmap.md; no GA shipments depend on native mode yet. Taking the break now is cheaper than retrofitting after 1.0 GA.


5. Alternatives Considered

5.1 Per-frame magic bytes

Prefix every frame with a 2-byte magic (e.g. 0x4E 0x50).

5.2 Do nothing (rely on HelloFrame as implicit preamble)

Keep the current design: first frame is HelloFrame (0x06); server parses it; non-NPS traffic fails on frame parse.

5.3 Binary magic (4 bytes, non-ASCII)

E.g., 0x89 4E 50 53 (PNG-style high-bit + “NPS”).

5.4 TLS ALPN token only

Rely on TLS ALPN (nps/1) for protocol identification; no in-band preamble.


6. Drawbacks & Risks


7. Security Considerations


8. Implementation Plan

8.1 Phasing

Phase Scope Exit criterion
1 Spec merged + .NET reference implementation behind NpsNativeOptions.RequirePreamble flag (default false) Unit tests green; cross-version interop test (preamble-aware client ↔ preamble-aware server)
2 All 6 SDKs implement with flag default false Cross-SDK interop matrix green; at least one SDK’s native-mode sample runs end-to-end with preamble on both sides
3 Flag default flips to true across SDKs; release notes call out the break No open regressions for 1 release cycle; min_agent_version bumped with 21-day deprecation window
4 Remove the flag — preamble mandatory Flag removal PR lands in each SDK; docs updated

8.2 SDK Coverage Matrix

SDK Owner Status Notes
.NET Ori Lynn pending Primary reference; lands first
Python TBD pending  
TypeScript TBD pending Browser: native mode not applicable; Node.js only
Java TBD pending  
Rust TBD pending  
Go TBD pending  

8.3 Test Plan

New tests that land with this RFC:

  1. Preamble-valid round-trip: client sends NPS/1.0\n + HelloFrame; server accepts, returns CapsFrame.
  2. Preamble-invalid rejection: client sends arbitrary 8 bytes ("GET / HTT", all-zero, NPS/2.0\n); server closes within 500 ms with no frame response.
  3. Preamble-truncated: client sends 3 bytes and pauses; server closes after 10-second timeout.
  4. HTTP mode unaffected: HTTP-mode requests continue to succeed with no preamble expected.
  5. Version-future rejection: client sends NPS/2.0\n; server MAY write NPS-PREAMBLE-UNSUPPORTED-VERSION\n diagnostic before close (test asserts close happens, diagnostic is optional).

Existing test changes:

Cross-SDK interop:

8.4 Benchmarks

No new benchmark needed. Wire-size impact is 8 bytes per connection, amortized to zero for any connection that exchanges >1 frame. Latency impact is zero because client can pipeline preamble + HelloFrame in a single write.

If a future measurement shows the 10-second PREAMBLE-WAIT timeout blocks >0.1% of legitimate connections, reopen this RFC’s open question #2.


9. Empirical Data

None yet. An experimental branch will be attached before moving to Accepted. Target measurements:

Metric Baseline Proposed Delta Method
Per-connection wire overhead 0 bytes 8 bytes +8 B trivial
Handshake RTT (localhost loopback) TBD TBD target: no regression .NET BenchmarkDotNet on native-mode loopback
Server-side cost to reject non-NPS scan full frame-parse attempt 8-byte memcmp target: ≥10× cheaper scan-simulation harness

10. Open Questions


11. Future Work


12. References


Appendix A. Revision History

Date Author Change
2026-04-21 Ori Lynn Initial draft
2026-04-25 Ori Lynn Accepted via pre-1.0 fast-track. Spec changes landed: §2.6.1 in NPS-1-NCP, error code NCP-PREAMBLE-INVALID, status code NPS-PROTO-PREAMBLE-INVALID, 0x4E reservation in frame-registry.yaml. Phase 1 .NET reference helpers (NPS.Core.Ncp.NcpPreamble) landed alongside; Phase 2 (other 5 SDKs) and Phase 3 (default-on flip) deferred per RFC §8.1.