Files
suaveplan/openspec/changes/complete-network-plugins/specs/network-plugin-intra-relay/spec.md
2026-05-27 23:29:53 +01:00

6.3 KiB

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.<KEY>" 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"