Updates
This commit is contained in:
@@ -1,36 +1,90 @@
|
||||
# hyper-p2p-capability-discovery
|
||||
|
||||
Gossip capability tags and metadata for capability-based peer selection.
|
||||
Gossip capability names and metadata so peers can find who offers a given service on a shared Hyperswarm topic.
|
||||
|
||||
**Category:** network-discovery
|
||||
|
||||
**Composes with:** hyper-p2p-capabilities
|
||||
|
||||
**Protocol:** `capability-discovery/v1`
|
||||
**Category:** network-discovery · **Protocol:** `capability-discovery/v1` · **Exports:** `HyperP2PCapabilityDiscovery`, `PROTOCOL`
|
||||
|
||||
## When to use
|
||||
|
||||
Mesh apps where peers advertise services (storage, relay, compute) and consumers query by capability name.
|
||||
Capability-based peer selection (relay, storage, compute tags) on one swarm topic.
|
||||
|
||||
## When not to use
|
||||
|
||||
Fixed peer lists or DHT-only discovery with no shared gossip topic.
|
||||
Fixed peer lists or discovery without gossip.
|
||||
|
||||
## Quick start
|
||||
|
||||
```js
|
||||
const { HyperP2PCapabilityDiscovery } = require('hyper-p2p-capability-discovery')
|
||||
const mod = new HyperP2PCapabilityDiscovery({ topic: process.argv[2] })
|
||||
await mod.ready()
|
||||
mod.registerCapability('relay', { version: 1 })
|
||||
console.log(mod.findByCapability('relay'))
|
||||
await mod.close()
|
||||
const cap = new HyperP2PCapabilityDiscovery({ topic: 'discover' })
|
||||
await cap.ready()
|
||||
cap.registerCapability('storage', { region: 'eu' })
|
||||
console.log(cap.findByCapability('storage'))
|
||||
await cap.close()
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
- [docs/api.md](docs/api.md) — constructor, methods, events, errors
|
||||
- [docs/architecture.md](docs/architecture.md) — wire types, state, composition
|
||||
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| Member | Description |
|
||||
|--------|-------------|
|
||||
| `new HyperP2PCapabilityDiscovery(opts?)` | `topic`, `keyPair` (default random). Sets `peerHex` from public key. |
|
||||
| `registerCapability(name, meta?)` | Local register + gossip. Returns `{ name, meta, peer, at }`. |
|
||||
| `findByCapability(name)` | All entries for `name` (local + remote). |
|
||||
| `listCapabilities()` | Distinct capability names. |
|
||||
| `getStats()` | `{ registered, gossipIn, gossipOut, capabilities, peers, protocol }`. |
|
||||
| `ready()` / `close()` | Join swarm when `topic` set; clear maps and destroy swarm. |
|
||||
|
||||
**Events:** `register`, `remote-register`, `closed`. Gossip only after `ready()` with `topic`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Peer A --cap-register--> swarm peers --merge by (name, peer)--> in-memory Map<name, Map<peerHex, entry>>
|
||||
```
|
||||
|
||||
State is `_byName`: capability → peer hex → entry. Remote updates apply last-write-wins on `at`. Local `registerCapability` always wins for this peer’s hex bucket. Works without `topic` (local-only; no gossip).
|
||||
|
||||
## Wire table
|
||||
|
||||
| `type` | Fields | Direction |
|
||||
|--------|--------|-----------|
|
||||
| `cap-register` | `name`, `meta`, `peer`, `at` | Any peer → all |
|
||||
|
||||
## Errors
|
||||
|
||||
- Empty `name` on `registerCapability` / `findByCapability` (`assertNonEmpty`).
|
||||
- Without `topic`, `ready()` is a no-op for swarm; gossip requires joined peers.
|
||||
|
||||
## Composition
|
||||
|
||||
- Hyperswarm topic is the discovery plane; capability name is the app-level index.
|
||||
- Pair with `hyper-p2p-seeder-registry` when capabilities imply content topics.
|
||||
- Local-only mode: omit `topic` and use `registerCapability` / `findByCapability` in-process.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const { HyperP2PCapabilityDiscovery } = require('hyper-p2p-capability-discovery')
|
||||
|
||||
const disc = new HyperP2PCapabilityDiscovery({ topic: process.argv[2] })
|
||||
disc.on('remote-register', (e) => console.log('remote', e.name, e.peer))
|
||||
|
||||
await disc.ready()
|
||||
disc.registerCapability('relay', { version: 1 })
|
||||
console.log(disc.findByCapability('relay'))
|
||||
console.log(disc.getStats())
|
||||
await disc.close()
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
|
||||
@@ -1,45 +1,91 @@
|
||||
# API: hyper-p2p-capability-discovery
|
||||
|
||||
**Protocol:** `capability-discovery/v1`
|
||||
**Protocol:** `capability-discovery/v1` · **Export:** `HyperP2PCapabilityDiscovery`, `PROTOCOL`
|
||||
|
||||
**Export:** `HyperP2PCapabilityDiscovery`
|
||||
## Overview
|
||||
|
||||
Gossip registry mapping capability names to peers offering them. Supports multi-peer entries per capability with LWW merge on `at` timestamp.
|
||||
|
||||
## Constructor
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `topic` | string \| Buffer | null | Hyperswarm join topic |
|
||||
| `keyPair` | KeyPair | random | Ed25519 identity |
|
||||
| `topic` | `string` \| `Buffer` | `null` | Swarm topic |
|
||||
| `keyPair` | `KeyPair` | random | `peerHex` identity |
|
||||
|
||||
## Methods
|
||||
|
||||
### `registerCapability(name, meta = {})`
|
||||
### `registerCapability(name, meta?)`
|
||||
|
||||
- **Returns:** `{ name, meta, peer, at }`
|
||||
- **Throws:** `ValidationError` if `name` is empty
|
||||
- **Returns:** `{ name, meta, peer: peerHex, at }`
|
||||
- **Throws:** `assertNonEmpty` on `name`
|
||||
- **Gossip:** `{ type: 'cap-register', name, meta, peer, at }`
|
||||
- **Emits:** `register`
|
||||
|
||||
### `findByCapability(name)`
|
||||
|
||||
- **Returns:** `Array<entry>` for that capability (all peers)
|
||||
- **Returns:** array of entries for capability
|
||||
|
||||
### `listCapabilities()`
|
||||
|
||||
- **Returns:** `string[]` — capability names with at least one peer
|
||||
- **Returns:** array of capability name strings
|
||||
|
||||
### `getStats()` / `ready()` / `close()`
|
||||
### `ready()` / `close()`
|
||||
|
||||
Standard lifecycle; see architecture doc.
|
||||
Clears `_byName` on close.
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Payload |
|
||||
|-------|---------|
|
||||
| `register` | local entry |
|
||||
| `remote-register` | remote entry |
|
||||
| `remote-register` | merged remote entry |
|
||||
| `closed` | — |
|
||||
|
||||
## getStats()
|
||||
|
||||
`registered`, `gossipIn`, `gossipOut`, `capabilities` (name count), `peers` (total entries), `protocol`.
|
||||
|
||||
## Wire
|
||||
|
||||
| type | fields | behavior |
|
||||
|------|--------|----------|
|
||||
| `cap-register` | `name`, `meta`, `peer`, `at` | LWW per `(name, peer)` bucket |
|
||||
|
||||
## Errors
|
||||
|
||||
`assertNonEmpty` on `name` in public methods.
|
||||
|
||||
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
|
||||
|
||||
## P2P
|
||||
|
||||
`_onGossip` resolves `peer` from message or `peerInfo.publicKey`.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
cd modules/network-discovery/hyper-p2p-capability-discovery && npm test
|
||||
```
|
||||
|
||||
## Composition
|
||||
|
||||
`hyper-p2p-topic-announcer`, `hyper-p2p-seeder-registry`, `hyper-p2p-discovery-health`.
|
||||
|
||||
## Example
|
||||
|
||||
See [`examples/basic.js`](../examples/basic.js).
|
||||
|
||||
## Remote merge rules
|
||||
|
||||
- Incoming `cap-register` ignored without `name`
|
||||
- Peer id from `data.peer` or gossip sender `publicKey`
|
||||
- Replace bucket entry when `at >= prev.at`
|
||||
|
||||
## Lifecycle
|
||||
|
||||
Call `ready()` before expecting gossip side effects. `close()` destroys swarm and clears capability map.
|
||||
|
||||
## See also
|
||||
|
||||
[`docs/architecture.md`](architecture.md), [`../../MODULE_CATEGORIES.md`](../../MODULE_CATEGORIES.md).
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
# Architecture: hyper-p2p-capability-discovery
|
||||
|
||||
**Category:** network-discovery
|
||||
**Protocol:** `capability-discovery/v1` · **Category:** network-discovery
|
||||
|
||||
## Wire messages
|
||||
|
||||
| type | fields | direction | behavior |
|
||||
|------|--------|-----------|----------|
|
||||
| `cap-register` | `name`, `meta`, `peer`, `at` | gossip | Upsert peer under capability name |
|
||||
| type | direction | fields | behavior |
|
||||
|------|-----------|--------|----------|
|
||||
| `cap-register` | gossip | `name`, `meta`, `peer`, `at` | LWW merge into `_byName[name][peer]`; emit `remote-register` |
|
||||
|
||||
## State model
|
||||
|
||||
- `_byName`: `Map<name, Map<peerHex, entry>>`
|
||||
- One entry per peer per capability (LWW on `at`)
|
||||
`_byName`: `Map<name, Map<peerHex, entry>>`.
|
||||
|
||||
## Composition
|
||||
|
||||
`initModuleSwarm` + `gossipSend` from `../../_shared/p2p-bare.js`.
|
||||
`hyper-p2p-topic-announcer`, `hyper-p2p-seeder-registry`.
|
||||
|
||||
@@ -1,39 +1,88 @@
|
||||
# hyper-p2p-discovery-health
|
||||
|
||||
Gossip peer health scores and RTT hints for discovery overlays.
|
||||
Gossip peer health reports (ok, score, RTT) for discovery-time filtering on a shared topic.
|
||||
|
||||
**Category:** network-discovery
|
||||
|
||||
**Composes with:** hyper-p2p-link-probe
|
||||
|
||||
**Protocol:** `discovery-health/v1`
|
||||
**Category:** network-discovery · **Protocol:** `discovery-health/v1` · **Exports:** `HyperP2PDiscoveryHealth`, `PROTOCOL`
|
||||
|
||||
## When to use
|
||||
|
||||
Discovery layers that rank or filter peers by health before routing or replication.
|
||||
Aggregate liveness and quality signals across a mesh before routing or bootstrap.
|
||||
|
||||
## When not to use
|
||||
|
||||
Full observability stacks (use hyper-p2p-health-probe or metrics modules instead).
|
||||
Centralized monitoring only (no P2P gossip needed).
|
||||
|
||||
## Quick start
|
||||
|
||||
```js
|
||||
const { HyperP2PDiscoveryHealth } = require('hyper-p2p-discovery-health')
|
||||
const mod = new HyperP2PDiscoveryHealth({ topic: process.argv[2] })
|
||||
await mod.ready()
|
||||
mod.reportPeer('peer-a', { ok: true, rttMs: 12 })
|
||||
console.log(mod.healthyPeers())
|
||||
await mod.close()
|
||||
const health = new HyperP2PDiscoveryHealth({ topic: 'discover' })
|
||||
await health.ready()
|
||||
health.reportPeer('abc123', { ok: true, score: 0.9, rttMs: 42 })
|
||||
console.log(health.healthyPeers())
|
||||
await health.close()
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
- [docs/api.md](docs/api.md) — constructor, methods, events, errors
|
||||
- [docs/architecture.md](docs/architecture.md) — wire types, state, composition
|
||||
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
npm test
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| Member | Description |
|
||||
|--------|-------------|
|
||||
| `new HyperP2PDiscoveryHealth(opts?)` | `topic`, `keyPair`. |
|
||||
| `reportPeer(peerId, status?)` | `{ ok, score, rttMs, ...status }`. Default `ok: true`, `score: 1`. Gossips `peer-health`. |
|
||||
| `get(peerId)` | Entry or `null`. |
|
||||
| `healthyPeers()` | Entries with `ok !== false`. |
|
||||
| `getStats()` | `{ reports, gossipIn, gossipOut, peers, healthy, protocol }`. |
|
||||
| `ready()` / `close()` | Swarm lifecycle. |
|
||||
|
||||
**Events:** `report`, `remote-report`, `closed`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
reportPeer --> _peers Map(peerId -> entry) --> gossip peer-health --> peers merge by peerId + at (LWW)
|
||||
```
|
||||
|
||||
`healthyPeers()` is a view filter, not persisted separately. Without `topic`, reports stay local.
|
||||
|
||||
## Wire table
|
||||
|
||||
| `type` | Fields | Direction |
|
||||
|--------|--------|-----------|
|
||||
| `peer-health` | `entry`: `{ peerId, ok, score, rttMs, at, ... }` | Any → all |
|
||||
|
||||
## Errors
|
||||
|
||||
- Empty `peerId` on `reportPeer` / `get`.
|
||||
- Extra fields on `status` are merged into the stored entry (spread after defaults).
|
||||
|
||||
## Composition
|
||||
|
||||
- Feed reports from probes or `hyper-p2p-udx-metrics` RTT estimates.
|
||||
- Filter bootstrap candidates with `healthyPeers()` before `hyper-p2p-peer-bootstrap-store` use.
|
||||
- Omit `topic` for single-node health caches without gossip.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const { HyperP2PDiscoveryHealth } = require('hyper-p2p-discovery-health')
|
||||
|
||||
const health = new HyperP2PDiscoveryHealth({ topic: process.argv[2] })
|
||||
await health.ready()
|
||||
|
||||
health.reportPeer('abc123', { ok: true, score: 0.9, rttMs: 42 })
|
||||
health.reportPeer('dead-peer', { ok: false, score: 0 })
|
||||
|
||||
console.log(health.healthyPeers())
|
||||
console.log(health.getStats())
|
||||
await health.close()
|
||||
```
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
# API: hyper-p2p-discovery-health
|
||||
|
||||
**Protocol:** `discovery-health/v1`
|
||||
**Protocol:** `discovery-health/v1` · **Export:** `HyperP2PDiscoveryHealth`, `PROTOCOL`
|
||||
|
||||
**Export:** `HyperP2PDiscoveryHealth`
|
||||
## Overview
|
||||
|
||||
Gossip peer health reports with ok/score/rtt and arbitrary status fields. Remote reports merge LWW by `entry.at`.
|
||||
|
||||
## Constructor
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `topic` | `string` \| `Buffer` | `null` | Swarm topic |
|
||||
| `keyPair` | `KeyPair` | random | Local identity |
|
||||
|
||||
## Methods
|
||||
|
||||
### `reportPeer(peerId, status = {})`
|
||||
### `reportPeer(peerId, status?)`
|
||||
|
||||
- **Returns:** entry with `ok`, `score`, `rttMs`, `at`, plus spread `status`
|
||||
- **Throws:** `ValidationError` if `peerId` is empty
|
||||
Builds entry: `ok` defaults true unless `status.ok === false`; `score` defaults to `0` if not ok else `1` unless numeric `status.score`; `rttMs` from status or `0`.
|
||||
|
||||
- **Returns:** entry
|
||||
- **Throws:** `assertNonEmpty` on `peerId`
|
||||
- **Gossip:** `{ type: 'peer-health', entry }`
|
||||
- **Emits:** `report`
|
||||
|
||||
### `get(peerId)`
|
||||
|
||||
@@ -17,18 +30,63 @@
|
||||
|
||||
### `healthyPeers()`
|
||||
|
||||
- **Returns:** entries where `ok !== false`
|
||||
- **Returns:** entries where `ok === true`
|
||||
|
||||
### `ready()` / `close()`
|
||||
|
||||
Standard P2P lifecycle.
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Payload |
|
||||
|-------|---------|
|
||||
| `report` | local entry |
|
||||
| `remote-report` | remote entry |
|
||||
| `remote-report` | merged entry |
|
||||
| `closed` | — |
|
||||
|
||||
## getStats()
|
||||
|
||||
`reports`, `gossipIn`, `gossipOut`, `peers`, `healthy`, `protocol`.
|
||||
|
||||
## Wire
|
||||
|
||||
| type | fields | behavior |
|
||||
|------|--------|----------|
|
||||
| `peer-health` | `entry` (full object) | LWW on `entry.peerId` by `entry.at` |
|
||||
|
||||
## Errors
|
||||
|
||||
`assertNonEmpty` on `peerId`.
|
||||
|
||||
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
|
||||
|
||||
## P2P
|
||||
|
||||
Spreads health scores for discovery ranking alongside capability and seeder modules.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
cd modules/network-discovery/hyper-p2p-discovery-health && npm test
|
||||
```
|
||||
|
||||
## Composition
|
||||
|
||||
`hyper-p2p-capability-discovery`, `hyper-p2p-seeder-registry`, `hyper-p2p-load-spread`.
|
||||
|
||||
## Example
|
||||
|
||||
See [`examples/basic.js`](../examples/basic.js).
|
||||
|
||||
## Remote merge rules
|
||||
|
||||
- Only `peer-health` with `entry` object applied
|
||||
- Replace when `entry.at >= prev.at` for same `peerId`
|
||||
|
||||
## Lifecycle
|
||||
|
||||
`healthyPeers()` is derived filter — not persisted separately from `_peers`.
|
||||
|
||||
## See also
|
||||
|
||||
[`docs/architecture.md`](architecture.md), [`../../MODULE_CATEGORIES.md`](../../MODULE_CATEGORIES.md).
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# Architecture: hyper-p2p-discovery-health
|
||||
|
||||
**Category:** network-discovery
|
||||
**Protocol:** `discovery-health/v1` · **Category:** network-discovery
|
||||
|
||||
## Wire messages
|
||||
|
||||
| type | fields | direction | behavior |
|
||||
|------|--------|-----------|----------|
|
||||
| `peer-health` | `entry` (`peerId`, `ok`, `score`, `rttMs`, `at`, …) | gossip | LWW merge on `entry.at` |
|
||||
| type | direction | fields | behavior |
|
||||
|------|-----------|--------|----------|
|
||||
| `peer-health` | gossip | `entry` (`peerId`, `ok`, `score`, `rttMs`, `at`, …) | LWW merge; emit `remote-report` |
|
||||
|
||||
## State model
|
||||
|
||||
- `_peers`: `Map<peerId, entry>`
|
||||
`_peers`: Map peerId → health entry.
|
||||
|
||||
## Composition
|
||||
|
||||
Discovery overlay health scores; pair with hyper-p2p-link-probe for measurements.
|
||||
`hyper-p2p-capability-discovery`, `hyper-p2p-seeder-registry`.
|
||||
|
||||
@@ -1,39 +1,85 @@
|
||||
# hyper-p2p-peer-bootstrap-store
|
||||
|
||||
Gossip bootstrap hints (host, port, relays) keyed by peer id.
|
||||
Gossip bootstrap hints (addresses, routes) keyed by peer id for mesh cold-start and reconnection.
|
||||
|
||||
**Category:** network-discovery
|
||||
|
||||
**Composes with:** hyper-p2p-gossip-mesh
|
||||
|
||||
**Protocol:** `peer-bootstrap-store/v1`
|
||||
**Category:** network-discovery · **Protocol:** `peer-bootstrap-store/v1` · **Exports:** `HyperP2PPeerBootstrapStore`, `PROTOCOL`
|
||||
|
||||
## When to use
|
||||
|
||||
Apps that collect dial hints from peers and need them replicated across a discovery topic.
|
||||
Peers share how to reach other peers (`hints` objects) on one discovery topic.
|
||||
|
||||
## When not to use
|
||||
|
||||
Centralized bootstrap servers only, or when hints must stay local (do not call `ready()`).
|
||||
Static bootstrap lists baked into config only.
|
||||
|
||||
## Quick start
|
||||
|
||||
```js
|
||||
const { HyperP2PPeerBootstrapStore } = require('hyper-p2p-peer-bootstrap-store')
|
||||
const mod = new HyperP2PPeerBootstrapStore({ topic: process.argv[2] })
|
||||
await mod.ready()
|
||||
mod.addBootstrap('peer-1', { host: '127.0.0.1', port: 49737 })
|
||||
console.log(mod.allBootstraps())
|
||||
await mod.close()
|
||||
const store = new HyperP2PPeerBootstrapStore({ topic: 'discover' })
|
||||
await store.ready()
|
||||
store.addBootstrap('peer-a', { host: '1.2.3.4', port: 49737 })
|
||||
console.log(store.allBootstraps())
|
||||
await store.close()
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
- [docs/api.md](docs/api.md) — constructor, methods, events, errors
|
||||
- [docs/architecture.md](docs/architecture.md) — wire types, state, composition
|
||||
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
npm test
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| Member | Description |
|
||||
|--------|-------------|
|
||||
| `new HyperP2PPeerBootstrapStore(opts?)` | `topic`, `keyPair`; `peerHex` = local public key hex. |
|
||||
| `addBootstrap(peerId, hints?)` | Store + gossip. Returns `{ peerId, hints, from, at }`. |
|
||||
| `getBootstrap(peerId)` | Entry or `null`. |
|
||||
| `allBootstraps()` | All values. |
|
||||
| `getStats()` | `{ added, gossipIn, gossipOut, bootstraps, protocol }`. |
|
||||
| `ready()` / `close()` | Swarm lifecycle. |
|
||||
|
||||
**Events:** `add`, `remote-add`, `closed`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
addBootstrap --> _bootstraps Map(peerId) --> bootstrap-add gossip --> LWW on at per peerId
|
||||
```
|
||||
|
||||
`from` records gossip source (hex public key). Remote `from` falls back to sender’s swarm public key when missing.
|
||||
|
||||
## Wire table
|
||||
|
||||
| `type` | Fields | Direction |
|
||||
|--------|--------|-----------|
|
||||
| `bootstrap-add` | `peerId`, `hints`, `from`, `at` | Any → all |
|
||||
|
||||
## Errors
|
||||
|
||||
- Empty `peerId` on `addBootstrap` / `getBootstrap`.
|
||||
- `hints` defaults to `{}` when omitted.
|
||||
|
||||
## Composition
|
||||
|
||||
- Populate `hints` from DHT or relay modules (`hyper-p2p-dht-bootstrap-hint`, `hyper-p2p-blind-relay-bridge`).
|
||||
- Consumers read `allBootstraps()` after `discovery-health` filtering.
|
||||
- `from` helps trace which peer advertised a route.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const { HyperP2PPeerBootstrapStore } = require('hyper-p2p-peer-bootstrap-store')
|
||||
|
||||
const store = new HyperP2PPeerBootstrapStore({ topic: process.argv[2] })
|
||||
await store.ready()
|
||||
|
||||
store.addBootstrap('peer-a', { host: '1.2.3.4', port: 49737 })
|
||||
console.log(store.allBootstraps())
|
||||
await store.close()
|
||||
```
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
# API: hyper-p2p-peer-bootstrap-store
|
||||
|
||||
**Protocol:** `peer-bootstrap-store/v1`
|
||||
**Protocol:** `peer-bootstrap-store/v1` · **Export:** `HyperP2PPeerBootstrapStore`, `PROTOCOL`
|
||||
|
||||
**Export:** `HyperP2PPeerBootstrapStore`
|
||||
## Overview
|
||||
|
||||
Stores bootstrap hint objects per `peerId` and gossips additions. Remote entries merge LWW on `at`.
|
||||
|
||||
## Constructor
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `topic` | `string` \| `Buffer` | `null` | Swarm topic |
|
||||
| `keyPair` | `KeyPair` | random | `peerHex` |
|
||||
|
||||
## Methods
|
||||
|
||||
### `addBootstrap(peerId, hints = {})`
|
||||
### `addBootstrap(peerId, hints?)`
|
||||
|
||||
- **Returns:** `{ peerId, hints, from, at }`
|
||||
- **Returns:** `{ peerId, hints, from: peerHex, at }`
|
||||
- **Throws:** `assertNonEmpty` on `peerId`
|
||||
- **Gossip:** `{ type: 'bootstrap-add', peerId, hints, from, at }`
|
||||
- **Emits:** `add`
|
||||
|
||||
### `getBootstrap(peerId)`
|
||||
|
||||
@@ -16,18 +28,63 @@
|
||||
|
||||
### `allBootstraps()`
|
||||
|
||||
- **Returns:** `Array<entry>`
|
||||
- **Returns:** array of all entries
|
||||
|
||||
### `ready()` / `close()`
|
||||
|
||||
Clears store on close.
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Payload |
|
||||
|-------|---------|
|
||||
| `add` | local entry |
|
||||
| `remote-add` | remote entry |
|
||||
| `remote-add` | merged entry |
|
||||
| `closed` | — |
|
||||
|
||||
## getStats()
|
||||
|
||||
`added`, `gossipIn`, `gossipOut`, `bootstraps`, `protocol`.
|
||||
|
||||
## Wire
|
||||
|
||||
| type | fields | behavior |
|
||||
|------|--------|----------|
|
||||
| `bootstrap-add` | `peerId`, `hints`, `from`, `at` | LWW per `peerId` |
|
||||
|
||||
## Errors
|
||||
|
||||
`assertNonEmpty` on `peerId`.
|
||||
|
||||
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
|
||||
|
||||
## P2P
|
||||
|
||||
`from` falls back to gossip sender public key hex.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
cd modules/network-discovery/hyper-p2p-peer-bootstrap-store && npm test
|
||||
```
|
||||
|
||||
## Composition
|
||||
|
||||
`hyper-p2p-dht-bootstrap-hint`, `hyper-bare-bundle-bridge`, `hyper-p2p-seeder-registry`.
|
||||
|
||||
## Example
|
||||
|
||||
See [`examples/basic.js`](../examples/basic.js).
|
||||
|
||||
## Remote merge rules
|
||||
|
||||
- `bootstrap-add` LWW on `peerId` by `at`
|
||||
- `from` resolved from message or gossip `peerInfo.publicKey`
|
||||
|
||||
## Lifecycle
|
||||
|
||||
`allBootstraps()` returns live snapshot; not cached copies.
|
||||
|
||||
## See also
|
||||
|
||||
[`docs/architecture.md`](architecture.md), [`../../MODULE_CATEGORIES.md`](../../MODULE_CATEGORIES.md).
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# Architecture: hyper-p2p-peer-bootstrap-store
|
||||
|
||||
**Category:** network-discovery
|
||||
**Protocol:** `peer-bootstrap-store/v1` · **Category:** network-discovery
|
||||
|
||||
## Wire messages
|
||||
|
||||
| type | fields | direction | behavior |
|
||||
|------|--------|-----------|----------|
|
||||
| `bootstrap-add` | `peerId`, `hints`, `from`, `at` | gossip | LWW upsert per `peerId` |
|
||||
| type | direction | fields | behavior |
|
||||
|------|-----------|--------|----------|
|
||||
| `bootstrap-add` | gossip | `peerId`, `hints`, `from`, `at` | LWW merge; emit `remote-add` |
|
||||
|
||||
## State model
|
||||
|
||||
- `_bootstraps`: `Map<peerId, entry>`
|
||||
`_bootstraps`: Map peerId → entry.
|
||||
|
||||
## Composition
|
||||
|
||||
Replicates dial hints across a gossip mesh topic.
|
||||
`hyper-p2p-dht-bootstrap-hint`, `hyper-bare-bundle-bridge`.
|
||||
|
||||
@@ -1,39 +1,86 @@
|
||||
# hyper-p2p-seeder-registry
|
||||
|
||||
Gossip which peers seed which logical topics.
|
||||
Register and lookup seeders per logical `topicId` over gossip on a shared swarm topic.
|
||||
|
||||
**Category:** network-discovery
|
||||
|
||||
**Composes with:** hyper-p2p-gossip-mesh
|
||||
|
||||
**Protocol:** `seeder-registry/v1`
|
||||
**Category:** network-discovery · **Protocol:** `seeder-registry/v1` · **Exports:** `HyperP2PSeederRegistry`, `PROTOCOL`
|
||||
|
||||
## When to use
|
||||
|
||||
Content or topic meshes where downloaders need to find seeders for a topic id on a shared swarm.
|
||||
Content or topic seeding: many peers announce who seeds which `topicId`.
|
||||
|
||||
## When not to use
|
||||
|
||||
Single-seeder deployments with no peer discovery requirement.
|
||||
Single fixed seeder with no mesh discovery.
|
||||
|
||||
## Quick start
|
||||
|
||||
```js
|
||||
const { HyperP2PSeederRegistry } = require('hyper-p2p-seeder-registry')
|
||||
const mod = new HyperP2PSeederRegistry({ topic: process.argv[2] })
|
||||
await mod.ready()
|
||||
mod.registerSeeder('topic-1', mod.peerHex, { slots: 8 })
|
||||
console.log(mod.lookup('topic-1'))
|
||||
await mod.close()
|
||||
const reg = new HyperP2PSeederRegistry({ topic: 'discover' })
|
||||
await reg.ready()
|
||||
reg.registerSeeder('my-feed', reg.peerHex, { role: 'primary' })
|
||||
console.log(reg.lookup('my-feed'))
|
||||
await reg.close()
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
- [docs/api.md](docs/api.md) — constructor, methods, events, errors
|
||||
- [docs/architecture.md](docs/architecture.md) — wire types, state, composition
|
||||
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
npm test
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| Member | Description |
|
||||
|--------|-------------|
|
||||
| `new HyperP2PSeederRegistry(opts?)` | `topic`, `keyPair`; `peerHex` local id. |
|
||||
| `registerSeeder(topicId, peerId, meta?)` | Upsert in topic bucket + gossip. |
|
||||
| `lookup(topicId)` | All seeders for topic. |
|
||||
| `listSeeders()` | Flat list across topics. |
|
||||
| `getStats()` | `{ registered, gossipIn, gossipOut, topics, seeders, protocol }`. |
|
||||
| `ready()` / `close()` | Swarm lifecycle. |
|
||||
|
||||
**Events:** `register`, `remote-register`, `closed`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
_byTopic: Map<topicId, Map<peerId, entry>>
|
||||
registerSeeder --> bucket[topicId][peerId] --> seeder-register gossip --> LWW on at
|
||||
```
|
||||
|
||||
Same `peerId` under one `topicId` is one slot; newer `at` replaces metadata.
|
||||
|
||||
## Wire table
|
||||
|
||||
| `type` | Fields | Direction |
|
||||
|--------|--------|-----------|
|
||||
| `seeder-register` | `topicId`, `peerId`, `meta`, `from`, `at` | Any → all |
|
||||
|
||||
## Errors
|
||||
|
||||
- Empty `topicId` or `peerId` on `registerSeeder` / `lookup`.
|
||||
- `lookup` returns `[]` when topic unknown (not an error).
|
||||
|
||||
## Composition
|
||||
|
||||
- Align `topicId` with `hyper-p2p-topic-announcer` announcements.
|
||||
- Use `hyper-p2p-capability-discovery` tag `seed` for capability-based lookup.
|
||||
- Same swarm `topic` as other discovery modules for one mesh view.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const { HyperP2PSeederRegistry } = require('hyper-p2p-seeder-registry')
|
||||
|
||||
const reg = new HyperP2PSeederRegistry({ topic: process.argv[2] })
|
||||
await reg.ready()
|
||||
|
||||
reg.registerSeeder('my-feed', reg.peerHex, { role: 'primary' })
|
||||
console.log(reg.lookup('my-feed'))
|
||||
await reg.close()
|
||||
```
|
||||
|
||||
@@ -1,37 +1,90 @@
|
||||
# API: hyper-p2p-seeder-registry
|
||||
|
||||
**Protocol:** `seeder-registry/v1`
|
||||
**Protocol:** `seeder-registry/v1` · **Export:** `HyperP2PSeederRegistry`, `PROTOCOL`
|
||||
|
||||
**Export:** `HyperP2PSeederRegistry`
|
||||
## Overview
|
||||
|
||||
Registers seeders per `topicId` with metadata and gossips registrations. Lookup returns all seeders known for a topic; LWW per `(topicId, peerId)` on merge.
|
||||
|
||||
## Constructor
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `topic` | `string` \| `Buffer` | `null` | Swarm topic |
|
||||
| `keyPair` | `KeyPair` | random | `peerHex` |
|
||||
|
||||
## Methods
|
||||
|
||||
### `registerSeeder(topicId, peerId, meta = {})`
|
||||
### `registerSeeder(topicId, peerId, meta?)`
|
||||
|
||||
- **Returns:** `{ topicId, peerId, meta, from, at }`
|
||||
- **Returns:** `{ topicId, peerId, meta, from: peerHex, at }`
|
||||
- **Throws:** `assertNonEmpty` on `topicId`, `peerId`
|
||||
- **Gossip:** `{ type: 'seeder-register', topicId, peerId, meta, from, at }`
|
||||
- **Emits:** `register`
|
||||
|
||||
### `lookup(topicId)`
|
||||
|
||||
- **Returns:** seeders for that topic
|
||||
- **Returns:** array of seeder entries
|
||||
|
||||
### `listSeeders()`
|
||||
|
||||
- **Returns:** all seeder entries across topics
|
||||
- **Returns:** flat array across all topics
|
||||
|
||||
## Properties
|
||||
### `ready()` / `close()`
|
||||
|
||||
- `peerHex` — local public key hex (from `keyPair`)
|
||||
Clears `_byTopic` on close.
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Payload |
|
||||
|-------|---------|
|
||||
| `register` | local entry |
|
||||
| `remote-register` | remote entry |
|
||||
| `remote-register` | merged entry |
|
||||
| `closed` | — |
|
||||
|
||||
## getStats()
|
||||
|
||||
`registered`, `gossipIn`, `gossipOut`, `topics`, `seeders`, `protocol`.
|
||||
|
||||
## Wire
|
||||
|
||||
| type | fields | behavior |
|
||||
|------|--------|----------|
|
||||
| `seeder-register` | `topicId`, `peerId`, `meta`, `from`, `at` | LWW per peer in topic bucket |
|
||||
|
||||
## Errors
|
||||
|
||||
`assertNonEmpty` on ids.
|
||||
|
||||
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
|
||||
|
||||
## P2P
|
||||
|
||||
Internal `_topicBucket(topicId)` → `Map<peerId, entry>`.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
cd modules/network-discovery/hyper-p2p-seeder-registry && npm test
|
||||
```
|
||||
|
||||
## Composition
|
||||
|
||||
`hyper-p2p-topic-announcer`, `hyper-p2p-capability-discovery`, `hyper-p2p-discovery-health`.
|
||||
|
||||
## Example
|
||||
|
||||
See [`examples/basic.js`](../examples/basic.js).
|
||||
|
||||
## Remote merge rules
|
||||
|
||||
- `seeder-register` LWW per `(topicId, peerId)` on `at`
|
||||
- `from` falls back to gossip sender key hex
|
||||
|
||||
## Lifecycle
|
||||
|
||||
`listSeeders()` flattens all topic buckets for export/debug.
|
||||
|
||||
## See also
|
||||
|
||||
[`docs/architecture.md`](architecture.md), [`../../MODULE_CATEGORIES.md`](../../MODULE_CATEGORIES.md).
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# Architecture: hyper-p2p-seeder-registry
|
||||
|
||||
**Category:** network-discovery
|
||||
**Protocol:** `seeder-registry/v1` · **Category:** network-discovery
|
||||
|
||||
## Wire messages
|
||||
|
||||
| type | fields | direction | behavior |
|
||||
|------|--------|-----------|----------|
|
||||
| `seeder-register` | `topicId`, `peerId`, `meta`, `from`, `at` | gossip | Upsert seeder per topic + peer |
|
||||
| type | direction | fields | behavior |
|
||||
|------|-----------|--------|----------|
|
||||
| `seeder-register` | gossip | `topicId`, `peerId`, `meta`, `from`, `at` | LWW into `_byTopic[topicId][peerId]`; emit `remote-register` |
|
||||
|
||||
## State model
|
||||
|
||||
- `_byTopic`: `Map<topicId, Map<peerId, entry>>`
|
||||
`_byTopic`: Map topicId → Map peerId → entry.
|
||||
|
||||
## Composition
|
||||
|
||||
Seeder directory for topic-based content meshes.
|
||||
`hyper-p2p-topic-announcer`, `hyper-p2p-capability-discovery`.
|
||||
|
||||
@@ -1,41 +1,89 @@
|
||||
# hyper-p2p-topic-announcer
|
||||
|
||||
Gossip topic announcements over a shared Hyperswarm topic.
|
||||
Announce and revoke logical topics (with metadata) on a gossip mesh; track remote announcements.
|
||||
|
||||
**Category:** network-discovery
|
||||
|
||||
**Composes with:** hyper-p2p-presence
|
||||
|
||||
**Protocol:** `topic-announcer/v1`
|
||||
**Category:** network-discovery · **Protocol:** `topic-announcer/v1` · **Exports:** `HyperP2PTopicAnnouncer`, `PROTOCOL`
|
||||
|
||||
## When to use
|
||||
|
||||
Apps that publish which logical topics they host and need peers to discover those announcements on a shared swarm topic.
|
||||
Peers advertise interest in named `topicId`s (channels, feeds) without joining each topic’s swarm yet.
|
||||
|
||||
## When not to use
|
||||
|
||||
Static topic lists baked into config, or apps with no P2P topic (skip `ready()` and use local-only announce/revoke).
|
||||
When topic membership is implicit from DHT only.
|
||||
|
||||
## Quick start
|
||||
|
||||
```js
|
||||
const { HyperP2PTopicAnnouncer } = require('hyper-p2p-topic-announcer')
|
||||
const topic = process.argv[2]
|
||||
const mod = new HyperP2PTopicAnnouncer({ topic })
|
||||
await mod.ready()
|
||||
mod.announce('my-app-v1', { region: 'eu' })
|
||||
console.log(mod.list())
|
||||
await mod.close()
|
||||
const ann = new HyperP2PTopicAnnouncer({ topic: 'discover' })
|
||||
await ann.ready()
|
||||
ann.announce('channel-1', { priority: 1 })
|
||||
console.log(ann.list())
|
||||
await ann.close()
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
- [docs/api.md](docs/api.md) — constructor, methods, events, errors
|
||||
- [docs/architecture.md](docs/architecture.md) — wire types, state, composition
|
||||
- [../../_shared/PRODUCTION.md](../../_shared/PRODUCTION.md) — production checklist
|
||||
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
npm test
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| Member | Description |
|
||||
|--------|-------------|
|
||||
| `new HyperP2PTopicAnnouncer(opts?)` | `topic` (swarm), `keyPair`; `peerHex`. |
|
||||
| `announce(topicId, meta?)` | Set local + gossip `announce`. |
|
||||
| `revoke(topicId)` | Delete if held; gossip `revoke`. Returns boolean. |
|
||||
| `list()` / `get(topicId)` | All entries / one. |
|
||||
| `getStats()` | `{ announced, revoked, gossipIn, gossipOut, topics, protocol }`. |
|
||||
| `ready()` / `close()` | Swarm lifecycle. |
|
||||
|
||||
**Events:** `announce`, `revoke`, `remote-announce`, `remote-revoke`, `closed`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
_topics Map(topicId -> { topicId, meta, peer, at })
|
||||
announce: LWW by at; revoke: delete if cur.peer matches revoker peer (or no cur)
|
||||
```
|
||||
|
||||
Revoke only removes when the revoking `peer` matches the announcer (or entry absent).
|
||||
|
||||
## Wire table
|
||||
|
||||
| `type` | Fields | Direction |
|
||||
|--------|--------|-----------|
|
||||
| `announce` | `topicId`, `meta`, `peer`, `at` | Any → all |
|
||||
| `revoke` | `topicId`, `peer`, `at` | Any → all |
|
||||
|
||||
## Errors
|
||||
|
||||
- Empty `topicId` on `announce`, `revoke`, `get`.
|
||||
- `revoke` returns `false` when topic was not locally announced.
|
||||
|
||||
## Composition
|
||||
|
||||
- `topicId` is logical; constructor `topic` is the Hyperswarm discovery topic.
|
||||
- Downstream: join per-topic swarms using announced ids + `hyper-p2p-seeder-registry`.
|
||||
- Listen to `remote-announce` to build subscription UI or routing tables.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const { HyperP2PTopicAnnouncer } = require('hyper-p2p-topic-announcer')
|
||||
|
||||
const ann = new HyperP2PTopicAnnouncer({ topic: process.argv[2] })
|
||||
ann.on('remote-announce', (e) => console.log('seen', e.topicId))
|
||||
|
||||
await ann.ready()
|
||||
ann.announce('channel-1', { priority: 1 })
|
||||
console.log(ann.list())
|
||||
ann.revoke('channel-1')
|
||||
await ann.close()
|
||||
```
|
||||
|
||||
@@ -1,59 +1,83 @@
|
||||
# API: hyper-p2p-topic-announcer
|
||||
|
||||
**Protocol:** `topic-announcer/v1`
|
||||
**Protocol:** `topic-announcer/v1` · **Export:** `HyperP2PTopicAnnouncer`, `PROTOCOL`
|
||||
|
||||
**Export:** `HyperP2PTopicAnnouncer`
|
||||
## Overview
|
||||
|
||||
Logical topic announcements over a gossip mesh: peers publish interest in named `topicId` values with metadata before joining dedicated swarms.
|
||||
|
||||
## Constructor
|
||||
|
||||
```js
|
||||
const mod = new HyperP2PTopicAnnouncer(opts)
|
||||
```
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `topic` | string \| Buffer | null | Hyperswarm join topic |
|
||||
| `keyPair` | KeyPair | random | Ed25519 identity |
|
||||
| `topic` | `string` \| `Buffer` | `null` | Hyperswarm mesh for announcer gossip |
|
||||
| `keyPair` | `KeyPair` | random | Local peer identity (`peerHex`) |
|
||||
|
||||
## Methods
|
||||
|
||||
### `announce(topicId, meta = {})`
|
||||
### `announce(topicId, meta?)`
|
||||
|
||||
Registers or updates a local announcement. Gossips when swarm is active.
|
||||
Registers or updates announcement (LWW by `at` on merge).
|
||||
|
||||
- **Returns:** entry `{ topicId, meta, peer, at }`
|
||||
- **Throws:** `ValidationError` if `topicId` is empty
|
||||
- **Returns:** `{ topicId, meta, peer, at }`
|
||||
- **Throws:** `assertNonEmpty` on `topicId`
|
||||
- **Gossip:** `{ type: 'announce', topicId, meta, peer, at }`
|
||||
|
||||
### `revoke(topicId)`
|
||||
|
||||
Removes a local announcement and gossips revoke.
|
||||
Deletes local entry and gossips revoke if held.
|
||||
|
||||
- **Returns:** `boolean` — whether an entry existed
|
||||
- **Returns:** `boolean`
|
||||
- **Gossip:** `{ type: 'revoke', topicId, peer, at }`
|
||||
|
||||
### `list()`
|
||||
### `list()` / `get(topicId)`
|
||||
|
||||
- **Returns:** `Array<entry>` — all known announcements
|
||||
Snapshot of all announcements or single lookup.
|
||||
|
||||
### `get(topicId)`
|
||||
### `ready()` / `close()`
|
||||
|
||||
- **Returns:** entry or `null`
|
||||
`initModuleSwarm` when `topic` set. `close` clears map and destroys swarm.
|
||||
|
||||
### `getStats()` / `ready()` / `close()`
|
||||
### `getStats()`
|
||||
|
||||
See architecture doc.
|
||||
`announced`, `revoked`, `gossipIn`, `gossipOut`, `topics`, `protocol`.
|
||||
|
||||
## Remote merge rules
|
||||
|
||||
- `announce`: accept if newer `at`
|
||||
- `revoke`: delete if revoking `peer` matches holder or no holder
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Payload |
|
||||
|-------|---------|
|
||||
| `announce` | local entry |
|
||||
| `revoke` | `{ topicId, peer }` |
|
||||
| `remote-announce` | entry |
|
||||
| `remote-revoke` | `{ topicId, peer }` |
|
||||
| `closed` | — |
|
||||
`announce`, `revoke`, `remote-announce`, `remote-revoke`, `closed`
|
||||
|
||||
## Wire
|
||||
|
||||
| type | fields |
|
||||
|------|--------|
|
||||
| `announce` | `topicId`, `meta`, `peer`, `at` |
|
||||
| `revoke` | `topicId`, `peer`, `at` |
|
||||
|
||||
## Errors
|
||||
|
||||
Empty `topicId` rejected via `assertNonEmpty`.
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
npm install && npm test
|
||||
```
|
||||
`npm test` — local announce/revoke and simulated gossip.
|
||||
|
||||
## P2P notes
|
||||
|
||||
Without `topic`, API is local-only; `ready()` is no-op for swarm.
|
||||
|
||||
## Integration
|
||||
|
||||
Compose with `hyper-p2p-capability-discovery` and `hyper-p2p-seeder-registry` for layered discovery.
|
||||
|
||||
## Example
|
||||
|
||||
See `examples/basic.js`.
|
||||
|
||||
## See also
|
||||
|
||||
[`docs/architecture.md`](architecture.md), [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
# Architecture: hyper-p2p-topic-announcer
|
||||
|
||||
**Category:** network-discovery
|
||||
**Protocol:** `topic-announcer/v1`
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
App[Application] --> Mod[HyperP2PTopicAnnouncer]
|
||||
Mod --> Mux[Protomux topic-announcer/v1]
|
||||
Mux --> Swarm[Hyperswarm]
|
||||
App --> Ann[TopicAnnouncer]
|
||||
Ann --> Gossip[p2p-bare]
|
||||
Gossip --> Peers
|
||||
```
|
||||
|
||||
## Wire messages
|
||||
|
||||
| type | fields | direction | behavior |
|
||||
|------|--------|-----------|----------|
|
||||
| `announce` | `topicId`, `meta`, `peer`, `at` | gossip | Upsert if `at` is newer |
|
||||
| `revoke` | `topicId`, `peer`, `at` | gossip | Delete if peer matches holder |
|
||||
| type | direction | fields | behavior |
|
||||
|------|-----------|--------|----------|
|
||||
| `announce` | gossip | `topicId`, `meta`, `peer`, `at` | LWW merge into `_topics` |
|
||||
| `revoke` | gossip | `topicId`, `peer`, `at` | Delete if peer matches holder |
|
||||
|
||||
## State model
|
||||
## State
|
||||
|
||||
- `_topics`: `Map<topicId, entry>`
|
||||
- LWW merge on `at` for remote announces
|
||||
- `close()` clears map and destroys swarm
|
||||
`_topics`: Map topicId → entry. Stats track announce/revoke counts.
|
||||
|
||||
## Composition
|
||||
|
||||
Uses `../../_shared/p2p-bare.js` (`initModuleSwarm`, `gossipSend`). Composes with hyper-p2p-presence for peer identity context.
|
||||
`hyper-p2p-dht-bootstrap-hint`, `hyper-p2p-topic-channel`.
|
||||
|
||||
Reference in New Issue
Block a user