# Capability Spec Delta: network-plugin-intra-relay (complete-network-plugins) **Package**: `@suaveplan/network-plugin-intra-relay` **Status**: PROPOSED > Every requirement below carries a `**Verified by:**` line at authoring time. ## ADDED Requirements ### Requirement: IntraRelayPlugin conforms to NetworkPlugin lifecycle The package SHALL export `IntraRelayPlugin implements NetworkPlugin`. The plugin SHALL manage relay-path discovery, connection lifecycle, envelope wrap/unwrap, and security on install. #### Scenario: install / uninstall lifecycle - **WHEN** a host installs then uninstalls the plugin - **THEN** the plugin reaches `INSTALLED` then `UNINSTALLED` cleanly **Verified by:** `src/plugin/intra-relay-plugin.spec-conformance.test.ts::"install / uninstall lifecycle is clean"` --- ### Requirement: RelayEnvelope wraps and unwraps inner messages with hop tracking The package SHALL export `wrapEnvelope`, `unwrapEnvelope`, `advanceEnvelope`, `nextHopFor`, `WrapInput`, `InnerMessage`, and `DEFAULT_MAX_HOPS`. Each hop SHALL increment a counter; the envelope SHALL throw `NetworkIntraRelayError(MAX_HOPS_EXCEEDED)` when the counter would exceed the configured limit. #### Scenario: hop counter increments per advance - **WHEN** `advanceEnvelope` is called twice on a fresh envelope - **THEN** the envelope's hop count is 2 **Verified by:** `src/envelope/relay-envelope.spec-conformance.test.ts::"advance increments hop counter"` --- ### Requirement: RelayPerformanceMonitor validates hysteresis config with typed error The package SHALL export `RelayPerformanceMonitor`. The class SHALL throw `NetworkIntraRelayError(HYSTERESIS_CONFIG_INVALID)` whenever hysteresis configuration is missing, negative, or non-finite. The class SHALL NOT throw bare `Error`. #### Scenario: invalid hysteresis config raises typed error - **WHEN** `new RelayPerformanceMonitor({ hysteresisMs: -1 })` is called - **THEN** the call throws `NetworkIntraRelayError` with `code === "NET_INTRA_RELAY.HYSTERESIS_CONFIG_INVALID"` **Verified by:** `src/monitor/relay-performance-monitor.spec-conformance.test.ts::"throws HYSTERESIS_CONFIG_INVALID on negative hysteresis"` --- ### Requirement: RelaySecurityManager validates rate-limit config with typed error `RelaySecurityManager` SHALL throw `NetworkIntraRelayError(RATE_LIMIT_CONFIG_INVALID)` whenever `refillRatePerSec` or `burstSize` is zero, negative, or non-finite. The class SHALL NOT throw bare `RangeError`. #### Scenario: zero burstSize raises typed error - **WHEN** `new RelaySecurityManager({ rateLimit: { burstSize: 0, … } })` is called - **THEN** the call throws `NetworkIntraRelayError` with `code === "NET_INTRA_RELAY.RATE_LIMIT_CONFIG_INVALID"` and `context.field === "burstSize"` #### Scenario: negative refillRatePerSec raises typed error - **WHEN** `new RelaySecurityManager({ rateLimit: { refillRatePerSec: -1, … } })` is called - **THEN** the call throws `NetworkIntraRelayError` with `code === "NET_INTRA_RELAY.RATE_LIMIT_CONFIG_INVALID"` and `context.field === "refillRatePerSec"` **Verified by:** `src/security/relay-security-manager.spec-conformance.test.ts::"throws RATE_LIMIT_CONFIG_INVALID on zero burstSize"` AND `src/security/relay-security-manager.spec-conformance.test.ts::"throws RATE_LIMIT_CONFIG_INVALID on negative refillRatePerSec"` --- ### Requirement: RelayDiscovery surfaces candidates and tracks peer snapshots The package SHALL export `RelayDiscovery`, `RelayDiscoveryConfig`, and `PeerSnapshot`. The discovery SHALL maintain a per-peer snapshot and SHALL throw `NetworkIntraRelayError(NO_RELAY_CANDIDATE)` when asked for a candidate and none qualify. #### Scenario: no candidate available - **WHEN** `discovery.pickCandidate()` is called with no qualifying peers - **THEN** the call throws `NetworkIntraRelayError(NO_RELAY_CANDIDATE)` **Verified by:** `src/discovery/relay-discovery.spec-conformance.test.ts::"throws NO_RELAY_CANDIDATE when no qualifying peers"` --- ### Requirement: RelayConnectionManager enforces a finite state machine The package SHALL export `RelayConnectionManager`, `RelayConnectionManagerConfig`, and the `RELAY_TRANSITIONS` table. Illegal transitions SHALL throw `NetworkIntraRelayError(INVALID_TRANSITION)`. #### Scenario: illegal transition raises typed error - **WHEN** the manager is asked to transition from `CLOSED` directly to `STREAMING` - **THEN** the call throws `NetworkIntraRelayError` with `code === "NET_INTRA_RELAY.INVALID_TRANSITION"` **Verified by:** `src/connection/relay-connection-manager.spec-conformance.test.ts::"throws INVALID_TRANSITION on illegal state move"` --- ### Requirement: Error-code registry is frozen and namespaced NET_INTRA_RELAY `NETWORK_INTRA_RELAY_CODES` SHALL be frozen under the `NET_INTRA_RELAY` namespace and SHALL include at minimum: `NO_RELAY_CANDIDATE`, `RELAY_LOOP_DETECTED`, `MAX_HOPS_EXCEEDED`, `INVALID_TRANSITION`, `PATH_NOT_FOUND`, `PATH_ALREADY_EXISTS`, `STRATEGY_UNKNOWN`, `ENVELOPE_INVALID`, `RELAY_SIGNATURE_INVALID`, `RELAY_PEER_UNTRUSTED`, `RELAY_RATE_LIMIT_EXCEEDED`, and the new `HYSTERESIS_CONFIG_INVALID`, `RATE_LIMIT_CONFIG_INVALID`. #### Scenario: registry contains the two new codes - **WHEN** `NETWORK_INTRA_RELAY_CODES` is inspected - **THEN** `HYSTERESIS_CONFIG_INVALID` and `RATE_LIMIT_CONFIG_INVALID` map to their `"NET_INTRA_RELAY."` values **Verified by:** `src/error-codes.spec-conformance.test.ts::"NETWORK_INTRA_RELAY_CODES includes HYSTERESIS_CONFIG_INVALID and RATE_LIMIT_CONFIG_INVALID"` --- ### Requirement: No bare-Error throws in production code `src/` SHALL contain zero bare `throw new (Error|TypeError|RangeError|SyntaxError)\(` sites outside test files. #### Scenario: source grep returns zero hits - **WHEN** the grep is run - **THEN** the output is empty **Verified by:** `tests/no-bare-throws.test.ts::"network-plugin-intra-relay src/ has no bare Error throws"` --- ### Requirement: README is auto-docs idempotent The package `README.md` SHALL contain exactly one balanced auto-docs block; the generator SHALL produce a byte-identical file on a second run. #### Scenario: marker count is exactly one - **WHEN** `grep -c "begin:auto-docs" packages/network/network-plugin-intra-relay/README.md` - **THEN** output is `1` **Verified by:** `scripts/generate-package-module-tree.test.ts::"README regen is idempotent for network-plugin-intra-relay"`