Files
suaveplan/openspec/archive/2026-05-12-enterprise-network-registry/proposal.md
2026-05-27 23:29:53 +01:00

204 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<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
```ts
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
```ts
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
```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.