Files
2026-05-27 23:47:53 +01:00
..
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00

@suaveplan/network-discovery-gundb - Browser-tier GunDB DiscoveryStrategy: gossip-over-WebSocket peer discovery via Gun. Relay list REQUIRED — no public-relay fallback. Mandatory Ed25519-signed peer announces. Zod wire-payload validation. Constant-time peer-ID compare. AbortSignal + EventBusLike + ServiceRegistry + OTel + Zod boundaries.

Summary

Browser-tier DiscoveryStrategy for @suaveplan/network-bootstrap chains. Wraps a GunDB instance — gossip-over- WebSocket decentralised graph database — and exposes a deduped, capped peer set to the bootstrap pipeline.

GunDB is flagged security-fragile upstream; this release tightens every relevant attack surface.

Documentation tree

- [gundb-discovery](./src/core/gundb-discovery/gundb-discovery.md) — _(no submodules)_- [errors](./src/errors/errors.md) — _(no submodules)_- [errors](./src/errors/errors.md) — _(no submodules)_- [otel](./src/otel/otel.md) — _(no submodules)_- [otel](./src/otel/otel.md) — _(no submodules)_- [plugin](./src/plugin/plugin.md) — _(no submodules)_- [plugin](./src/plugin/plugin.md) — _(no submodules)_- [signing](./src/signing/signing.md) — _(no submodules)_- [signing](./src/signing/signing.md) — _(no submodules)_- [transport](./src/transport/transport.md) — _(no submodules)_- [envelope-signing](./src/transport/envelope-signing.md) — Provides the cryptographic helpers used by `GunDBTransport` to sign every outgoing `MessageEnvelope` and verify every i…

Why this package

  • Plug-and-play DiscoveryStrategy slot for network bootstrap chains. Works alongside @suaveplan/network-discovery-peerjs, @suaveplan/network-discovery-trystero, @suaveplan/network-discovery-webtorrent, and @suaveplan/network-discovery-crdt.
  • Relay list REQUIRED. Upstream's public-relay fallback (https://gun-manhattan.herokuapp.com/gun) is removed. Callers must supply their own relays.
  • Every outbound announce is Ed25519-signed. The local peer's 32-byte secret is the contract input.
  • Every inbound announce is Zod-parsed AND Ed25519-verified before admission. Failures silently drop the payload and increment gundb.invalid_messages_total{reason} / gundb.signature_rejected_total{reason} — a malicious peer cannot crash a node by gossiping garbage.
  • Constant-time peer-ID compare via constantTimeEqual from @suaveplan/crypto-utils.
  • Injectable gunFactory — production wires the real Gun(config); tests bind a deterministic GunNodeLike fake (no vi.mock ever used).
  • Typed EventBusLike<GunDBEventMap> integration.
  • Optional @suaveplan/service-discovery ServiceRegistry mirroring.
  • OTel surface: disc_gundb.start span; counters peers_discovered_total{outcome}, gundb.invalid_messages_total{reason}, gundb.signature_rejected_total{reason}, disc_gundb.relay_errors_total{relay}; histogram gundb_announce_duration_ms.
  • AbortSignal honoured on every async public method.
  • 100% test + branch + function + line coverage.

Basic example

import { GunDBDiscovery, publicKeyHex } from "@suaveplan/network-discovery-gundb";

const signingKey = crypto.getRandomValues(new Uint8Array(32));
const localPeerId = publicKeyHex(signingKey);

const discovery = new GunDBDiscovery({
    peers: ["wss://my-private-relay.example.com/gun"],
    gunFactory: async (cfg) => {
        const { default: Gun } = await import("gun");
        return Gun({ peers: cfg.peers as string[] }) as unknown as GunNodeLike;
    },
    localPeerId,
    signingKey,
});

await discovery.start();
const peers = await discovery.discover(5_000);
console.log(`found ${peers.length} peers`);
await discovery.stop();

Advanced example

import { EventBus } from "@suaveplan/events";
import { ServiceRegistry } from "@suaveplan/service-discovery";
import { DiscoveryChain } from "@suaveplan/network-bootstrap";
import {
    GunDBDiscovery,
    publicKeyHex,
    type GunDBEventMap,
} from "@suaveplan/network-discovery-gundb";

const bus = new EventBus<GunDBEventMap>();
const registry = new ServiceRegistry();
const signingKey = crypto.getRandomValues(new Uint8Array(32));

bus.on("gundb:peer-discovered", ({ peer }) => {
    console.log("peer:", peer.id, peer.addresses);
});
bus.on("gundb:relay-error", ({ reason }) => {
    console.warn("relay:", reason);
});

const discovery = new GunDBDiscovery({
    peers: [
        "wss://relay-a.example.com/gun",
        "wss://relay-b.example.com/gun",
    ],
    room: "my-app/v1",
    maxPeers: 256,
    announceIntervalMs: 15_000,
    gunFactory: async (cfg) => {
        const { default: Gun } = await import("gun");
        return Gun({
            peers: cfg.peers as string[],
            localStorage: cfg.localStorage,
        }) as unknown as GunNodeLike;
    },
    localPeerId: publicKeyHex(signingKey),
    signingKey,
    bus,
    registry,
});

const chain = new DiscoveryChain([discovery]);
const peers = await chain.discover(5_000);

API

See sibling co-located markdown:

License

MIT