5.6 KiB
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)
class CRDTDiscovery implements DiscoveryStrategy {
constructor(config: CRDTDiscoveryConfig)
start(): Promise<void>
stop(): Promise<void>
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
interface CRDTProviderAdapter {
init(config: CRDTDiscoveryConfig): Promise<void>
destroy(): Promise<void>
getAwareness(): Record<string, CRDTPeerMetadata>
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
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.