Files
2026-05-27 23:29:53 +01:00

5.6 KiB
Raw Permalink Blame History

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

function createNetworkRegistry(options?: NetworkRegistryOptions): NetworkRegistry

interface NetworkRegistry {
  register(entry: RegistryEntry): RegistrationHandle
  deregister(entryId: string): boolean
  lookup(query: RegistryQuery): ReadonlyArray<RegistryEntry>
  get(entryId: string): RegistryEntry | null
  list(filter?: RegistryFilter): ReadonlyArray<RegistryEntry>
  updateHealth(entryId: string, status: HealthStatus): boolean
  subscribe(query: RegistryQuery, handler: RegistryChangeHandler): () => void
  purgeExpired(): number
}

Entry types

interface RegistryEntry {
  id: string
  name: string
  type: 'peer' | 'service' | 'relay' | 'bootstrap'
  address?: string
  port?: number
  region?: string
  capabilities: string[]
  tags?: Record<string, string>
  metadata?: Record<string, unknown>
  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

interface RegistryQuery {
  type?: RegistryEntry['type'] | Array<RegistryEntry['type']>
  capabilities?: string[]          // entry must have ALL listed capabilities
  anyCapabilities?: string[]       // entry must have AT LEAST ONE
  tags?: Record<string, string>    // all tag key/value pairs must match
  region?: string
  healthyOnly?: boolean
  limit?: number
}

interface RegistryFilter {
  type?: RegistryEntry['type'] | Array<RegistryEntry['type']>
  healthyOnly?: boolean
  region?: string
}

type RegistryChangeHandler = (change: RegistryChange) => void

interface RegistryChange {
  type: 'registered' | 'deregistered' | 'health-changed' | 'expired'
  entry: RegistryEntry
  previous?: RegistryEntry
}

Options

interface NetworkRegistryOptions {
  ttlDefaultMs?: number
  maxEntries?: number
  gcIntervalMs?: number
  logger?: LoggerLike
  clock?: Clock
  eventBus?: EventBusLike
}

Error codes

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

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.