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
@@ -1,33 +1,92 @@
# hyper-p2p-blind-pair-handoff
Production module: Blind pair handoff.
Three-phase blind session handoff state machine (offer → accept → complete) synced over gossip.
**Protocol:** `blind-pair-handoff/v1`
**Category:** trust-security · **Protocol:** `blind-pair-handoff/v1` · **Exports:** `HyperP2PBlindPairHandoff`, `PROTOCOL`
## When to use
Peer session migration.
Coordinate moving a session to `targetPeer` without exposing pair details in this module.
## When not to use
Direct connect only.
Instant local pairing only (`secret-stream-pair`).
## Quick start
```js
const { HyperP2PBlindPairHandoff } = require('hyper-p2p-blind-pair-handoff')
const m = new HyperP2PBlindPairHandoff()
await m.ready()
await m.close()
const h = new HyperP2PBlindPairHandoff({ topic: 'handoff' })
await h.ready()
h.offerHandoff('sess-1', 'target-peer-hex')
h.acceptHandoff('sess-1')
h.completeHandoff('sess-1')
await h.close()
```
## Docs
- [docs/api.md](docs/api.md)
- [docs/architecture.md](docs/architecture.md)
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
## Test
```bash
npm install && npm test
npm test
```
## API
| Member | Description |
|--------|-------------|
| `new HyperP2PBlindPairHandoff(opts?)` | `topic`, `keyPair`. |
| `offerHandoff(sessionId, targetPeer)` | State `offered`; gossips `handoff-offer`. |
| `acceptHandoff(sessionId)` | Requires `offered`; state `accepted`. |
| `completeHandoff(sessionId)` | Requires `accepted`; state `completed`. |
| `getHandoff(sessionId)` | Full record or `null`. |
| `getStats()` | `{ offered, accepted, completed, gossipIn, gossipOut, handoffs, protocol }`. |
| `ready()` / `close()` | Swarm lifecycle. |
**Events:** `offered`, `accepted`, `completed`, `closed`. Throws if wrong state or missing handoff.
## Architecture
```
_handoffs Map(sessionId -> { sessionId, targetPeer, state, offeredAt, acceptedAt, completedAt })
Remote: offer creates if missing; accept/complete advance only from valid prior state
```
Gossip does not emit events on remote—only updates map (listen via polling `getHandoff` or extend locally).
## Wire table
| `type` | Fields | Direction |
|--------|--------|-----------|
| `handoff-offer` | `sessionId`, `targetPeer`, `offeredAt` | Any → all |
| `handoff-accept` | `sessionId`, `acceptedAt` | Any → all |
| `handoff-complete` | `sessionId`, `completedAt` | Any → all |
## Errors
- `acceptHandoff` / `completeHandoff`: missing id → `handoff not found`; wrong state → `handoff not offered` / `not accepted`.
- Empty `sessionId` / `targetPeer` on offer.
## Composition
- `sessionId` aligns with `hyper-p2p-noise-session-wrap` ids.
- `targetPeer` is hex or app-defined peer string.
- Use after `hyper-p2p-blind-relay-bridge` selects a relay path.
## Example
```js
const { HyperP2PBlindPairHandoff } = require('hyper-p2p-blind-pair-handoff')
const h = new HyperP2PBlindPairHandoff({ topic: process.argv[2] })
await h.ready()
h.offerHandoff('sess-1', 'target-peer-hex')
h.acceptHandoff('sess-1')
h.completeHandoff('sess-1')
console.log(h.getHandoff('sess-1'))
await h.close()
```
@@ -1,26 +1,97 @@
# API: hyper-p2p-blind-pair-handoff
**Protocol:** `blind-pair-handoff/v1` · **Export:** `HyperP2PBlindPairHandoff`
**Protocol:** `blind-pair-handoff/v1` · **Export:** `HyperP2PBlindPairHandoff`, `PROTOCOL`
## Overview
Coordinates blind session handoffs through offered → accepted → completed states. Gossips state transitions so peers observe handoff progress without exposing full pairing material in the API surface.
## Constructor
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | `string` \| `Buffer` | `null` | Swarm topic |
| `keyPair` | `KeyPair` | random | Local identity |
## Methods
### `offerHandoff(...)`
### `offerHandoff(sessionId, targetPeer)`
Domain API.
- **Returns:** handoff `{ sessionId, targetPeer, state: 'offered', offeredAt, acceptedAt: null, completedAt: null }`
- **Throws:** `assertNonEmpty` on `sessionId`, `targetPeer`
- **Gossip:** `{ type: 'handoff-offer', sessionId, targetPeer, offeredAt }`
- **Emits:** `offered`
### `acceptHandoff(...)`
### `acceptHandoff(sessionId)`
Domain API.
- **Throws:** `handoff not found: ${sessionId}`; `handoff not offered: ${sessionId}`
- **Gossip:** `{ type: 'handoff-accept', sessionId, acceptedAt }`
- **Emits:** `accepted`
### `completeHandoff(...)`
### `completeHandoff(sessionId)`
Domain API.
- **Throws:** `handoff not found: ${sessionId}`; `handoff not accepted: ${sessionId}`
- **Gossip:** `{ type: 'handoff-complete', sessionId, completedAt }`
- **Emits:** `completed`
### `getHandoff(sessionId)`
### `getStats()` / `ready()` / `close()`
- **Returns:** handoff or `null`
Lifecycle helpers.
### `ready()` / `close()`
Standard swarm lifecycle.
## Events
| Event | Payload |
|-------|---------|
| `offered` | handoff object |
| `accepted` | handoff object |
| `completed` | handoff object |
| `closed` | — |
## getStats()
| Field | Meaning |
|-------|---------|
| `offered` / `accepted` / `completed` | transition counts |
| `gossipIn` / `gossipOut` | mesh |
| `handoffs` | map size |
| `protocol` | `blind-pair-handoff/v1` |
## Wire
| type | fields | behavior |
|------|--------|----------|
| `handoff-offer` | `sessionId`, `targetPeer`, `offeredAt` | Create offered entry if missing |
| `handoff-accept` | `sessionId`, `acceptedAt` | Promote offered → accepted |
| `handoff-complete` | `sessionId`, `completedAt` | Promote accepted → completed |
## Errors
State machine errors listed above. Empty ids via `assertNonEmpty`.
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
## P2P
Joins Hyperswarm when `topic` is set.
Remote handlers in `_onGossip` merge offers and advance state when local state allows.
## Testing
```bash
cd modules/trust-security/hyper-p2p-blind-pair-handoff && npm test
```
## Composition
`hyper-p2p-blind-relay-bridge`, `hyper-p2p-secret-stream-pair`, `hyper-p2p-noise-session-wrap`.
## Example
See [`examples/basic.js`](../examples/basic.js).
## See also
[`docs/architecture.md`](architecture.md).
@@ -1,13 +1,26 @@
# Architecture: hyper-p2p-blind-pair-handoff
**Protocol:** `blind-pair-handoff/v1` · **Category:** trust-security
```mermaid
stateDiagram-v2
[*] --> offered
offered --> accepted
accepted --> completed
```
## Wire messages
| type | fields | direction | behavior |
|------|--------|-----------|----------|
| `handoff-offer` | sessionId, targetPeer | gossip | Offer |
| `handoff-accept` | sessionId | gossip | Accept |
| `handoff-complete` | sessionId | gossip | Done |
| type | direction | fields | behavior |
|------|-----------|--------|----------|
| `handoff-offer` | gossip | `sessionId`, `targetPeer`, `offeredAt` | Insert if `sessionId` unknown |
| `handoff-accept` | gossip | `sessionId`, `acceptedAt` | Set state accepted when offered |
| `handoff-complete` | gossip | `sessionId`, `completedAt` | Set state completed when accepted |
## State
## State model
In-memory structures; gossip via `initModuleSwarm` / `gossipSend` when P2P enabled.
`_handoffs`: Map sessionId → handoff record with `state` enum.
## Composition
`hyper-p2p-blind-relay-bridge`, `hyper-p2p-secret-stream-pair`.
@@ -1,34 +1,92 @@
# hyper-p2p-encrypted-topic
Production module: Topic encryption demo.
Register topics with a `keyHint`, derive a symmetric key, XOR-encrypt payloads; gossip topic registration (hint only).
**Protocol:** `encrypted-topic/v1`
**Category:** trust-security · **Protocol:** `encrypted-topic/v1` · **Exports:** `HyperP2PEncryptedTopic`, `PROTOCOL`, `deriveKey`, `xorCrypt`
## When to use
Topic-scoped payload crypto.
Lightweight obfuscation keyed by shared hint on a mesh (not a substitute for Noise).
## When not to use
Production AEAD.
Production confidentiality without real AEAD/Noise.
## Quick start
```js
const { HyperP2PEncryptedTopic } = require('hyper-p2p-encrypted-topic')
const m = new HyperP2PEncryptedTopic()
m.registerTopic('t','hint')
await m.ready()
await m.close()
const enc = new HyperP2PEncryptedTopic({ topic: 'secure' })
await enc.ready()
enc.registerTopic('private-feed', 'shared-secret-hint')
const cipher = enc.encryptPayload('private-feed', 'hello')
console.log(enc.decryptPayload('private-feed', cipher).toString())
await enc.close()
```
## Docs
- [docs/api.md](docs/api.md)
- [docs/architecture.md](docs/architecture.md)
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
## Test
```bash
npm install && npm test
npm test
```
## API
| Member | Description |
|--------|-------------|
| `new HyperP2PEncryptedTopic(opts?)` | `topic`, `keyPair`. |
| `registerTopic(topicId, keyHint)` | `key = hash(keyHint)`; gossips `topic-register`. |
| `encryptPayload(topicId, data)` | Buffer or encodable; returns cipher buffer. |
| `decryptPayload(topicId, buf)` | XOR decrypt; requires registered topic. |
| `deriveKey(hint)` / `xorCrypt(keyBuf, dataBuf)` | Low-level helpers (exported). |
| `getStats()` | `{ registered, encrypted, decrypted, gossipIn, gossipOut, topics, protocol }`. |
| `ready()` / `close()` | Swarm lifecycle. |
**Events:** `registered`, `closed`. Remote register fills map if `topicId` new.
## Architecture
```
_topics Map(topicId -> { keyHint, key, registeredAt })
deriveKey: hypercore-crypto.hash(b4a.from(String(hint)))
xorCrypt: repeating-key XOR byte-wise
```
Peers must use same `keyHint` for a `topicId` to decrypt. Gossip shares hint, not key bytes.
## Wire table
| `type` | Fields | Direction |
|--------|--------|-----------|
| `topic-register` | `topicId`, `keyHint` | Any → all |
## Errors
- Unregistered `topicId` on encrypt/decrypt throws `topic not registered`.
- `decryptPayload`: non-buffer input throws.
- Empty `topicId` / `keyHint` on register.
## Composition
- XOR is not AEAD—layer Noise or app crypto for real secrecy.
- Same `keyHint` across peers required; rotate via `hyper-p2p-key-rotation` on hint strings.
- Gossip does not re-broadcast ciphertext—only registration.
## Example
```js
const { HyperP2PEncryptedTopic } = require('hyper-p2p-encrypted-topic')
const enc = new HyperP2PEncryptedTopic({ topic: process.argv[2] })
await enc.ready()
enc.registerTopic('private-feed', 'shared-secret-hint')
const cipher = enc.encryptPayload('private-feed', 'hello')
const plain = enc.decryptPayload('private-feed', cipher)
console.log(plain.toString())
await enc.close()
```
@@ -1,26 +1,91 @@
# API: hyper-p2p-encrypted-topic
**Protocol:** `encrypted-topic/v1` · **Export:** `HyperP2PEncryptedTopic`
**Protocol:** `encrypted-topic/v1` · **Export:** `HyperP2PEncryptedTopic`, `PROTOCOL`, `deriveKey`, `xorCrypt`
## Overview
Registers logical topics with a `keyHint`, derives a symmetric key via `hypercore-crypto.hash`, and XOR-encrypts payloads. Gossips `topic-register` so peers learn hints (not raw keys — key re-derived locally).
## Constructor
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | `string` \| `Buffer` | `null` | Swarm topic |
| `keyPair` | `KeyPair` | random | Swarm identity |
## Methods
### `registerTopic(...)`
### `registerTopic(topicId, keyHint)`
Domain API.
- **Returns:** `{ topicId, keyHint, key, registeredAt }`
- **Throws:** `assertNonEmpty` on both args
- **Gossip:** `{ type: 'topic-register', topicId, keyHint }`
- **Emits:** `registered` `{ topicId, keyHint }`
### `encryptPayload(...)`
### `encryptPayload(topicId, data)`
Domain API.
- **Returns:** Buffer cipher
- **Throws:** `topic not registered: ${topicId}`
### `decryptPayload(...)`
### `decryptPayload(topicId, buf)`
Domain API.
- **Returns:** plain Buffer
- **Throws:** `buf must be a buffer`; `topic not registered: ${topicId}`
### `ready()` / `close()`
### `getStats()` / `ready()` / `close()`
Standard swarm.
Lifecycle helpers.
## Events
| Event | Payload |
|-------|---------|
| `registered` | `{ topicId, keyHint }` |
| `closed` | — |
## getStats()
`registered`, `encrypted`, `decrypted`, `gossipIn`, `gossipOut`, `topics`, `protocol`.
## Wire
| type | fields | behavior |
|------|--------|----------|
| `topic-register` | `topicId`, `keyHint` | Insert topic if unknown; derive key |
## Errors
Registration and buffer errors above. Uses `assertNonEmpty` from shared errors.
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
## P2P
Joins Hyperswarm when `topic` is set.
XOR is not authenticated encryption — use for obfuscation/lightweight topic privacy only.
## Testing
```bash
cd modules/trust-security/hyper-p2p-encrypted-topic && npm test
```
## Composition
`hyper-p2p-topic-announcer`, `hyper-p2p-key-rotation`, `hyper-p2p-session-rotation`.
## Example
See [`examples/basic.js`](../examples/basic.js).
## Remote merge rules
- `topic-register` only inserts when `topicId` not already local
- Remote peers re-derive `key` from gossiped `keyHint` (never send raw key)
## Lifecycle
Encrypt/decrypt increment `encrypted` / `decrypted` stats per operation.
## See also
[`docs/architecture.md`](architecture.md), [`../../MODULE_CATEGORIES.md`](../../MODULE_CATEGORIES.md).
@@ -1,11 +1,22 @@
# Architecture: hyper-p2p-encrypted-topic
**Protocol:** `encrypted-topic/v1` · **Category:** trust-security
## Wire messages
| type | fields | direction | behavior |
|------|--------|-----------|----------|
| `topic-register` | topicId, keyHint | gossip | Register |
| type | direction | fields | behavior |
|------|-----------|--------|----------|
| `topic-register` | gossip | `topicId`, `keyHint` | Store topic; `deriveKey(keyHint)` locally |
## State
## State model
In-memory structures; gossip via `initModuleSwarm` / `gossipSend` when P2P enabled.
`_topics`: Map topicId → `{ topicId, keyHint, key, registeredAt }`.
## Helpers
- `deriveKey(hint)``crypto.hash(b4a.from(String(hint)))`
- `xorCrypt(keyBuf, dataBuf)` — byte XOR stream cipher
## Composition
`hyper-p2p-key-rotation`, `hyper-p2p-topic-announcer`.
+66 -11
View File
@@ -1,34 +1,89 @@
# hyper-p2p-key-rotation
Production module: Key rotation scheduling.
Schedule and activate key material rotations by `keyId` with gossip sync and time-based activation.
**Protocol:** `key-rotation/v1`
**Category:** trust-security · **Protocol:** `key-rotation/v1` · **Exports:** `HyperP2PKeyRotation`, `PROTOCOL`
## When to use
Rotating keys on a timeline.
Rotate logical keys on a clock across peers (`activateAt`).
## When not to use
Static keys only.
Hypercore key rotation built into core replication.
## Quick start
```js
const { HyperP2PKeyRotation } = require('hyper-p2p-key-rotation')
const m = new HyperP2PKeyRotation()
m.scheduleRotation('k', 'mat', Date.now())
await m.ready()
await m.close()
const rot = new HyperP2PKeyRotation({ topic: 'keys' })
await rot.ready()
rot.scheduleRotation('app-key', 'material-v2', Date.now() + 3600_000)
console.log(rot.activeKey('app-key'))
await rot.close()
```
## Docs
- [docs/api.md](docs/api.md)
- [docs/architecture.md](docs/architecture.md)
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
## Test
```bash
npm install && npm test
npm test
```
## API
| Member | Description |
|--------|-------------|
| `new HyperP2PKeyRotation(opts?)` | `topic`, `keyPair`. |
| `scheduleRotation(keyId, newMaterial, activateAt?)` | Default `activateAt = now`; may activate immediately. |
| `activeKey(keyId)` | `{ keyId, material, activatedAt }` or `null` (runs pending check). |
| `pendingRotations()` | Future `{ keyId, newMaterial, activateAt, scheduledAt }[]`. |
| `getStats()` | `{ scheduled, activated, gossipIn, gossipOut, activeKeys, pending, protocol }`. |
| `ready()` / `close()` | Swarm lifecycle. |
**Events:** `scheduled`, `activated`, `closed`.
## Architecture
```
_pending Map(keyId -> [entries])
_active Map(keyId -> { material, activatedAt })
_maybeActivate: all entries with activateAt <= now move to active + gossip rotation-activated
```
`newMaterial` is opaque string (app-defined encoding).
## Wire table
| `type` | Fields | Direction |
|--------|--------|-----------|
| `rotation-scheduled` | `keyId`, `newMaterial`, `activateAt` | Any → all |
| `rotation-activated` | `keyId`, `material`, `activatedAt` | Any → all |
## Errors
- Empty `keyId` or `newMaterial` on schedule / `activeKey`.
- Pending list only includes future `activateAt` entries.
## Composition
- Store serialized keys in `newMaterial` (JSON string, hex, etc.).
- Call `activeKey` before encrypt ops in `hyper-p2p-encrypted-topic`.
- Immediate activation when `activateAt <= now` on schedule.
## Example
```js
const { HyperP2PKeyRotation } = require('hyper-p2p-key-rotation')
const rot = new HyperP2PKeyRotation({ topic: process.argv[2] })
await rot.ready()
rot.scheduleRotation('app-key', 'material-v2', Date.now() + 3600_000)
console.log(rot.activeKey('app-key'))
console.log(rot.pendingRotations())
await rot.close()
```
@@ -1,26 +1,93 @@
# API: hyper-p2p-key-rotation
**Protocol:** `key-rotation/v1` · **Export:** `HyperP2PKeyRotation`
**Protocol:** `key-rotation/v1` · **Export:** `HyperP2PKeyRotation`, `PROTOCOL`
## Overview
Schedules key material rotations with optional `activateAt` timestamp and activates when due. Gossips schedule and activation events for mesh-wide key state.
## Constructor
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | `string` \| `Buffer` | `null` | Swarm topic |
| `keyPair` | `KeyPair` | random | Identity |
## Methods
### `scheduleRotation(...)`
### `scheduleRotation(keyId, newMaterial, activateAt?)`
Domain API.
- **Returns:** `{ keyId, newMaterial, activateAt, scheduledAt }`
- **Throws:** `assertNonEmpty` on `keyId`, `newMaterial`
- **Gossip:** `{ type: 'rotation-scheduled', keyId, newMaterial, activateAt }`
- **Emits:** `scheduled`
- **Side effect:** `_maybeActivate(keyId)` immediately
### `activeKey(...)`
### `activeKey(keyId)`
Domain API.
- **Returns:** `{ keyId, material, activatedAt }` or `null`
- Runs `_maybeActivate` before read
### `pendingRotations(...)`
### `pendingRotations()`
Domain API.
- **Returns:** array of future rotations (`activateAt > now`)
### `ready()` / `close()`
### `getStats()` / `ready()` / `close()`
Standard.
Lifecycle helpers.
## Events
| Event | Payload |
|-------|---------|
| `scheduled` | schedule entry |
| `activated` | active key record |
| `closed` | — |
## getStats()
`scheduled`, `activated`, `gossipIn`, `gossipOut`, `activeKeys`, `pending`, `protocol`.
## Wire
| type | fields | behavior |
|------|--------|----------|
| `rotation-scheduled` | `keyId`, `newMaterial`, `activateAt` | Append pending; maybe activate |
| `rotation-activated` | `keyId`, `material`, `activatedAt` | Set `_active` |
## Errors
`assertNonEmpty` on key ids.
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
## P2P
Joins Hyperswarm when `topic` is set.
Activation is time-based (`activateAt <= Date.now()`).
## Testing
```bash
cd modules/trust-security/hyper-p2p-key-rotation && npm test
```
## Composition
`hyper-p2p-encrypted-topic`, `hyper-p2p-session-rotation`, `hyper-p2p-noise-session-wrap`.
## Example
See [`examples/basic.js`](../examples/basic.js).
## Remote merge rules
- `rotation-scheduled` appends to pending list then `_maybeActivate`
- `rotation-activated` overwrites `_active` for `keyId`
## Lifecycle
Call `activeKey` or `scheduleRotation` to trigger time-based activation checks.
## See also
[`docs/architecture.md`](architecture.md), [`../../MODULE_CATEGORIES.md`](../../MODULE_CATEGORIES.md).
@@ -1,12 +1,19 @@
# Architecture: hyper-p2p-key-rotation
**Protocol:** `key-rotation/v1` · **Category:** trust-security
## Wire messages
| type | fields | direction | behavior |
|------|--------|-----------|----------|
| `rotation-scheduled` | keyId, newMaterial, activateAt | gossip | Schedule |
| `rotation-activated` | keyId, material, activatedAt | gossip | Activate |
| type | direction | fields | behavior |
|------|-----------|--------|----------|
| `rotation-scheduled` | gossip | `keyId`, `newMaterial`, `activateAt` | Queue pending; `_maybeActivate` |
| `rotation-activated` | gossip | `keyId`, `material`, `activatedAt` | Write `_active` map |
## State
## State model
In-memory structures; gossip via `initModuleSwarm` / `gossipSend` when P2P enabled.
- `_active`: Map keyId → active material
- `_pending`: Map keyId → array of scheduled entries
## Composition
`hyper-p2p-encrypted-topic`, `hyper-p2p-session-rotation`.
@@ -1,33 +1,91 @@
# hyper-p2p-multisig-threshold
Production module: Threshold multisig.
Threshold approval proposals: create with signers list, collect signatures, approve when count ≥ threshold.
**Protocol:** `multisig-threshold/v1`
**Category:** trust-security · **Protocol:** `multisig-threshold/v1` · **Exports:** `HyperP2PMultisigThreshold`, `PROTOCOL`
## When to use
M-of-N approvals.
Gossip-aligned multisig-style gates (ids, signer ids as strings).
## When not to use
Single signer.
On-chain multisig or Ed25519 aggregate signatures (signatures are presence flags here).
## Quick start
```js
const { HyperP2PMultisigThreshold } = require('hyper-p2p-multisig-threshold')
const m = new HyperP2PMultisigThreshold()
await m.ready()
await m.close()
const ms = new HyperP2PMultisigThreshold({ topic: 'gov' })
await ms.ready()
ms.createProposal('deploy-1', ['alice', 'bob', 'carol'], 2)
ms.addSignature('deploy-1', 'alice')
console.log(ms.isApproved('deploy-1'))
await ms.close()
```
## Docs
- [docs/api.md](docs/api.md)
- [docs/architecture.md](docs/architecture.md)
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
## Test
```bash
npm install && npm test
npm test
```
## API
| Member | Description |
|--------|-------------|
| `new HyperP2PMultisigThreshold(opts?)` | `topic`, `keyPair`. |
| `createProposal(id, signers, threshold)` | `1 ≤ threshold ≤ signers.length`; gossips `proposal-create`. |
| `addSignature(id, signer)` | Signer must be in list; returns `{ id, signer, count, approved }`. |
| `isApproved(id)` | Boolean. |
| `getStats()` | `{ created, signatures, approved, gossipIn, gossipOut, proposals, protocol }`. |
| `ready()` / `close()` | Swarm lifecycle. |
**Events:** `proposal`, `approved`, `closed`.
## Architecture
```
_proposals Map(id -> { signers[], threshold, signatures Set, approved })
Remote sigs add to Set; approved when size >= threshold
```
No cryptographic verify of `signer` string—authorization is membership in `signers`.
## Wire table
| `type` | Fields | Direction |
|--------|--------|-----------|
| `proposal-create` | `id`, `signers`, `threshold`, `createdAt` | Any → all |
| `proposal-sig` | `id`, `signer` | Any → all |
## Errors
- Invalid `signers` array or `threshold` on `createProposal`.
- Unknown proposal or unauthorized `signer` on `addSignature`.
## Composition
- Signer ids often match `peerHex` from discovery modules.
- Gate deploys with `isApproved` before `hyper-pear-update-gossip` publish.
- Duplicate signatures from same signer are idempotent (Set).
## Example
```js
const { HyperP2PMultisigThreshold } = require('hyper-p2p-multisig-threshold')
const ms = new HyperP2PMultisigThreshold({ topic: process.argv[2] })
ms.on('approved', ({ id }) => console.log('approved', id))
await ms.ready()
ms.createProposal('deploy-1', ['alice', 'bob', 'carol'], 2)
ms.addSignature('deploy-1', 'alice')
ms.addSignature('deploy-1', 'bob')
console.log(ms.isApproved('deploy-1'))
await ms.close()
```
@@ -1,26 +1,85 @@
# API: hyper-p2p-multisig-threshold
**Protocol:** `multisig-threshold/v1` · **Export:** `HyperP2PMultisigThreshold`
**Protocol:** `multisig-threshold/v1` · **Export:** `HyperP2PMultisigThreshold`, `PROTOCOL`
## Overview
Threshold signature coordination for proposals: create with signer list and threshold, collect signatures until `threshold` met, gossip create and sig messages.
## Constructor
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | `string` \| `Buffer` | `null` | Swarm topic |
| `keyPair` | `KeyPair` | random | Identity |
## Methods
### `createProposal(...)`
### `createProposal(id, signers, threshold)`
Domain API.
- **Returns:** `{ id, signers, threshold }`
- **Throws:** `signers must be a non-empty array`; `threshold must be between 1 and signers.length`
- **Gossip:** `{ type: 'proposal-create', id, signers, threshold, createdAt }`
- **Emits:** `proposal`
### `addSignature(...)`
### `addSignature(id, signer)`
Domain API.
- **Returns:** `{ id, signer, count, approved }`
- **Throws:** `proposal not found: ${id}`; `signer not authorized: ${signer}`
- **Gossip:** `{ type: 'proposal-sig', id, signer }`
- **Emits:** `approved` when threshold first reached
### `isApproved(...)`
### `isApproved(id)`
Domain API.
- **Returns:** boolean
### `ready()` / `close()`
### `getStats()` / `ready()` / `close()`
Standard.
Lifecycle helpers.
## Events
| Event | Payload |
|-------|---------|
| `proposal` | `{ id, signers, threshold }` |
| `approved` | `{ id }` |
| `closed` | — |
## getStats()
`created`, `signatures`, `approved`, `gossipIn`, `gossipOut`, `proposals`, `protocol`.
## Wire
| type | fields | behavior |
|------|--------|----------|
| `proposal-create` | `id`, `signers`, `threshold`, `createdAt` | Insert proposal if missing |
| `proposal-sig` | `id`, `signer` | Add sig if signer authorized; maybe approve |
## Errors
Validation strings above; `assertNonEmpty` on ids.
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
## P2P
Joins Hyperswarm when `topic` is set.
Signatures stored in `Set` per proposal (not deduplicated across duplicate gossip).
## Testing
```bash
cd modules/trust-security/hyper-p2p-multisig-threshold && npm test
```
## Composition
`hyper-p2p-key-rotation`, agent workflows requiring quorum.
## Example
See [`examples/basic.js`](../examples/basic.js).
## See also
[`docs/architecture.md`](architecture.md).
@@ -1,12 +1,18 @@
# Architecture: hyper-p2p-multisig-threshold
**Protocol:** `multisig-threshold/v1` · **Category:** trust-security
## Wire messages
| type | fields | direction | behavior |
|------|--------|-----------|----------|
| `proposal-create` | id, signers, threshold | gossip | Create |
| `proposal-sig` | id, signer | gossip | Sign |
| type | direction | fields | behavior |
|------|-----------|--------|----------|
| `proposal-create` | gossip | `id`, `signers`, `threshold`, `createdAt` | Create `_proposals` entry |
| `proposal-sig` | gossip | `id`, `signer` | Add to `signatures` Set; set `approved` at threshold |
## State
## State model
In-memory structures; gossip via `initModuleSwarm` / `gossipSend` when P2P enabled.
`_proposals`: Map id → `{ id, signers, threshold, signatures: Set, createdAt, approved }`.
## Composition
`hyper-p2p-key-rotation`, `hyper-p2p-workflow-graph`.
@@ -1,43 +1,103 @@
# hyper-p2p-reputation-system
HyperP2PReputationSystem Novel P2P reputation/trust primitive for Bare/Pear. - Cryptographic Ed25519 signed attestations for tamper-proof updates - Time-decaying scores with configurable rate
Ed25519-signed attestations, decaying scores, optional persistence, and gossip for P2P trust on Bare/Pear.
**Category:** Trust & security
**Composes with:** `hyper-p2p-attestation-chain`, `hyper-p2p-trust-graph`
**Protocol:** `hyper-p2p-reputation-system/v1`
**Category:** trust-security · **Protocol:** `hyper-p2p-reputation-system/v1` · **Export:** `HyperP2PReputationSystem` (default), `REPUTATION_PROTOCOL`
## When to use
Multi-peer apps that need trust & security over a shared Hyperswarm topic.
Peer trust scores with tamper-evident deltas and optional `topic` gossip.
## When not to use
Single-process tools with no P2P topic (use local APIs only or skip `ready()`).
Simple allowlists without attestations or history.
## Quick start
```js
const { HyperP2PReputationSystem } = require('hyper-p2p-reputation-system')
const topic = process.argv[2] // 64-char hex or string
const mod = new HyperP2PReputationSystem({ topic, enableBackgroundTimers: false })
await mod.ready() // joins swarm when topic set
// ... application logic ...
await mod.close()
const HyperP2PReputationSystem = require('hyper-p2p-reputation-system')
const rep = new HyperP2PReputationSystem({ topic: 'trust', enableBackgroundTimers: false })
await rep.ready()
await rep.attest('peer-abc', 10, { reason: 'good relay' })
console.log(rep.getReputation('peer-abc'))
await rep.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
- [../_shared/DOC_STANDARDS.md](../../_shared/DOC_STANDARDS.md) — documentation standards
- Integration: [`../../real_tests/integration/`](../../../real_tests/integration/) — `reputation-system-two-node.js`
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
## Test
```bash
npm install && npm test
npm test
```
## API
| Member | Description |
|--------|-------------|
| `new HyperP2PReputationSystem(opts?)` | `keyPair`, `topic`, `storageDir`, `decayIntervalMs`, `decayRate`, `minScore`, `maxScore`, `useHyperbee`, `enableBackgroundTimers`. |
| `ready()` | Storage init, swarm if `topic`, decay timer if enabled. Emits `ready`. |
| `attest(targetPeerId, delta, metadata?)` | Sign, apply locally, gossip `{ type:'attestation' }`. |
| `receiveAttestation(attestation, targetPeerId)` | Verify + apply; false on failure. |
| `getReputation(peerId)` | `{ score, lastUpdated, attestationsCount }`. |
| `getTopPeers(k?)` | Sorted by score (default 10). |
| `getHistory(peerId, limit?)` | Last N attestation records (max 100 stored). |
| `exportSnapshot()` / `importSnapshot(snapshot)` | Portable state v1.x. |
| `getCausalTick()` | Monotonic local counter for hooks. |
| `simulateRemoteAttestation(...)` | Test helper (non-crypto sig). |
| `getStats()` | `{ ops, errors }`. |
| `close()` | Stop timers, destroy swarm/core, persist. |
**Events:** `ready`, `attestation`, `scoreUpdated`, `peerBanned`, `decay` via timer, `snapshotImported`, `hyperbee-fallback`, `error`, `close`.
## Architecture
```
scores Map(peerHex -> { score, lastUpdated, attestationsCount })
history Map(peerHex -> [{ ts, delta, attester, signature, metadata }])
_verifyAttestation: Ed25519 verify, nonce dedup, 7-day TTL
Persistence: state.json or optional Hyperbee on storageDir
Decay: multiplicative per interval when enableBackgroundTimers
```
Gossip applies remote attestations via `_applyAttestation` on `attestation` messages.
## Wire table
| `type` | Fields | Direction |
|--------|--------|-----------|
| `attestation` | `target` (hex), `attestation` `{ payload, signature, nonce }` | Any → all |
Payload JSON (base64): `{ target, delta, attester, nonce, ts, metadata }`.
## Errors
- `importSnapshot`: invalid object or unsupported `version`.
- `receiveAttestation`: returns `false` on verify/TTL/replay failure.
- Replay nonces increment `metrics.sybilAttemptsBlocked`.
## Composition
- Ban hook: `peerBanned` when decayed score ≤ `minScore`.
- Optional Hyperbee under `storageDir/reputation-core`.
- `getCausalTick()` for event-bus / vector-clock integration.
## Example
```js
const HyperP2PReputationSystem = require('hyper-p2p-reputation-system')
const rep = new HyperP2PReputationSystem({
topic: process.argv[2],
enableBackgroundTimers: false,
storageDir: './rep-storage'
})
await rep.ready()
await rep.attest('peer-abc', 10, { reason: 'good relay' })
console.log(rep.getReputation('peer-abc'))
console.log(rep.getTopPeers(5))
await rep.close()
```
@@ -0,0 +1,11 @@
require('bare-process/global')
const { HyperP2PReputationSystem } = require('../index.js')
async function main () {
const rep = new HyperP2PReputationSystem()
await rep.ready()
rep.attest('peer-b', 10, { reason: 'helpful' })
console.log('[reputation]', rep.getReputation('peer-b'), rep.getStats())
await rep.close()
}
main().catch(console.error)
@@ -1,34 +1,87 @@
# hyper-p2p-session-rotation
Production module: Session token rotation.
Rotate session tokens with monotonic `version`; gossip so peers keep highest version per `sessionId`.
**Protocol:** `session-rotation/v1`
**Category:** trust-security · **Protocol:** `session-rotation/v1` · **Exports:** `HyperP2PSessionRotation`, `PROTOCOL`
## When to use
Refreshing session tokens.
Invalidate old session tokens across a mesh after compromise or TTL.
## When not to use
Immutable sessions.
Stateless sessions with no shared rotation ledger.
## Quick start
```js
const { HyperP2PSessionRotation } = require('hyper-p2p-session-rotation')
const m = new HyperP2PSessionRotation()
m.rotateSession('s', 'tok')
await m.ready()
await m.close()
const rot = new HyperP2PSessionRotation({ topic: 'sessions' })
await rot.ready()
rot.rotateSession('user-sess', 'token-v2')
console.log(rot.getSession('user-sess'))
await rot.close()
```
## Docs
- [docs/api.md](docs/api.md)
- [docs/architecture.md](docs/architecture.md)
- [docs/api.md](docs/api.md) · [docs/architecture.md](docs/architecture.md)
## Test
```bash
npm install && npm test
npm test
```
## API
| Member | Description |
|--------|-------------|
| `new HyperP2PSessionRotation(opts?)` | `topic`, `keyPair`. |
| `rotateSession(sessionId, newToken)` | Bump `version`, gossip `session-rotate`. |
| `getSession(sessionId)` | `{ sessionId, token, version, rotatedAt }` or `null`. |
| `listSessions()` | All session records. |
| `getStats()` | `{ rotations, gossipIn, gossipOut, sessions, protocol }`. |
| `ready()` / `close()` | Swarm lifecycle. |
**Events:** `rotated`, `closed`. Remote merge accepts when `d.version >= prev.version`.
## Architecture
```
_sessions Map(sessionId -> { token, version, rotatedAt })
Each rotateSession increments version from previous or 0
```
Token is opaque string; no crypto in this module.
## Wire table
| `type` | Fields | Direction |
|--------|--------|-----------|
| `session-rotate` | `sessionId`, `token`, `version`, `rotatedAt` | Any → all |
## Errors
- Empty `sessionId` or `newToken` on `rotateSession` / `getSession`.
- Stale remote rotations ignored when incoming `version` is lower than local.
## Composition
- Validate `token` on inbound connections after rotation.
- Works with `hyper-p2p-noise-session-wrap` session ids.
- `listSessions()` for admin/debug dashboards.
## Example
```js
const { HyperP2PSessionRotation } = require('hyper-p2p-session-rotation')
const rot = new HyperP2PSessionRotation({ topic: process.argv[2] })
await rot.ready()
rot.rotateSession('user-sess', 'token-v1')
rot.rotateSession('user-sess', 'token-v2')
console.log(rot.getSession('user-sess'))
await rot.close()
```
@@ -1,26 +1,88 @@
# API: hyper-p2p-session-rotation
**Protocol:** `session-rotation/v1` · **Export:** `HyperP2PSessionRotation`
**Protocol:** `session-rotation/v1` · **Export:** `HyperP2PSessionRotation`, `PROTOCOL`
## Overview
Rotates session tokens with monotonic `version` per `sessionId`. Gossips rotations; remote merges when `version >=` local.
## Constructor
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `topic` | `string` \| `Buffer` | `null` | Swarm topic |
| `keyPair` | `KeyPair` | random | Identity |
## Methods
### `rotateSession(...)`
### `rotateSession(sessionId, newToken)`
Domain API.
- **Returns:** `{ sessionId, token, version, rotatedAt }`
- **Throws:** `assertNonEmpty` on both args
- **Gossip:** `{ type: 'session-rotate', sessionId, token, version, rotatedAt }`
- **Emits:** `rotated`
### `getSession(...)`
### `getSession(sessionId)`
Domain API.
- **Returns:** session or `null`
### `listSessions(...)`
### `listSessions()`
Domain API.
- **Returns:** all session values
### `ready()` / `close()`
### `getStats()` / `ready()` / `close()`
Standard.
Lifecycle helpers.
## Events
| Event | Payload |
|-------|---------|
| `rotated` | session record |
| `closed` | — |
## getStats()
`rotations`, `gossipIn`, `gossipOut`, `sessions`, `protocol`.
## Wire
| type | fields | behavior |
|------|--------|----------|
| `session-rotate` | `sessionId`, `token`, `version`, `rotatedAt` | Replace if newer version |
## Errors
`assertNonEmpty` on session id and token.
See [`../../_shared/ERROR_CODES.md`](../../_shared/ERROR_CODES.md).
## P2P
Joins Hyperswarm when `topic` is set.
Version starts at 1 on first rotation for a session.
## Testing
```bash
cd modules/trust-security/hyper-p2p-session-rotation && npm test
```
## Composition
`hyper-pear-runtime-session`, `hyper-p2p-noise-session-wrap`, `hyper-p2p-key-rotation`.
## Example
See [`examples/basic.js`](../examples/basic.js).
## Remote merge rules
- `session-rotate` applied when `!prev || d.version >= prev.version`
## Lifecycle
`version` increments on each local `rotateSession` call.
## See also
[`docs/architecture.md`](architecture.md), [`../../MODULE_CATEGORIES.md`](../../MODULE_CATEGORIES.md).
@@ -1,11 +1,17 @@
# Architecture: hyper-p2p-session-rotation
**Protocol:** `session-rotation/v1` · **Category:** trust-security
## Wire messages
| type | fields | direction | behavior |
|------|--------|-----------|----------|
| `session-rotate` | sessionId, token, version | gossip | Rotate |
| type | direction | fields | behavior |
|------|-----------|--------|----------|
| `session-rotate` | gossip | `sessionId`, `token`, `version`, `rotatedAt` | LWW by `version` |
## State
## State model
In-memory structures; gossip via `initModuleSwarm` / `gossipSend` when P2P enabled.
`_sessions`: Map sessionId → `{ sessionId, token, version, rotatedAt }`.
## Composition
`hyper-pear-runtime-session`, `hyper-p2p-noise-session-wrap`.