Files
skills/docs/networking/drive/drive-mesh/src/gossip/gossip-engine/gossip-engine.md
2026-05-29 08:20:56 +01:00

4.1 KiB

gossip-engine.ts

(no submodules)

Purpose

GossipEngine runs the bitset-gossip loop drive-mesh uses to keep peers informed of local chunk availability. Two responsibilities: (1) periodically push local availability to every connected peer, and (2) accept inbound advertisements and forward them to the PeerRegistry.

The engine is transport-agnostic — it iterates the configured DriveTransport[] and uses each transport's advertise / onAdvertisement surface. Gossip is intentionally best-effort: a failed advertise call is swallowed and retried on the next tick. Chunk-fetch failures, by contrast, drive the scoring + denylist inside the registry.

Features

  • Periodic outbound tick (default 5_000 ms, configurable).
  • Inbound handler wired automatically on start.
  • tick() exposed for deterministic testing.
  • Cleanup on stop removes every subscription and clears the timer.
  • Pluggable advertSource so the mesh decides what bytes go on the wire (the chunks plugin's hasChunks result + framing).

Usage: Basic Example

import { GossipEngine } from "@suaveplan/drive-mesh/gossip/gossip-engine";

const gossip = new GossipEngine({
    registry,
    transports: [httpsTransport],
    advertSource: fileId => buildAdvert(fileId),
    trackedFiles: () => chunks.knownFiles(),
});
gossip.start();
// ...
gossip.stop();

Usage: Advanced Example

import { GossipEngine } from "@suaveplan/drive-mesh/gossip/gossip-engine";
import { encodeFramedBitset } from "@suaveplan/drive-mesh-protocol";

const gossip = new GossipEngine({
    registry,
    transports: [webrtcTransport, httpsTransport],
    intervalMs: 1_000,
    advertSource: async fileId => {
        const bits = await chunks.hasChunks(fromHex(fileId));
        const totalChunks = bits.length;
        const raw = boolsToBytes(bits);
        return {
            fileId,
            totalChunks,
            bitset: encodeFramedBitset(raw, totalChunks),
        };
    },
    trackedFiles: () => activeFiles,
    onAdvertiseSent: (peerId, advert) =>
        telemetry.emit("gossip:sent", { peerId, fileId: advert.fileId }),
    onAdvertiseReceived: (peerId, advert) =>
        telemetry.emit("gossip:received", { peerId, fileId: advert.fileId }),
});

gossip.start();
// Force-trigger a tick for an integration test:
await gossip.tick();

API Reference

  • class GossipEngine:
    • constructor(options: GossipEngineOptions).
    • start(): void — wires inbound subscriptions + schedules the outbound tick. Idempotent.
    • stop(): void — cancels the tick and detaches subscriptions. Idempotent.
    • tick(): Promise<void> — run a single outbound pass; intended for tests.
  • GossipEngineOptions:
    • registry — PeerRegistry instance.
    • transports — readonly DriveTransport[].
    • advertSource(fileId) — returns the local advertisement or undefined when nothing to advertise.
    • trackedFiles() — generator of fileId hex strings.
    • intervalMs? — outbound tick period; default 5_000.
    • clock? — defaults to Date.now-based clock.
    • onAdvertiseSent?(peerId, advert) — observability hook.
    • onAdvertiseReceived?(peerId, advert) — observability hook.

Implementation Notes

Outbound fan-out iterates every peer reported by every transport. Duplicate peers across transports receive duplicate advertisements, which is fine: receivers idempotently overwrite the bitset and the registry's recordAvailability is set-shaped.

The scheduling primitive is setTimeout rather than setInterval so overlapping ticks cannot occur — the next tick is only scheduled after the previous tick's promise resolves. unref is applied where available (Node) so the event loop can drain in tests; browsers do not implement unref, which is fine because they manage timers through page lifecycle instead.

Inbound advertisements flow through addOrUpdate (updates lastSeen) and recordAvailability (updates the bitset) so a peer sending its first advertisement is admitted to the registry without a separate peer:joined event being required.