Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sandwichfarm/nostr-watch/llms.txt

Use this file to discover all available pages before exploring further.

@nostrwatch/nostrings cleans, validates, normalizes, and deduplicates relay URLs for the Nostr protocol. Relay URLs must conform to wss:// or ws:// format and must not point to local or reserved IP ranges. The library exposes four composable operations — sanitize, qualify, normalize, and dedup — that handle messy real-world relay URL inputs. The sanitize function runs the full pipeline in a single call and is the recommended entry point for most use cases.

Installation

npm install @nostrwatch/nostrings

Quick start

import {sanitize, normalize} from '@nostrwatch/nostrings'

const raw = [
  '  WSS://RELAY.DAMUS.IO/#fragment  ',
  'wss://nos.lol',
  'ws://localhost',
  'wss://nos.lol'
]

const clean = sanitize(raw)
console.log(clean)
// ['wss://relay.damus.io/', 'wss://nos.lol/']
The pipeline lowercases, trims, strips fragments, removes trailing slashes, rejects local and reserved IP addresses, normalizes, and deduplicates — all in one pass.

API

sanitize(relays, rules?)

sanitize(relays: string[], rules?: RuleSet): string[] | void
Runs the full pipeline on an array of relay URLs: splits comma-separated entries, sanitizes each URL with sanitizeRelayUrl, filters with qualifyRelayUrl, applies optional custom discriminators and mutators, normalizes, and deduplicates. Returns the cleaned array, or void if the input is empty.
relays
string[]
required
Raw relay URLs to process. Entries may be comma-separated, URL-encoded, mixed-case, or contain fragments.
rules
RuleSet
Optional custom rules to apply after built-in filtering.
Returns: string[] with the cleaned relay URLs, or void if the input array is empty.

qualifyRelayUrl(relay)

qualifyRelayUrl(relay: string): boolean
Returns true if the URL passes all qualification checks. Uses a pessimistic invalidation pattern — any failure disqualifies the URL. Checks performed:
  • Must start with wss:// or ws://
  • Hostname must contain a dot (rejects mangled URLs like wss://wws//nos.lol)
  • Must not be a local file path or network path (file:, UNC paths)
  • Must not resolve to a reserved IP range (loopback, private network, link-local)
  • Must not contain localhost or .local
  • Must not embed a secondary protocol string in hostname or pathname
  • Must not be a NATO phonetic alphabet spam URL (e.g., wss://relay.example.com/alpha-bravo)
  • Must not contain an npub public key string
relay
string
required
A single relay URL string to check.

normalizeRelayUrl(relay)

normalizeRelayUrl(relay: string): string
Strips hash, search, and username components from a relay URL using the browser-native URL API. Returns the canonical form, or an empty string if the URL cannot be parsed.
relay
string
required
A single relay URL string to normalize.

dedup(relays)

dedup(relays: string[]): string[]
Removes duplicate URLs from an array using a Set. Returns a new array with unique entries only.
relays
string[]
required
Array of relay URLs, possibly containing duplicates.

Additional exports

The following functions are also exported for use in custom pipelines:
sanitizeRelayUrl
(relay: string) => string
Sanitizes a single URL: lowercases, trims, removes pipes, consolidates whitespace, removes trailing slashes and dots, strips blob hashes, and returns the part before any comma.
maybeSplitRelayList
(relays: string[]) => string[]
Splits comma-separated relay strings into individual entries. Useful when relay lists arrive as a single comma-joined string.
normalizeRelayUrls
(relays: string[]) => string[]
Applies normalizeRelayUrl to every entry in an array. Convenience wrapper around Array.map.
normalizeRelayUrlAcc
(acc: string[], relay: string) => string[]
Reducer form of normalizeRelayUrl for use with Array.reduce. Skips entries that normalize to an empty string.
isLocal
(url: string) => boolean
Returns true if the URL is a local file path (file: protocol) or a Windows UNC network path.
isLocalNet
(url: string) => boolean
Returns true if the URL hostname falls within a reserved IP range: 127.x.x.x (loopback), 10.x.x.x, 192.168.x.x, or 172.16–31.x.x (private networks).
isNatoPhoneticSpam
(url: URL) => boolean
Returns true if the URL’s final path segment consists of one to three hyphen-separated NATO phonetic alphabet codes or known bot-network spam words. Used internally by qualifyRelayUrl.
VERSION
string
The current package version string. Consumers use this to version-gate retroactive sanitization sweeps — bumping this value triggers a one-shot re-evaluation of relay URL rows under the new rules.

Usage examples

import {sanitize} from '@nostrwatch/nostrings'

const raw = [
  '  WSS://RELAY.DAMUS.IO/#fragment  ',
  'wss://nos.lol',
  'ws://localhost',
  'wss://nos.lol',
  'wss://192.168.1.1'
]

const clean = sanitize(raw)
// ['wss://relay.damus.io/', 'wss://nos.lol/']

Spam filtering

@nostrwatch/nostrings includes built-in detection for a known bot network that generates relay URLs with NATO phonetic alphabet codes and related words as URL path segments (e.g., wss://relay.example.com/alpha-bravo). The isNatoPhoneticSpam function checks for one to three hyphen-separated codes from a list of 52 known spam words and is applied automatically inside qualifyRelayUrl.
The spam word list covers all 26 NATO phonetic codes plus 26 custom words extracted from production relay status data. If you encounter a new spam pattern, it can be added to the SPAM_WORDS constant in the source.

Known limitations

No known limitations at this time.

@nostrwatch/nocap

Relay capability checker that uses nostrings for URL validation before connecting.

@nostrwatch/route66

Relay aggregation and state management; passes relay URLs through nostrings before storage.

Build docs developers (and LLMs) love