Manthan Gossip Demo (Phase 3 — The Void Network)
Phase 3 deliverable per the Manthan platform tracker §3 (P-004 + P-005a). Three Manthan nodes communicate over real WebSocket connections using the MWP handshake (G-005) and ENVELOPE_GOSSIP frames (G-006).
What this demonstrates
1. Three independent nodes (Alice, Bob, Carol), each running:
- Its own SQLite ledger (G-010 schema)
- Its own MWP server on an ephemeral port
- Outbound MWP client connections to one peer (forming a ring)
2. Three WebSocket connections / six local MWP session objects (one inbound + one outbound at each node) established via the three-message handshake (HELO → HELO_ACK → HELO_ACK_2) with mutual transcript signatures.
3. Controlled gossip propagation: Alice and Bob originate envelopes; neighbors relay them across the local mesh.
4. Dedupe: when an envelope reaches a node from multiple paths, only the first ingestion is recorded and duplicate frames are counted.
5. Persistence: every received envelope lands in the receiver's SQLite ledger in seen state.
Run
cd demo/manthan-gossip
node --experimental-strip-types src/gossip.tsSample output
=== Manthan Gossip Demo (Phase 3 — The Void Network) ===
Alice sigil:...
Bob sigil:...
Carol sigil:...
Ports: Alice=51595 Bob=51596 Carol=51597
[Alice] session established (outgoing→Bob)
[Bob] session established (incoming)
[Bob] session established (outgoing→Carol)
[Carol] session established (incoming)
[Carol] session established (outgoing→Alice)
[Alice] session established (incoming)
--- gossip start ---
[Alice] originating envelope id=691bf0b09822… to=Bob
[Bob] received envelope id=691bf0b09822… from Alice
[Carol] received envelope id=691bf0b09822… from Alice
[Bob] originating envelope id=f0f8cd394925… to=Carol
[Alice] received envelope id=f0f8cd394925… from Bob
[Carol] received envelope id=f0f8cd394925… from Bob
=== ledger state ===
Alice seen:1 verified:1 unique-ids-seen=2 duplicate-frames-dropped=...
Bob seen:1 verified:1 unique-ids-seen=2 duplicate-frames-dropped=...
Carol seen:2 verified:0 unique-ids-seen=2 duplicate-frames-dropped=...
dedupe verified: dropped ... duplicate frame(s)
shutting down…The final assertions confirm each node tracked exactly two distinct envelopes, and the duplicate-frame counter confirms dedupe was actually exercised.
What the MWP handshake does
Per G-005, each pair-wise session is established by:
1. HELO — initiator sends version, sigil, fresh nonce, supported envelope/artifact/role types, max frame size.
2. HELO_ACK — responder replies with its own sigil, fresh nonce, the intersection of supported sets, plus transcript_signature (Ed25519 over CBOR-canonical(HELO || HELO_ACK_core)).
3. HELO_ACK_2 — initiator counter-signs the same transcript and emits.
4. Both sides derive session_id = SHA-256(transcript)[0..16].
5. Subsequent frames carry session_id + monotonic seq per direction; non-monotonic seq closes the session immediately.
This defends against:
- Replay — fresh nonces produce unique transcripts; replayed HELO_ACK fails on
deriveSessionId(transcript) !== ack.session_id. - Downgrade — version is in the signed transcript; tampering breaks the signature.
- Splicing — both peers sign the same transcript; HELO from session A + HELO_ACK from session B fails verification on both sides.
- Forgery — HELO_ACK signed with the wrong key fails Ed25519 verification (test:
mwp server rejects HELO_ACK_2 signed by wrong key). - MITM — when TLS exporter (RFC 5705) is available, both peers include it; mismatch is rejected at HELO_ACK time.
Test coverage
lib/manthan-mwp/test/handshake.test.ts— 10/10 tests covering V1 (healthy), V2 (forged), V3 (replay), V4 (downgrade), V5 (splicing), V6 (TLS exporter mismatch), V7 (unsupported version), V8 (signer identity binding), V9 (TLS exporter stripping), and intersection logic.lib/manthan-mwp/test/integration.test.ts— 3/3 end-to-end tests on real WebSocket connections.
What this is NOT
- Not a production gossip network. No bootstrap discovery, no peer rotation, no anti-eclipse measures (those land in Phase 4 per G-006 §3.1–3.3).
- Not encrypted. Frames are JSON over plain WebSocket. Production deployments use
wss://over TLS; the handshake'stls_exporterfield binds MWP to the TLS session. - Not authoritative gossip. Receivers ingest envelopes in
seenstate; full verification + state-machine progression is the receiver's job (Phase 2 ledger handles it). - Not anti-entropy repair.
INVENTORY/GET_ENVELOPESframes are typed inframes.tsbut not exercised in this demo. Hash-range Merkle reconciliation is a Phase 3.5+ feature.
Cross-references
manthan-platform/gates/G-005-mwp-handshake.md— the spec this implementsmanthan-platform/gates/G-006-gossip-discipline.md— gossip frame typeslib/manthan-mwp/— the MWP librarylib/manthan-ledger/— the SQLite ledger storing received envelopeslib/herald-core-ts/src/cbor.ts— CBOR-canonical encoding for transcripts