# OpenSpec Change: enterprise-network-registry - **Change ID**: enterprise-network-registry - **Package**: `@suaveplan/network-registry` - **Category**: universal/network - **Target path**: `universal/network/network-registry` - **Status**: SUPERSEDED-BY-GENESIS-WAVE - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/network-registry.md` --- ## Summary Deliver `@suaveplan/network-registry` on the genesis branch from scratch at enterprise quality. The package provides a network service and peer registry: register/deregister services with metadata and capabilities, health-status tracking, TTL-based expiry, lookup by capability/tag/region, and change-notification subscriptions. Genesis adds structured errors via `@suaveplan/error`, Zod schemas for all registry types, OTel instrumentation on register/lookup operations, `AbortSignal` on async lookup, injectable `Clock` for TTL expiry, event-bus emission for registration changes, and property-based tests for lookup correctness. --- ## 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 `NetworkRegistryError`; stable error codes | | `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock`, `EventBusLike` | | `@suaveplan/events` (`workspace:^`) | Event bus for registration changes | | `@suaveplan/logger` (`workspace:^`) | Structured logging | | `zod` (`catalog:forms`) | Zod schemas | | `@opentelemetry/api` (`catalog:observability`) | Spans + counters on register/lookup | | `tslib` (`catalog:build`) | Runtime helpers | Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`). --- ## Public API surface ### Core ```ts function createNetworkRegistry(options?: NetworkRegistryOptions): NetworkRegistry interface NetworkRegistry { register(entry: RegistryEntry): RegistrationHandle deregister(entryId: string): boolean lookup(query: RegistryQuery): ReadonlyArray get(entryId: string): RegistryEntry | null list(filter?: RegistryFilter): ReadonlyArray updateHealth(entryId: string, status: HealthStatus): boolean subscribe(query: RegistryQuery, handler: RegistryChangeHandler): () => void purgeExpired(): number } ``` ### Entry types ```ts interface RegistryEntry { id: string name: string type: 'peer' | 'service' | 'relay' | 'bootstrap' address?: string port?: number region?: string capabilities: string[] tags?: Record metadata?: Record ttlMs?: number registeredAt?: number expiresAt?: number health?: HealthStatus } interface HealthStatus { healthy: boolean lastCheckedAt: number latencyMs?: number load?: number // 0–1 } interface RegistrationHandle { readonly entryId: string renew(ttlMs?: number): void deregister(): void } ``` ### Query types ```ts interface RegistryQuery { type?: RegistryEntry['type'] | Array capabilities?: string[] // entry must have ALL listed capabilities anyCapabilities?: string[] // entry must have AT LEAST ONE tags?: Record // all tag key/value pairs must match region?: string healthyOnly?: boolean limit?: number } interface RegistryFilter { type?: RegistryEntry['type'] | Array healthyOnly?: boolean region?: string } type RegistryChangeHandler = (change: RegistryChange) => void interface RegistryChange { type: 'registered' | 'deregistered' | 'health-changed' | 'expired' entry: RegistryEntry previous?: RegistryEntry } ``` ### Options ```ts interface NetworkRegistryOptions { ttlDefaultMs?: number maxEntries?: number gcIntervalMs?: number logger?: LoggerLike clock?: Clock eventBus?: EventBusLike } ``` ### Error codes ```ts type NetworkRegistryErrorCode = | 'REGISTRY_ENTRY_NOT_FOUND' | 'REGISTRY_DUPLICATE_ID' | 'REGISTRY_CAPACITY_EXCEEDED' | 'REGISTRY_INVALID_ENTRY' | 'REGISTRY_EXPIRED' function registerNetworkRegistryErrorCodes(): void ``` --- ## Genesis additions 1. **Structured `NetworkRegistryError`** with stable `REGISTRY.*` codes 2. **Zod schemas** for `RegistryEntry`, `RegistryQuery`, `NetworkRegistryOptions` 3. **TTL-based expiry** with periodic GC via injectable `Clock`; `purgeExpired()` callable manually 4. **Subscription-based change notifications** with query-scoped filtering 5. **Capability AND/OR query operators** — `capabilities` (AND), `anyCapabilities` (OR) 6. **Health status tracking** with `updateHealth()` API 7. **OTel instrumentation** — counter `registry.register_total`, `registry.deregister_total`, `registry.lookup_total`, histogram `registry.lookup_results` 8. **Property-based tests** — lookup(query) results are always a subset of `list()`; TTL expiry correctness --- ## File layout ```text universal/network/network-registry/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts constants/ index.ts types/ common.ts common.md core/ registry/ registry.ts registry.md registry.types.ts registry.test.ts index.ts query-engine/ query-engine.ts query-engine.md query-engine.test.ts index.ts ttl-manager/ ttl-manager.ts ttl-manager.md ttl-manager.test.ts index.ts tests/ integration.test.ts property.test.ts ``` --- ## Impact **Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/logger` must be published.