@@ -43,8 +43,13 @@ function isBlockedHost(host) {
4343 * (trailing slash on bare origins), or null if it must not be stored/dialed.
4444 */
4545export function safeRelayUrl ( url ) {
46+ const raw = String ( url ) ;
47+ // Real relay URLs are short and single-scheme. Reject length blowups,
48+ // whitespace, and a second "://" (embedded scheme) — the signature of
49+ // concatenated multi-relay junk packed into one tag by broken clients.
50+ if ( raw . length > 120 || / \s / . test ( raw ) || raw . indexOf ( '://' ) !== raw . lastIndexOf ( '://' ) ) return null ;
4651 let u ;
47- try { u = new URL ( String ( url ) ) ; } catch { return null ; }
52+ try { u = new URL ( raw ) ; } catch { return null ; }
4853 if ( u . protocol !== 'wss:' && u . protocol !== 'ws:' ) return null ;
4954 if ( isBlockedHost ( u . hostname ) ) return null ;
5055 if ( u . pathname === '' || u . pathname === '/' ) return `${ u . protocol } //${ u . host } /` ;
@@ -68,8 +73,10 @@ const tms = (x) => (x ? new Date(x).getTime() || 0 : 0);
6873 * e.g. a bomb) — keep the origin + richest few, drop the rest
6974 * - renames: a surviving doc's stored `relay` ≠ its canonical form
7075 * - stale: (if staleDays) a survivor not checked within staleDays
76+ * - notReal: (if realOnly) survivors that aren't a bare origin that has been
77+ * online at least once — i.e. the unverified/dead/path entries
7178 */
72- export function planRelaySweep ( docs , { maxPerHost = 3 , staleDays = 0 , now = 0 } = { } ) {
79+ export function planRelaySweep ( docs , { maxPerHost = 3 , staleDays = 0 , now = 0 , realOnly = false } = { } ) {
7380 const rich = ( a , b ) => ( b . checksTotal || 0 ) - ( a . checksTotal || 0 ) || tms ( b . lastChecked ) - tms ( a . lastChecked ) ;
7481 // 1. screen + canonicalize, group by canonical URL
7582 const byCanon = new Map ( ) ;
@@ -102,15 +109,26 @@ export function planRelaySweep(docs, { maxPerHost = 3, staleDays = 0, now = 0 }
102109 }
103110 survivors = kept ;
104111 }
105- // 4. normalizations + stale among survivors
112+ // 4. realOnly: keep only bare-origin relays online at least once (the
113+ // verified/reachable set); everything else is unverified/dead/path junk.
114+ const notReal = [ ] ;
115+ if ( realOnly ) {
116+ const real = [ ] ;
117+ for ( const s of survivors ) {
118+ if ( isOrigin ( s . _canon ) && ( s . checksOnline || 0 ) >= 1 ) real . push ( s ) ;
119+ else notReal . push ( s ) ;
120+ }
121+ survivors = real ;
122+ }
123+ // 5. normalizations + stale among final survivors
106124 const renames = [ ] ;
107125 const stale = [ ] ;
108126 const cutoff = staleDays && now ? now - staleDays * 864e5 : null ;
109127 for ( const s of survivors ) {
110128 if ( s . relay !== s . _canon ) renames . push ( { doc : s , canonical : s . _canon } ) ;
111129 if ( cutoff && tms ( s . lastChecked ) < cutoff ) stale . push ( s ) ;
112130 }
113- return { bad, dups, hostSpam, renames, stale, kept : survivors . length } ;
131+ return { bad, dups, hostSpam, notReal , renames, stale, kept : survivors . length } ;
114132}
115133
116134export async function ensureRelayDirectoryIndex ( db ) {
0 commit comments