# OpenSpec Change: enterprise-network-protocol - **Change ID**: enterprise-network-protocol - **Package**: `@suaveplan/network-protocol` - **Category**: universal/network - **Target path**: `universal/network/network-protocol` - **Status**: SUPERSEDED-BY-GENESIS-WAVE - **Date**: 2026-04-24 --- ## Summary Deliver `@suaveplan/network-protocol` on the genesis branch from scratch at enterprise quality. The package provides custom application-level protocol definitions: message framing (length-prefix, delimiter, fixed-length), codec registry (encode/decode for message types), protocol versioning and negotiation, session handshake lifecycle, and flow-control primitives. Genesis adds structured errors via `@suaveplan/error`, Zod schemas for all protocol types, OTel instrumentation, `AbortSignal` on handshake and negotiation, injectable `Clock` for session timing, and property-based tests for codec round-trip invariants. --- ## Current-state audit (genesis) The package does not exist on genesis yet — this is a fresh build. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/error` (`workspace:^`) | Base class for `NetworkProtocolError`; stable error codes | | `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock`, `EventBusLike` | | `@suaveplan/events` (`workspace:^`) | Event bus for session lifecycle events | | `@suaveplan/logger` (`workspace:^`) | Structured logging | | `@suaveplan/serialize` (`workspace:^`) | MessagePack binary serialization | | `@suaveplan/crypto-utils` (`workspace:^`) | Byte utilities (concat, i2osp, os2ip) for framing | | `zod` (`catalog:forms`) | Zod schemas | | `@opentelemetry/api` (`catalog:observability`) | Spans + counters on encode/decode | | `tslib` (`catalog:build`) | Runtime helpers | Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`). --- ## Public API surface ### Core ```ts function defineProtocol(definition: ProtocolDefinition): Protocol interface Protocol { readonly name: string readonly version: number createSession(options?: SessionOptions): ProtocolSession negotiate(offered: ProtocolOffer, signal?: AbortSignal): Promise } interface ProtocolSession { readonly id: string readonly state: SessionState encode(message: ProtocolMessage): Uint8Array decode(data: Uint8Array): ProtocolMessage handshake(signal?: AbortSignal): Promise close(): void on(event: SessionEvent, handler: (data: unknown) => void): () => void } ``` ### Protocol definition ```ts interface ProtocolDefinition { name: string version: number framing: FramingStrategy codecs: ReadonlyArray> handshake?: HandshakeDefinition maxMessageSize?: number } type FramingStrategy = | { type: 'length-prefix'; lengthBytes: 2 | 4; bigEndian?: boolean } | { type: 'delimiter'; delimiter: Uint8Array } | { type: 'fixed'; size: number } interface MessageCodec { readonly type: string readonly version?: number encode(message: T): Uint8Array decode(data: Uint8Array): T schema?: { parse: (data: unknown) => T } } interface HandshakeDefinition { timeout?: number clientHello(session: ProtocolSession): Uint8Array serverHello(clientData: Uint8Array): Uint8Array verify?(data: Uint8Array): boolean } ``` ### Negotiation ```ts interface ProtocolOffer { name: string versions: number[] capabilities?: string[] } interface ProtocolNegotiationResult { readonly agreed: boolean readonly version: number readonly capabilities: string[] readonly reason?: string } ``` ### Types ```ts interface ProtocolMessage { type: string version?: number payload: unknown id?: string sessionId?: string } type SessionState = 'idle' | 'handshaking' | 'established' | 'closing' | 'closed' type SessionEvent = 'established' | 'message' | 'close' | 'error' interface SessionOptions { logger?: LoggerLike clock?: Clock eventBus?: EventBusLike } ``` ### Error codes ```ts type NetworkProtocolErrorCode = | 'PROTO_FRAMING_ERROR' | 'PROTO_CODEC_NOT_FOUND' | 'PROTO_DECODE_FAILED' | 'PROTO_ENCODE_FAILED' | 'PROTO_HANDSHAKE_FAILED' | 'PROTO_VERSION_MISMATCH' | 'PROTO_MESSAGE_TOO_LARGE' | 'PROTO_ABORT' function registerNetworkProtocolErrorCodes(): void ``` --- ## Genesis additions 1. **Structured `NetworkProtocolError`** with stable `PROTO.*` codes 2. **Zod schemas** for `ProtocolDefinition`, `ProtocolOffer`, `ProtocolMessage` 3. **Three framing strategies** with length-prefix and delimiter implementations using `@suaveplan/crypto-utils` byte helpers 4. **Codec registry** — look up codec by `message.type`; throw `PROTO_CODEC_NOT_FOUND` if missing 5. **Protocol negotiation** — version intersection + capability set intersection 6. **Message size guard** — throw `PROTO_MESSAGE_TOO_LARGE` before encoding 7. **OTel instrumentation** — counter `proto.messages_encoded_total`, `proto.messages_decoded_total`, histogram `proto.message_size_bytes` 8. **Property-based tests** — encode/decode round-trip for arbitrary message payloads; framing idempotency --- ## File layout ```text universal/network/network-protocol/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts constants/ index.ts types/ common.ts common.md core/ protocol/ protocol.ts protocol.md protocol.types.ts protocol.test.ts index.ts session/ session.ts session.md session.test.ts index.ts framing/ length-prefix.ts length-prefix.md length-prefix.test.ts delimiter.ts delimiter.md delimiter.test.ts fixed.ts fixed.md fixed.test.ts codecs/ codec-registry.ts codec-registry.md codec-registry.test.ts index.ts tests/ integration.test.ts property.test.ts ``` --- ## Impact **Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/logger`, `@suaveplan/serialize`, `@suaveplan/crypto-utils` must be published.