Initial Commit
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
# 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<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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```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.
|
||||
@@ -0,0 +1,109 @@
|
||||
# Tasks: enterprise-browser-network-discovery-crdt
|
||||
|
||||
**Change ID**: enterprise-browser-network-discovery-crdt
|
||||
**Package**: `@suaveplan/network-discovery-crdt`
|
||||
**Target path**: `browser/network/network-discovery-crdt`
|
||||
**Prerequisite packages on Verdaccio**: `@suaveplan/network`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/crypto`
|
||||
|
||||
---
|
||||
|
||||
## 1. Scaffold
|
||||
|
||||
- [x] Create directory tree per layout in `proposal.md`.
|
||||
- [x] Write `package.json` at version `0.1.0` — `"sideEffects": false`, `strict: true` default.
|
||||
- `dependencies`: `@suaveplan/network workspace:^`, `@suaveplan/error workspace:^`, `@suaveplan/types workspace:^`, `@suaveplan/crypto workspace:^`, `yjs`, `y-webrtc`, all `@automerge/*` packages (Bun catalog), `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`
|
||||
- `devDependencies`: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `@suaveplan/testing workspace:^`, `react catalog:browser`, `@types/react catalog:browser`
|
||||
- [x] Write `tsconfig.json` and `vitest.config.ts` (pool: forks).
|
||||
|
||||
---
|
||||
|
||||
## 2. Error codes
|
||||
|
||||
- [x] Define `CRDTErrorCode`: `CRDT_PROVIDER_UNAVAILABLE`, `CRDT_SIGNALING_FAILED`, `AWARENESS_REJECTED`.
|
||||
- [x] Export `registerCRDTErrorCodes()` and call eagerly.
|
||||
|
||||
---
|
||||
|
||||
## 3. `CRDTProviderAdapter` interface (`src/provider/provider.ts`)
|
||||
|
||||
- [x] Define `CRDTProviderAdapter` interface with `init`, `destroy`, `getAwareness`, `setLocalAwareness`, `onAwarenessChange`.
|
||||
- [x] Export `AwarenessChange` type.
|
||||
|
||||
---
|
||||
|
||||
## 4. Yjs provider (`src/provider/yjs/`)
|
||||
|
||||
- [x] Implement `YjsProvider implements CRDTProviderAdapter`:
|
||||
- Strict-mode: reject if `signalingUrls` empty or contains known public URLs without `strict: false`.
|
||||
- `yjs-awareness.ts`: awareness state helpers.
|
||||
- `yjs-sync.ts`: Y.Doc sync utilities.
|
||||
- [x] Write `yjs-provider.test.ts` using in-process signalling harness (no public URLs), `yjs-provider.md`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Automerge provider (`src/provider/automerge/`)
|
||||
|
||||
- [x] Implement `AutomergeProvider implements CRDTProviderAdapter` using `@automerge/automerge-repo` with broadcast-channel + websocket adapters.
|
||||
- [x] Write `automerge-provider.test.ts`, `automerge-provider.md`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Awareness schema (`src/schema/awareness/`)
|
||||
|
||||
- [x] Define `CRDTPeerMetadataSchema` (Zod): `{ peerId: string, addresses: string[], protocols: string[], capabilities: string[], timestamp: number }`.
|
||||
- [x] Register in `@suaveplan/schema-registry`.
|
||||
- [x] Write `awareness.md`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Manager modules
|
||||
|
||||
For each manager (`crdt-peer-manager`, `crdt-state-manager`, `crdt-initializer`, `crdt-intervals`):
|
||||
|
||||
- [x] Implement the manager.
|
||||
- [x] Write `*.test.ts` and `*.md`.
|
||||
|
||||
---
|
||||
|
||||
## 8. CRDTDiscovery orchestrator (`src/crdt-discovery/`)
|
||||
|
||||
- [x] Implement `CRDTDiscovery` using `CRDTProviderAdapter` (no provider-string branching).
|
||||
- [x] Optional encryption: if `config.encrypt`, wrap awareness payloads with `@suaveplan/crypto` AES-GCM.
|
||||
- [x] Wire OTel metrics.
|
||||
- [x] Write `crdt-discovery.test.ts` (Yjs + Automerge paths via their respective providers; in-process harness), `crdt-discovery.md`, `crdt-discovery.types.ts`.
|
||||
|
||||
---
|
||||
|
||||
## 9. Storybook story
|
||||
|
||||
- [x] Write `src/crdt-discovery/crdt-discovery.stories.tsx` with `react` in `devDependencies` — no missing dep.
|
||||
- [x] Story demonstrates peer discovery with both providers.
|
||||
|
||||
---
|
||||
|
||||
## 10. Integration test (`tests/integration.test.ts`)
|
||||
|
||||
- [x] Two `CRDTDiscovery` instances with Yjs provider via in-process signalling; assert peer discovery event fires.
|
||||
- [x] Automerge provider round-trip.
|
||||
- [x] Awareness payload with malformed data rejected with `AWARENESS_REJECTED`.
|
||||
- [x] Encryption: encrypted awareness payload cannot be read without the key.
|
||||
|
||||
---
|
||||
|
||||
## 11. Docs
|
||||
|
||||
- [x] Write `.md` per module (≥200 words, API reference, examples).
|
||||
- [x] Write `README.md` with provider-adapter table, signalling self-hosting guidance, encryption opt-in.
|
||||
|
||||
---
|
||||
|
||||
## 12. Verify and publish
|
||||
|
||||
- [x] `bunx turbo lint typecheck test --filter=@suaveplan/network-discovery-crdt` — green.
|
||||
- [x] `bunx turbo test:coverage --filter=@suaveplan/network-discovery-crdt` — 100%.
|
||||
- [x] `bun run docs:check --filter=@suaveplan/network-discovery-crdt` — zero errors.
|
||||
- [x] No `vi.mock`, `any`, `ts-ignore`, `.skip` in `src/`.
|
||||
- [x] Story builds cleanly; React dep declared.
|
||||
- [x] Awareness payload Zod schema registered.
|
||||
- [x] `bun scripts/publish.ts --package @suaveplan/network-discovery-crdt`.
|
||||
- [x] Archive to `openspec/archive/2026-04-24-enterprise-browser-network-discovery-crdt/`.
|
||||
Reference in New Issue
Block a user