@nostrwatch/seed collects relay URLs from multiple sources to bootstrap the relay monitoring pipeline. Relay monitors (such as @nostrwatch/relaymon) need an initial list of relays (WebSocket servers that store and forward Nostr events) to check — seed provides that bootstrap by querying config values, static files, databases, REST APIs, and Nostr events. Results are deduplicated, sanitized, and filtered by network type (clearnet, Tor, I2P) before being handed off to the monitor.
Node.js >=20 and pnpm >=9.
This is an internal monorepo package. Add it as a workspace dependency:
pnpm add @nostrwatch/seed --filter @nostrwatch/your-packageIt is not published to npm.
import {RelaySeeder} from '@nostrwatch/seed'
const seeder = new RelaySeeder({
interval: 3600000, // re-seed every hour
sources: ['config', 'static', 'api'],
options: {
allowedNetworks: ['clearnet'],
config: ['wss://relay.damus.io', 'wss://nos.lol'],
static: {path: './relays.yaml'},
api: {rest_api: 'https://api.nostr.watch/v1'}
}
})
await seeder.start()
const relays = seeder.getRelays()
console.log(`Seeded ${relays.length} relays`)
// Seeded 247 relaysclass RelaySeeder {
constructor(options: SeederOptions)
start(): Promise<void>
stop(): void
seed(): Promise<string[]>
getRelays(): string[]
getLastSeedTimestamps(): Record<string, number>
}constructor(options)
Creates a new seeder. Loads last seed timestamps from the database and sets up the relay block function.
start()
Runs seed() in a loop, waiting interval milliseconds between runs. Resolves only when stop() is called.
stop()
Signals the seeding loop to exit after the current iteration completes.
seed()
Runs all configured source strategies once, deduplicates results, sanitizes URLs, and filters by allowedNetworks. Returns the filtered relay list.
getRelays()
Returns the current deduplicated relay list as an array of URL strings.
getLastSeedTimestamps()
Returns a map of source name to Unix timestamp indicating when each source was last queried.
interface SeederOptions {
interval: number
sources: string[]
options: {
db?: {path: string; enableWAL?: boolean}
static?: {path: string}
config?: string[]
api?: {rest_api: string}
events?: {pubkeys: string[]; relays: string[]}
allowedNetworks?: string[]
logLevel?: LogLevel
isRelayBlocked?: (relay: string) => boolean
}
}| Source | Description |
|---|---|
config |
Relay URLs passed directly as an array in options.config |
static |
Relay URLs read from a YAML or JSON file at options.static.path |
db |
Relay URLs queried from a SQLite database at options.db.path (filters by allowedNetworks) |
api |
Relay URLs fetched from a REST API at options.api.rest_api/online |
events |
Relay URLs extracted from NIP-66 kind 30166 events published by the authors in options.events.pubkeys, fetched from options.events.relays |
cache |
Relay URLs queried from the local @nostrwatch/db relay status table |
Set allowedNetworks to restrict which relay URLs are returned. Supported values:
clearnet— standardwss://relay URLstor—.onionrelay URLsi2p— I2P relay URLs
If allowedNetworks is empty or omitted, the seeder defaults to ['clearnet'].
Pass isRelayBlocked to exclude specific relays:
const seeder = new RelaySeeder({
interval: 3600000,
sources: ['config'],
options: {
allowedNetworks: ['clearnet'],
config: ['wss://relay.damus.io'],
isRelayBlocked: (relay) => relay.includes('blocked-relay.com')
}
})The static source expects a YAML or JSON file with a relays array:
relays:
- wss://relay.damus.io
- wss://nos.lol
- wss://relay.nostr.bandNo known limitations at this time.
No agent skills defined yet for this package.
@nostrwatch/relaymon— primary consumer ofRelaySeeder; uses it to bootstrap the relay list before starting health checks@nostrwatch/db— provides the SQLite database that thedbandcacheseed sources read from@nostrwatch/utils— providesparseRelayNetworkused to detect relay network type during filtering