This commit is contained in:
Raven Scott
2026-05-20 23:36:32 -04:00
parent a020270cb1
commit be94546cd3
218 changed files with 9189 additions and 3078 deletions
@@ -2,79 +2,84 @@
**Protocol:** `qos-topic/v1`
**Export:** `HyperP2PQosTopic`
**Export:** `HyperP2PQosTopic`, `PROTOCOL`
## Overview
Production p2p module: Hyperswarm discovery + Protomux when `topic` is set.
Priority pub/sub with three QoS queues (02). Higher QoS messages drain before lower; subscribers declare minimum QoS per channel. Publishes gossip as `qos-publish`.
## Constructor
```js
const mod = new HyperP2PQosTopic(opts)
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | varies | null | topic |
| `keyPair` | KeyPair | random Ed25519 | keyPair |
| Option | Description |
|--------|-------------|
| `topic` | Hyperswarm topic |
| `keyPair` | Publisher identity |
## Methods
### `subscribe(channel, handler, qos = 0)`
### `subscribe(channel, handler, qos?)`
- **Returns:** `value`
- **Throws:**
- `Error: handler must be a function`
Default subscriber QoS `0`. Handler receives messages where `msg.qos >= sub.qos`.
### `publish(channel, payload, opts = {})`
### `publish(channel, payload, opts?)`
- **Returns:** `value`
- **Throws:** — (none documented in method body)
- **opts.qos** — clamped 02
- Enqueues then `_drain()` priority order
### `pending(qos)`
### `pending(qos?)`
- **Returns:** `value`
- **Throws:** — (none documented in method body)
Queue depth for one level or total.
### `getStats()`
### `getStats()` / `ready()` / `close()`
- **Returns:** `object`
- **Throws:** — (none documented in method body)
`published`, `delivered`, `gossipIn`, `gossipOut`.
### `ready(—)`
## Wire
- **Returns:** `Promise`
- **Throws:** — (none documented in method body)
### `close(—)`
- **Returns:** `Promise<void>`
- **Throws:** — (none documented in method body)
| type | fields |
|------|--------|
| `qos-publish` | `channel`, `payload`, `qos`, `from`, `at` |
## Events
| Event | Payload |
|-------|---------|
| `message` | msg |
## getStats()
Returns `{ ...this._stats }` — typically `ops`, `errors`, and module-specific counters (`created`, `relays`, `open`, `peers`, etc.).
Library-only modules may include `mode: 'local'`.
`message`.
## Errors
Stable message substrings: see [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
`assertNonEmpty(channel)`, invalid handler.
## P2P
## Composition
When `topic` is set, `ready()` joins Hyperswarm and opens Protomux `qos-topic/v1`.
`hyper-p2p-topic-channel` for retain; Wave 9 `qos-topic-two-node.js`.
## Testing
```bash
npm install && npm test
```
`npm test`, integration smoke.
Integration: [`../../../real_tests/integration/qos-topic-two-node.js`](../../../real_tests/integration/qos-topic-two-node.js)
## Example
`examples/basic.js`
## State model
Three queues `_queues[0..2]`; drain processes highest index first.
## Performance
`pending()` walks all queues; bound publish rate if memory grows.
## Versioning
`qos-topic/v1` publish type unchanged since Wave 9.
## Security
No encryption; use `hyper-p2p-encrypted-topic` for sensitive channels.
## Related modules
- `hyper-p2p-topic-channel` — retain and subscribe
## See also
[`docs/architecture.md`](architecture.md)
@@ -1,83 +1,83 @@
# API: hyper-p2p-retained-messages
**Protocol:** `retained-messages/v1`
**Protocol:** `retained-messages/v1` (local store)
**Export:** `HyperP2PRetainedMessages`
**Export:** `HyperP2PRetainedMessages`, `PROTOCOL`
## Overview
Production p2p module: Hyperswarm discovery + Protomux when `topic` is set.
Per-channel FIFO of retained payloads (default max 32 per channel). Used standalone or behind `hyper-p2p-topic-channel` retain flag.
## Constructor
```js
const mod = new HyperP2PRetainedMessages(opts)
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | `string` | `Buffer` | `null` | Hyperswarm topic; required for P2P `ready()` |
| `keyPair` | KeyPair | random | Ed25519 key pair |
| `maxPerChannel` | number | 32 | maxPerChannel |
| Option | Default | Description |
|--------|---------|-------------|
| `maxPerChannel` | `32` | Ring size per channel |
## Methods
### `retain(channel, payload, meta = {})`
### `retain(channel, payload, meta?)`
- **Returns:** `value`
- **Throws:** — (none documented in method body)
Appends entry `{ payload, meta, at }`; emits `retain`.
### `latest(channel)`
### `latest(channel)` / `list(channel, limit?)`
- **Returns:** `value`
- **Throws:** — (none documented in method body)
### `clear(channel?)`
### `list(channel, limit = 10)`
Omit channel to wipe all.
- **Returns:** `value`
- **Throws:** — (none documented in method body)
### `getStats()` / `ready()` / `close()`
### `clear(channel)`
`set`, `get`, `cleared`, `channels`, `protocol`.
- **Returns:** `value`
- **Throws:** — (none documented in method body)
## Wire
### `getStats(—)`
- **Returns:** `object`
- **Throws:** — (none documented in method body)
### `ready(—)`
- **Returns:** `Promise`
- **Throws:** — (none documented in method body)
### `close(—)`
- **Returns:** `Promise<void>`
- **Throws:** — (none documented in method body)
None in-module; topic-channel gossips `retained-sync`.
## Events
| Event | Payload |
|-------|---------|
| `retain` | payload object |
## getStats()
Returns `{ ...this._stats }` — typically `ops`, `errors`, and module-specific counters (`created`, `relays`, `open`, `peers`, etc.).
Library-only modules may include `mode: 'local'`.
`retain`.
## Errors
Stable message substrings: see [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
`assertNonEmpty(channel)`.
## P2P
## Composition
When `topic` is set, `ready()` joins Hyperswarm and opens Protomux `retained-messages/v1`.
Pair with `hyper-p2p-topic-channel.publish(..., { retain: true })`.
## Testing
```bash
npm install && npm test
```
`npm test`
## Example
`examples/basic.js`
## State model
Per-channel arrays capped by `maxPerChannel`; `latest` is O(1).
## Performance
`list` copies tail slice only; safe for UI replay buffers.
## Versioning
Local store; sync via topic-channel gossip separately.
## Security
Retained payloads persist in memory until `clear`; scrub secrets on channel delete.
## Related modules
- `hyper-p2p-topic-channel` — networked retain sync
## Changelog
Wave 9: standalone retain store extracted for composition and tests.
## See also
[`docs/architecture.md`](architecture.md)
@@ -2,85 +2,79 @@
**Protocol:** `subscription-lease/v1`
**Export:** `HyperP2PSubscriptionLease`
**Export:** `HyperP2PSubscriptionLease`, `PROTOCOL`
## Overview
Production p2p module: Hyperswarm discovery + Protomux when `topic` is set.
Time-bounded exclusive channel subscription leases. `acquire` fails if another holders lease is unexpired; background timer sweeps expired entries when enabled.
## Constructor
```js
const mod = new HyperP2PSubscriptionLease(opts)
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | varies | null | topic |
| `keyPair` | KeyPair | random Ed25519 | keyPair |
| `leaseMs` | varies | DEFAULT_LEASE_MS | lease (ms) |
| `enableBackgroundTimers` | boolean | `false` | Periodic timers (off in tests) |
| Option | Default | Description |
|--------|---------|-------------|
| `leaseMs` | `60000` | Lease duration |
| `enableBackgroundTimers` | `false` | Periodic expiry sweep |
## Methods
### `acquire(channel)`
### `acquire(channel)` / `renew(channel)` / `release(channel)`
- **Returns:** `value`
- **Throws:** — (none documented in method body)
### `getLease(channel)` / `isActive(channel)`
### `renew(channel)`
### `getStats()` / `ready()` / `close()`
- **Returns:** `value`
- **Throws:** — (none documented in method body)
## Wire
### `release(channel)`
- **Returns:** `value`
- **Throws:** — (none documented in method body)
### `holder(channel)`
- **Returns:** `value`
- **Throws:** — (none documented in method body)
### `getStats(—)`
- **Returns:** `object`
- **Throws:** — (none documented in method body)
### `ready(—)`
- **Returns:** `Promise`
- **Throws:** — (none documented in method body)
### `close(—)`
- **Returns:** `Promise<void>`
- **Throws:** — (none documented in method body)
| type | purpose |
|------|---------|
| `sub-lease` | full lease object |
| `sub-release` | `channel`, `holder` |
## Events
| Event | Payload |
|-------|---------|
| `acquire` | lease |
| `closed` | no payload |
| `expired` | channel |
## getStats()
Returns `{ ...this._stats }` — typically `ops`, `errors`, and module-specific counters (`created`, `relays`, `open`, `peers`, etc.).
Library-only modules may include `mode: 'local'`.
`acquire`, `release`, `expired`.
## Errors
Stable message substrings: see [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
`assertNonEmpty(channel)`; acquire conflict returns `{ ok: false, holder }`.
## P2P
## Composition
When `topic` is set, `ready()` joins Hyperswarm and opens Protomux `subscription-lease/v1`.
`hyper-p2p-topic-channel` subscribe exclusivity.
## Testing
```bash
npm install && npm test
```
`npm test`
## Example
`examples/basic.js`
## State model
`_leases` map channel → `{ holder, expiresAt }`; timer sweeps expired when enabled.
## Performance
Acquire is O(1); gossip on every renew — throttle renew frequency.
## Versioning
`subscription-lease/v1` wire messages documented in architecture.
## Security
Leases are cooperative, not Byzantine-safe; use consensus modules for hard exclusivity.
## Related modules
- `hyper-p2p-topic-channel` — delivery path
## Changelog
- Wave 9: lease acquire/renew/release with optional background expiry timer.
- Default `leaseMs` is 60000 unless overridden in constructor.
## See also
[`docs/architecture.md`](architecture.md)
@@ -2,90 +2,90 @@
**Protocol:** `topic-channel/v1`
**Export:** `HyperP2PTopicChannel`
**Export:** `HyperP2PTopicChannel`, `PROTOCOL`
## Overview
Production p2p module: Hyperswarm discovery + Protomux when `topic` is set.
MQTT-style named channels over one Hyperswarm topic: subscribe handlers, publish with optional QoS level and retained last message per channel, plus gossip for subscribe/unsubscribe/publish/retained-sync.
## Constructor
```js
const mod = new HyperP2PTopicChannel(opts)
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | varies | null | topic |
| `keyPair` | KeyPair | random Ed25519 | keyPair |
| Option | Default | Description |
|--------|---------|-------------|
| `topic` | `null` | Shared swarm topic |
| `keyPair` | random | Publisher `peerHex` |
| `retainMessages` | `true` | Default retain on publish |
## Methods
### `subscribe(channel, handler)`
- **Returns:** `value`
- **Throws:**
- `Error: handler must be a function`
- **Returns:** unsubscribe function
- **Throws:** empty channel, `handler must be a function`
- **Side effect:** gossips `subscribe`; replays retained payload if present
### `unsubscribe(channel)`
- **Returns:** `value`
- **Throws:** — (none documented in method body)
### `publish(channel, payload, opts?)`
### `publish(channel, payload, opts = {})`
- **opts.qos** — number tag on message
- **opts.retain** — store last message for channel
- **Returns:** gossip message object
- **Emits:** `message` on all subscribers
- **Returns:** `value`
- **Throws:** — (none documented in method body)
### `getRetained(channel)` / `syncRetained(channel)`
### `getRetained(channel)`
Push retained snapshot to peers (`retained-sync`).
- **Returns:** `value`
- **Throws:** — (none documented in method body)
### `getStats()`
### `syncRetained(channel)`
`published`, `received`, `subscriptions`, `gossipIn`, `gossipOut`, `channels`, `retained`, `protocol`.
- **Returns:** `value`
- **Throws:** — (none documented in method body)
### `ready()` / `close()`
### `getStats(—)`
Protomux `topic-channel/v1`; `close` emits `closed`.
- **Returns:** `object`
- **Throws:** — (none documented in method body)
## Wire messages
### `ready(—)`
- **Returns:** `Promise`
- **Throws:** — (none documented in method body)
### `close(—)`
- **Returns:** `Promise<void>`
- **Throws:** — (none documented in method body)
| type | purpose |
|------|---------|
| `subscribe` | `channel`, `peer` |
| `unsubscribe` | `channel`, `peer` |
| `publish` | `channel`, `payload`, `from`, `at`, `qos`, `retain` |
| `retained-sync` | last retained payload |
## Events
| Event | Payload |
|-------|---------|
| `closed` | no payload |
| `message` | payload object |
## getStats()
Returns `{ ...this._stats }` — typically `ops`, `errors`, and module-specific counters (`created`, `relays`, `open`, `peers`, etc.).
Library-only modules may include `mode: 'local'`.
`message`, `closed`.
## Errors
Stable message substrings: see [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
`assertNonEmpty(channel)`.
## P2P
## Composition
When `topic` is set, `ready()` joins Hyperswarm and opens Protomux `topic-channel/v1`.
`hyper-p2p-qos-topic`, `hyper-p2p-subscription-lease`, `hyper-p2p-retained-messages`, Wave 9 stack.
## Testing
```bash
npm install && npm test
```
`npm test`, `topic-channel-two-node.js`, `wave9-stack-two-node.js`.
Integration: [`../../../real_tests/integration/topic-channel-two-node.js`](../../../real_tests/integration/topic-channel-two-node.js)
## Example
`examples/basic.js`
## State model
`_subs` maps channel → handler; `_retained` maps channel → `{ payload, from, at }`. Stats counters increment on publish/delivery/gossip paths only.
## Performance
Retained replay is O(1) per subscribe. Gossip fan-out is O(peers) via Protomux; keep payloads small.
## Versioning
Protocol string `topic-channel/v1` is stable; bump only with breaking wire shape changes.
## See also
[`docs/architecture.md`](architecture.md)