Initial Commit
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
# OpenSpec Change: enterprise-network-bootstrap
|
||||
|
||||
- **Change ID**: enterprise-network-bootstrap
|
||||
- **Package**: `@suaveplan/network-bootstrap`
|
||||
- **Category**: universal/network
|
||||
- **Target path**: `universal/network/network-bootstrap`
|
||||
- **Status**: SUPERSEDED-BY-GENESIS-WAVE
|
||||
- **Date**: 2026-04-24
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/network-bootstrap.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Deliver `@suaveplan/network-bootstrap` on the genesis branch from scratch at enterprise quality. The package provides network bootstrap and initialization: seed peer discovery, initial topology negotiation, connection establishment sequencing, bootstrap state machine, retry-with-backoff on seed failure, and a readiness signal when the minimum peer threshold is met. Genesis adds structured errors via `@suaveplan/error`, Zod schemas for all bootstrap configuration types, OTel instrumentation on every bootstrap phase, `AbortSignal` on the full bootstrap sequence, injectable `Clock` and `IdGenerator`, event-bus integration for phase transitions, and property-based tests for state machine invariants.
|
||||
|
||||
---
|
||||
|
||||
## 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 `NetworkBootstrapError`; stable error codes |
|
||||
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock`, `IdGenerator`, `EventBusLike` |
|
||||
| `@suaveplan/events` (`workspace:^`) | Event bus for bootstrap phase transitions |
|
||||
| `@suaveplan/logger` (`workspace:^`) | Structured logging |
|
||||
| `@suaveplan/retry` (`workspace:^`) | Retry with exponential backoff on seed discovery |
|
||||
| `@suaveplan/fsm` (`workspace:^`) | FSM for bootstrap state machine |
|
||||
| `zod` (`catalog:forms`) | Zod schemas |
|
||||
| `@opentelemetry/api` (`catalog:observability`) | Spans + counters on bootstrap phases |
|
||||
| `tslib` (`catalog:build`) | Runtime helpers |
|
||||
|
||||
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`).
|
||||
|
||||
---
|
||||
|
||||
## Public API surface
|
||||
|
||||
### Core
|
||||
|
||||
```ts
|
||||
function createBootstrapper(config: BootstrapConfig, options?: BootstrapOptions): Bootstrapper
|
||||
|
||||
interface Bootstrapper {
|
||||
readonly state: BootstrapState
|
||||
readonly peers: ReadonlyArray<BootstrapPeer>
|
||||
bootstrap(signal?: AbortSignal): Promise<BootstrapResult>
|
||||
abort(): void
|
||||
on(event: BootstrapEvent, handler: (data: BootstrapEventData) => void): () => void
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
```ts
|
||||
interface BootstrapConfig {
|
||||
seeds: SeedConfig[]
|
||||
minPeers?: number
|
||||
maxPeers?: number
|
||||
topology?: TopologyHint
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
interface SeedConfig {
|
||||
id?: string
|
||||
address: string
|
||||
port: number
|
||||
priority?: number
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
type TopologyHint = 'mesh' | 'star' | 'ring' | 'tree'
|
||||
|
||||
interface BootstrapOptions {
|
||||
retry?: RetryOptions
|
||||
logger?: LoggerLike
|
||||
clock?: Clock
|
||||
idGenerator?: IdGenerator
|
||||
eventBus?: EventBusLike
|
||||
}
|
||||
```
|
||||
|
||||
### State types
|
||||
|
||||
```ts
|
||||
type BootstrapState =
|
||||
| 'idle'
|
||||
| 'discovering'
|
||||
| 'connecting'
|
||||
| 'negotiating'
|
||||
| 'ready'
|
||||
| 'failed'
|
||||
| 'aborted'
|
||||
|
||||
interface BootstrapPeer {
|
||||
readonly id: string
|
||||
readonly address: string
|
||||
readonly port: number
|
||||
readonly connectedAt: number
|
||||
readonly latencyMs?: number
|
||||
readonly capabilities?: string[]
|
||||
}
|
||||
|
||||
interface BootstrapResult {
|
||||
readonly success: boolean
|
||||
readonly peers: ReadonlyArray<BootstrapPeer>
|
||||
readonly durationMs: number
|
||||
readonly error?: NetworkBootstrapError
|
||||
}
|
||||
|
||||
interface BootstrapEventData {
|
||||
readonly state: BootstrapState
|
||||
readonly peers?: ReadonlyArray<BootstrapPeer>
|
||||
readonly seed?: SeedConfig
|
||||
readonly error?: string
|
||||
}
|
||||
|
||||
type BootstrapEvent = 'state-change' | 'peer-discovered' | 'peer-connected' | 'ready' | 'failed'
|
||||
```
|
||||
|
||||
### Error codes
|
||||
|
||||
```ts
|
||||
type NetworkBootstrapErrorCode =
|
||||
| 'BOOTSTRAP_NO_SEEDS'
|
||||
| 'BOOTSTRAP_SEED_UNREACHABLE'
|
||||
| 'BOOTSTRAP_TIMEOUT'
|
||||
| 'BOOTSTRAP_MIN_PEERS_NOT_MET'
|
||||
| 'BOOTSTRAP_NEGOTIATION_FAILED'
|
||||
| 'BOOTSTRAP_ABORT'
|
||||
|
||||
function registerNetworkBootstrapErrorCodes(): void
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Genesis additions
|
||||
|
||||
1. **Structured `NetworkBootstrapError`** with stable `BOOTSTRAP.*` codes
|
||||
2. **Zod schemas** for `BootstrapConfig`, `SeedConfig`, `BootstrapOptions`
|
||||
3. **FSM-backed state machine** via `@suaveplan/fsm` — states: idle → discovering → connecting → negotiating → ready/failed
|
||||
4. **Retry on seed discovery** via `@suaveplan/retry` with injectable config
|
||||
5. **Minimum peer threshold** — resolves `bootstrap()` promise only when `minPeers` is reached
|
||||
6. **Injectable `Clock` and `IdGenerator`** for deterministic tests
|
||||
7. **OTel instrumentation** — span `bootstrap.run`, child spans per seed, histogram `bootstrap.duration_ms`, counter `bootstrap.peers_connected_total`
|
||||
8. **Property-based tests** — state machine transitions always reach a terminal state; min-peers invariant
|
||||
|
||||
---
|
||||
|
||||
## File layout
|
||||
|
||||
```text
|
||||
universal/network/network-bootstrap/
|
||||
package.json
|
||||
tsconfig.json
|
||||
vitest.config.ts
|
||||
src/
|
||||
index.ts
|
||||
error-codes.ts
|
||||
constants/
|
||||
index.ts
|
||||
types/
|
||||
common.ts
|
||||
common.md
|
||||
core/
|
||||
bootstrapper/
|
||||
bootstrapper.ts
|
||||
bootstrapper.md
|
||||
bootstrapper.types.ts
|
||||
bootstrapper.test.ts
|
||||
index.ts
|
||||
discovery/
|
||||
discovery.ts
|
||||
discovery.md
|
||||
discovery.test.ts
|
||||
index.ts
|
||||
negotiation/
|
||||
negotiation.ts
|
||||
negotiation.md
|
||||
negotiation.test.ts
|
||||
index.ts
|
||||
tests/
|
||||
integration.test.ts
|
||||
property.test.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Impact
|
||||
|
||||
**Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/logger`, `@suaveplan/retry`, `@suaveplan/fsm` must be published.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Tasks: enterprise-network-bootstrap
|
||||
|
||||
**Change ID**: enterprise-network-bootstrap
|
||||
**Package**: `@suaveplan/network-bootstrap`
|
||||
**Target path**: `universal/network/network-bootstrap`
|
||||
**Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/logger`, `@suaveplan/retry`, `@suaveplan/fsm`
|
||||
|
||||
---
|
||||
|
||||
## 1. Scaffold
|
||||
|
||||
- [x] Create `universal/network/network-bootstrap/` with `src/`, `tests/`, and all feature subdirectories.
|
||||
- [x] Write `package.json` at version `0.1.0`: `"sideEffects": false`, exports map, deps per `proposal.md`.
|
||||
- [x] Write `tsconfig.json`, `vitest.config.ts`, scripts.
|
||||
|
||||
---
|
||||
|
||||
## 2. Types and error codes
|
||||
|
||||
- [x] Define all public types in `src/types/common.ts`: `BootstrapConfig`, `SeedConfig`, `TopologyHint`, `BootstrapOptions`, `BootstrapState`, `BootstrapPeer`, `BootstrapResult`, `BootstrapEvent`, `BootstrapEventData`.
|
||||
- [x] Write `src/types/common.md` (≥200 words, simple + advanced examples).
|
||||
- [x] Define error codes in `src/error-codes.ts`.
|
||||
- [x] Write Zod schemas in `src/types/schemas.ts` + `src/types/schemas.md`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Peer discovery (`src/core/discovery/`)
|
||||
|
||||
- [x] Implement `discoverPeers(seeds: SeedConfig[], options, signal?)`:
|
||||
- Try each seed in priority order.
|
||||
- Use `@suaveplan/retry` to retry each seed up to `options.retry.maxAttempts`.
|
||||
- Return list of discovered peer addresses.
|
||||
- Throw `NetworkBootstrapError(BOOTSTRAP_SEED_UNREACHABLE)` if all seeds fail.
|
||||
- [x] Write `discovery.test.ts` (≤500 lines): happy path, retry on failure, all fail throws.
|
||||
- [x] Write `discovery.md` (≥200 words).
|
||||
|
||||
---
|
||||
|
||||
## 4. Peer negotiation (`src/core/negotiation/`)
|
||||
|
||||
- [x] Implement `negotiatePeer(address: string, port: number, config, signal?)`:
|
||||
- Connect to peer; exchange capability and topology messages.
|
||||
- Return `BootstrapPeer` with capabilities and measured latency.
|
||||
- Throw `NetworkBootstrapError(BOOTSTRAP_NEGOTIATION_FAILED)` on failure.
|
||||
- [x] Write `negotiation.test.ts` + `negotiation.md`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Bootstrapper (`src/core/bootstrapper/`)
|
||||
|
||||
- [x] Implement `createBootstrapper(config, options?)`:
|
||||
- Validates config with Zod.
|
||||
- Uses `@suaveplan/fsm` for state machine: `idle → discovering → connecting → negotiating → ready/failed`.
|
||||
- [x] Implement `bootstrapper.bootstrap(signal?)`:
|
||||
- Transition FSM through phases.
|
||||
- Phase 1 (discovering): call `discoverPeers`.
|
||||
- Phase 2 (connecting): establish connections to discovered peers.
|
||||
- Phase 3 (negotiating): negotiate capabilities with each peer.
|
||||
- Resolve when `peers.length >= config.minPeers` (default 1).
|
||||
- Throw `NetworkBootstrapError(BOOTSTRAP_MIN_PEERS_NOT_MET)` if timeout elapses.
|
||||
- OTel: span `bootstrap.run`, child spans per phase, counter `bootstrap.peers_connected_total`.
|
||||
- [x] Implement `bootstrapper.abort()` — transitions FSM to `aborted`, fires `AbortSignal`.
|
||||
- [x] Implement `bootstrapper.on(event, handler)` — fires on FSM state transitions.
|
||||
- [x] Write `bootstrapper.test.ts` (≤500 lines): full bootstrap, abort, timeout, min-peers not met.
|
||||
- [x] Write `bootstrapper.md` (≥200 words).
|
||||
|
||||
---
|
||||
|
||||
## 6. Property-based tests (`tests/property.test.ts`)
|
||||
|
||||
- [x] State machine always reaches a terminal state (`ready`, `failed`, or `aborted`).
|
||||
- [x] `peers.length >= minPeers` when result is `success: true`.
|
||||
- [x] Seed priority ordering: higher-priority seeds are always tried before lower-priority ones.
|
||||
|
||||
---
|
||||
|
||||
## 7. Docs and verify
|
||||
|
||||
- [x] All per-file `.md` files per layout in `proposal.md`.
|
||||
- [x] `universal/network/network-bootstrap/README.md`.
|
||||
- [x] `bunx turbo lint typecheck test test:coverage --filter=@suaveplan/network-bootstrap` — all green, 100%.
|
||||
- [x] `bun scripts/publish.ts --package @suaveplan/network-bootstrap`.
|
||||
- [x] Update `CLAUDE.md`. Archive change folder.
|
||||
Reference in New Issue
Block a user