Skip to main content

Module transport

Module transport 

Source
Expand description

Transport abstraction for Community events.

The protocol’s send/sync logic is written against this trait, not against the live Nostr client, so it can be exercised end-to-end by multiple emulated clients sharing an in-memory relay (no network, fully deterministic). Production provides an adapter over NOSTR_CLIENT; tests use [MemoryRelay].

Structs§

LiveTransport
Production Transport over the live Nostr network.
Query
The slice of a relay query the Community protocol needs: event kinds, the z pseudonym tag values, and an optional since floor. Production translates this into a Nostr Filter; the in-memory relay matches it directly.

Constants§

CONFIRM_WINDOW
Hard ceiling on the initial confirmation: at least one relay must ACK within this window or the publish is a failure (we throw rather than spin on a dead/unreachable relay set forever). Once ONE relay accepts, the slow/ratelimited stragglers are threaded in the background (capped at MAX_PUBLISH_ATTEMPTS).
FETCH_UNION_GRACE_MS
Union grace window: how long past the FIRST response the live transport keeps unioning slower relays before returning. Long enough for a healthy-but-slower relay on a high-latency link; short enough that a dead relay doesn’t stall every sync.
MAX_PUBLISH_ATTEMPTS
Per-relay broadcast cap: retry each relay up to this many times before giving up on it (matching the NIP-17 deletable-DM durability). High enough to ride out a transient relay/local-network blip.

Traits§

CommunityIngestSink
Sink for “straggler” events — ones a SLOWER relay returns after a racing LiveTransport::fetch has already handed the caller the first relay’s batch. The integrator (src-tauri) registers a handler that feeds them back through the normal Concord ingest path (process_incoming for content, control/rekey re-fold for authority). The transport stays DUMB: it dedups only by event id (identical bytes) and forwards everything else. Two relays disagreeing on the latest control commit are two editions with DIFFERENT ids, so BOTH reach the ingester, where the deterministic convergence engine (version floors + same-version tiebreakers) decides the winner. The transport never resolves conflicts itself.
Transport
Publish + fetch over a set of relays. Async to match the live Nostr client (the whole app is tokio-based and network I/O is async); async-trait boxes the futures as Send so impls work inside spawned tasks.

Functions§

durable_broadcast
The retry engine behind a durable broadcast, factored out so it is unit-testable without a live client. send_round(pending) performs ONE broadcast attempt to the given still-pending relays and returns the subset that ACKed; this retries the rest (with backoff between rounds) until every relay has ACKed or max_attempts is reached. Returns Ok if at least one relay ever accepted (the event is durably out there; the fetch-union self-heals the stragglers), Err only if ZERO relays accepted after exhausting the retries. Dedups relays first so a duplicated url isn’t double-counted.
forget_warmed_relay
Forget a relay from the warm set — call when a relay is REMOVED from the pool (e.g. pruned after leaving a community), so a later warm_client for another community that shares it doesn’t fast-path-skip the re-add and target a relay the pool no longer holds. Poison-tolerant: the set is pure optimization state, so a poisoned lock is recovered rather than propagated.
prune_unneeded_community_relays
Shed pooled Community relays from candidates that no JOINED community still needs. Used by both the leave path (relays of a community we left) and the invite-preload TTL cleanup (relays an unsolicited/declined invite warmed but never became a join, #297). Keep rules: a relay is kept if a remaining joined community lists it, OR it carries READ/WRITE (the user’s own chat relays — Community relays are GOSSIP-only, so never READ/WRITE). A pruned relay re-warms automatically if its invite is later accepted (the join’s subscription re-adds it), so pruning a still-pending invite’s relay is safe.
set_community_ingest_sink
Register the straggler ingest sink. Call once during app startup (mirrors set_event_emitter).