feat: NIP-66 relay liveness pre-filtering with safety guardrails#55
Open
alltheseas wants to merge 4 commits into
Open
feat: NIP-66 relay liveness pre-filtering with safety guardrails#55alltheseas wants to merge 4 commits into
alltheseas wants to merge 4 commits into
Conversation
|
Pure helper for classifying and filtering dead relays using NIP-66 monitor data. Safety guardrails prevent rogue monitors from breaking relay selection: - Empty alive set = no-op (NIP-66 PR #2240 requirement 1) - minAliveSetSize (default 100) skips tiny/suspect monitor data - maxFilterRatio (default 0.8) per-user cap on removal - monitoredRelays option preserves unmonitored personal relays - .onion/.i2p and malformed URLs always preserved Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Follows the ignoreBlacklistedRelays pattern. Accepts a static Set or Observable<Set> of alive relay URLs. Observable inputs are wrapped with startWith(emptySet) so the pipeline emits immediately and never blocks waiting for monitor data (NIP-66 PR #2240 requirement 1). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds aliveRelays, livenessFilter, and livenessId to OutboxModelOptions. Pipeline: contacts → blacklist → mailboxes → ignoreDeadRelays → select. Absent aliveRelays = no-op, existing behavior unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
29 tests covering: - normalizeRelayUrl: trailing slash, hostname case, scheme upgrade - classifyRelays: alive/dead/unmonitored, .onion/.i2p, malformed URLs - removeDeadRelays: empty set no-op, minAliveSetSize, maxFilterRatio edge cases (0.8 threshold, all-dead skip, single-relay skip), monitoredRelays preservation - ignoreDeadRelays operator: static Set, Observable with startWith Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
alltheseas
force-pushed
the
feat/nip66-relay-liveness
branch
from
March 5, 2026 18:19
1e9ec80 to
efbae92
Compare
This was referenced Mar 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
removeDeadRelayshelper — pure function that filters dead relays fromProfilePointer[]using NIP-66 monitor alive setsclassifyRelayshelper — classifies relay URLs as alive/dead/unmonitored with.onion/.i2ppreservationignoreDeadRelaysRxJS operator — followsignoreBlacklistedRelayspattern; Observable inputs wrapped withstartWith(emptySet)so the pipeline never blocksOutboxModel— newaliveRelays,livenessFilter,livenessIdoptions; absent = no-op, zero behavior change for existing callersDesigned in response to community feedback on nostrability/nostrability#69. Outbox benchmarks show NIP-66 pre-filtering removes 53.8% dead relays, improves success rate by +41.7pp, and reduces wall-clock time by 45%.
NIP-66 PR #2240 compliance
aliveRelays= no-op (pass-through)maxFilterRatio(default 0.8) per-user cap;minAliveSetSize(default 100) global guardaliveRelaysis opaque — client builds it from WoT-filtered monitors. Applesauce doesn't choose monitorsSafety guardrails
minAliveSetSize(default 100): skip filtering if alive set is suspiciously small (rogue/misconfigured monitor)maxFilterRatio(default 0.8): if filtering would remove >80% of a user's relays, skip that user (protects against monitor claiming most relays are dead)monitoredRelaysoption: only remove relays that monitors actually checked; unmonitored personal relays are preserved.onion/.i2ppreservation: monitors can't check these without Tor/I2PCommits
2fbc33b3classifyRelays,removeDeadRelays,normalizeRelayUrl40516f67ignoreDeadRelayswithstartWith(emptySet)88bab56daliveRelays/livenessFilter/livenessId1e9ec809Test plan
npx vitest run packages/core)removeDeadRelays(users, new Set())returns users unchanged (spec req 1)removeDeadRelays(users, tinySet)wheretinySet.size < 100returns users unchanged.onionrelays preserved regardless of alive setmonitoredRelaysoption keeps unmonitored personal relaysmaxFilterRatioedge cases: 4/5=0.8 filtered, 5/5=1.0 skipped, 1/1=1.0 skippedstartWith, never blocks🤖 Generated with Claude Code