# OpenSpec Change: enterprise-browser-network-discovery-crdt - **Change ID**: enterprise-browser-network-discovery-crdt - **Package**: `@suaveplan/network-discovery-crdt` - **Category**: browser - **Target path**: `browser/network/network-discovery-crdt` - **Status**: SUPERSEDED-BY-GENESIS-WAVE - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/network-discovery-crdt.md` --- ## Summary Deliver `@suaveplan/network-discovery-crdt` on the genesis branch at enterprise quality. The package implements `DiscoveryStrategy` from `@suaveplan/network` using CRDT awareness layers (Yjs and Automerge) for eventually-consistent peer metadata exchange. Ships `CRDTDiscovery` backed by a clean `CRDTProviderAdapter` interface with `YjsProvider` and `AutomergeProvider` implementations. No mock global stubs — tests run against in-process signalling harnesses. --- ## Current-state audit (genesis) Fresh build — does not exist on genesis yet. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/network` (`workspace:^`) | `DiscoveryStrategy` interface, `PeerInfo` type | | `@suaveplan/error` (`workspace:^`) | Typed CRDT errors | | `@suaveplan/types` (`workspace:^`) | `LoggerLike` | | `@suaveplan/crypto` (`workspace:^`) | Optional AES-GCM awareness encryption | | `@suaveplan/schema-registry` (`workspace:^`) | Register awareness payload schema | | `zod` (`catalog:forms`) | Awareness payload Zod schema | | `@opentelemetry/api` (`catalog:observability`) | Peer/conflict/latency metrics | | `tslib` (`catalog:build`) | Runtime helpers | External: `yjs`, `y-webrtc`, `@automerge/automerge-repo` + network/storage adapters (Bun catalog). Dev deps: `vitest`, `@vitest/coverage-istanbul`, `@suaveplan/testing workspace:^`. React in `devDependencies` for stories. --- ## Preserved public API surface (from main branch) ```ts class CRDTDiscovery implements DiscoveryStrategy { constructor(config: CRDTDiscoveryConfig) start(): Promise stop(): Promise getPeers(): PeerInfo[] on(event: 'peer:discovered' | 'peer:left' | 'error', handler: Function): void } interface CRDTDiscoveryConfig { provider: 'yjs' | 'automerge' roomId: string yjs?: YjsSignalingConfig // Required when provider === 'yjs' automerge?: AutomergeConfig // Required when provider === 'automerge' encrypt?: boolean // Optional AES-GCM via @suaveplan/crypto strict?: boolean // Default true; refuses public signalling defaults } interface YjsSignalingConfig { signalingUrls: string[]; password?: string } interface AutomergeConfig { serverUrl: string; indexedDB?: boolean } // + CRDTConnectionStatus, CRDTMessage, CRDTMessageType, CRDTPeerMetadata, CRDTProvider, CRDTSyncStatus ``` --- ## New additions for genesis ### 1. `CRDTProviderAdapter` interface ```ts interface CRDTProviderAdapter { init(config: CRDTDiscoveryConfig): Promise destroy(): Promise getAwareness(): Record setLocalAwareness(metadata: CRDTPeerMetadata): void onAwarenessChange(handler: (changes: AwarenessChange[]) => void): void } ``` Both `YjsProvider` and `AutomergeProvider` implement this interface; `CRDTDiscovery` no longer branches on provider string. ### 2. Strict-mode signalling gate When `strict: true` (default), `YjsSignalingConfig.signalingUrls` must be non-empty; reject `wss://signaling.yjs.dev` without explicit opt-in. ### 3. Awareness payload Zod schema `CRDTPeerMetadataSchema` validating `{ peerId, addresses, protocols, capabilities, timestamp }`; reject malformed on receive. ### 4. Optional AES-GCM encryption When `config.encrypt: true`, wrap awareness payloads with `@suaveplan/crypto` AES-GCM before write; decrypt on read. ### 5. OTel metrics Counter `discovery.crdt.peers_seen_total{provider}`, counter `discovery.crdt.conflicts_resolved_total`, histogram `discovery.crdt.awareness_propagation_ms`. ### 6. Structured errors `CRDT_PROVIDER_UNAVAILABLE`, `CRDT_SIGNALING_FAILED`, `AWARENESS_REJECTED`. --- ## File layout ```text browser/network/network-discovery-crdt/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts crdt-discovery/ crdt-discovery.ts crdt-discovery.md crdt-discovery.types.ts crdt-discovery.test.ts index.ts provider/ provider.ts (CRDTProviderAdapter interface) yjs/ yjs-provider.ts yjs-provider.md yjs-provider.test.ts yjs-awareness.ts yjs-sync.ts index.ts automerge/ automerge-provider.ts automerge-provider.md automerge-provider.test.ts index.ts managers/ crdt-peer-manager/ crdt-peer-manager.ts crdt-peer-manager.md crdt-peer-manager.test.ts index.ts crdt-state-manager/ crdt-state-manager.ts crdt-state-manager.md crdt-state-manager.test.ts index.ts crdt-initializer/ crdt-initializer.ts crdt-initializer.md crdt-initializer.test.ts index.ts crdt-intervals/ crdt-intervals.ts crdt-intervals.md crdt-intervals.test.ts index.ts schema/ awareness/ awareness.ts (CRDTPeerMetadataSchema) awareness.md index.ts tests/ integration.test.ts ``` React dev dep declared for `crdt-discovery.stories.tsx` — story file lives in `src/` alongside the implementation. --- ## Impact **Downstream**: `@suaveplan/network-discovery-hybrid` (optional peer dep). **Prerequisites**: `@suaveplan/network`, `@suaveplan/error`, `@suaveplan/events` on Verdaccio.