@johnhenry/browsermesh-discovery
v0.2.1
Published
DHT, peer discovery, naming, swarm coordination, and stealth networking for BrowserMesh
Readme
browsermesh-discovery
DHT, peer discovery, naming, swarm coordination, and stealth networking for BrowserMesh.
Why this exists
Identity (browsermesh-primitives/browsermesh-core) answers "who is this peer," but a mesh also needs to answer "which peers exist, and how do I find one I haven't talked to yet." That's a genuinely different problem -- Kademlia-style routing tables, gossip, service directories, name resolution, swarm membership/leader-election -- and bundling it into browsermesh-core would force every identity-only consumer to pull in DHT/swarm machinery it doesn't need. browsermesh-discovery exists as that separate layer: DhtNode/RoutingTable/GossipProtocol for peer-to-peer lookup, DiscoveryManager/DiscoveryStrategy for pluggable discovery mechanisms (including DHT-backed bootstrap), MeshNameResolver for human-readable naming, and SwarmCoordinator/SwimMembership for group membership and coordination. It depends only on browsermesh-primitives, for the shared MESH_TYPE message-type constants its DHT and swarm messages are tagged with.
Used by
@johnhenry/browsermesh-apps treats this package as an optional peer: mesh-bootstrap.mjs lazily import()s it to wire a real DHT-backed discovery strategy into createMeshNode() (mesh-dht.mjs), and serverless-fetch.mjs's MeshFetchRouter reuses this package's parseMeshRequest() directly to route mesh:// URLs. Neither is a hard requirement -- createMeshNode() works with no discovery strategy at all, or with a caller-supplied one.
Provenance
Extracted from the private clawser monorepo (previously packages/browsermesh-discovery), where it was manually published to npm, unscoped, as [email protected] (2026-07-17) with no CI ever automating that publish. This is its first release as part of the @johnhenry/browsermesh monorepo; the version restarts at 0.0.0 per family convention.
Modules
| Module | Key Exports |
|--------|-------------|
| dht | DhtNode, RoutingTable, KBucket, GossipProtocol |
| discovery | DiscoveryManager, DiscoveryStrategy, ServiceDirectory, BroadcastChannelStrategy |
| naming | MeshNameResolver, NameRecord, parseMeshUri |
| swarm | SwarmCoordinator, LeaderElection, TaskDistributor, SwimMembership |
| sw-routing | MeshFetchRouter, parseMeshRequest |
| stealth | StealthAgent, ShardDistributor, ShardCollector |
Install
npm install @johnhenry/browsermesh-discovery @johnhenry/browsermesh-primitivesUsage
import { DhtNode, DiscoveryManager, SwarmCoordinator } from '@johnhenry/browsermesh-discovery';StealthAgent is sharding; encryption is opt-in
For secrecy, derive a per-group key and use the async encrypted path:
const key = await deriveStealthKey(groupSecret, 'my-group'); // HKDF-SHA-256 -> AES-256-GCM
const agent = new StealthAgent({ agentId, dhtNode, key });
await agent.hideEncrypted(state); // shard holders see only ciphertext
const state = await agent.reconstituteEncrypted();The payload is bound to the agent id and a wrong key or any tampering rejects.
Not done yet (tracked in #230): threshold sharing of the key, signed shards,
agent-anonymous DHT keys, a real checksum and multi-loss parity. The plain
hide() / reconstitute() below are unchanged and plaintext:
StealthAgent.hide(stateString) / reconstitute() split a state string into
threshold plaintext data shards plus XOR parity shards and store them in the
DHT under stealth:<agentId>:shard:<i>. It improves availability, not secrecy:
- the shards contain the state verbatim (data shards) or its XOR (parity); anyone who can read the DHT entries can read the state;
- every parity shard is the same XOR of all data chunks, so the state survives
the loss of one data shard, not
total - thresholdof them; - the checksum detects accidental corruption only, not tampering;
- the keys name the agent they hold state for.
If the state is sensitive, encrypt it before hide() and decrypt after
reconstitute(); this package does not manage keys.
License
MIT
