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/relaymon monitors Nostr relay (WebSocket server) health through automated check cycles. It runs configurable check suites — DNS, connectivity, SSL, NIP-11 info document retrieval, and geo-location — against a seeded relay list, then publishes results as NIP-66 relay status events to the Nostr network. Relays are discovered from multiple seed sources including static configuration, API endpoints, and existing NIP-66 events. Supports clearnet, Tor (.onion), and I2P (.i2p) relay monitoring via configurable network routing.

Prerequisites

  • Deno >=1.40
  • SQLite is created automatically at the path specified in config.yaml

Environment variables

VariableRequiredDescriptionExample
RELAYMON_NSECYesPrivate key for signing NIP-66 events and monitor announcementsnsec1...

Installation

@nostrwatch/relaymon is a Deno application — no npm install step is required. Clone the monorepo and navigate to the app directory.
1

Install Deno

Install Deno >=1.40 from deno.land if you have not already.
2

Navigate to the app directory

cd apps/relaymon
3

Copy the sample configuration

cp config.sample.yaml config.yaml
4

Edit config.yaml

At minimum, set monitor.slug, publisher.relays, and relaymon.networks to match your deployment.
5

Set your signing key

export RELAYMON_NSEC=nsec1...
6

Start monitoring

deno task start

Quick start

# Copy and edit the configuration file
cp config.sample.yaml config.yaml

# Start monitoring
RELAYMON_NSEC=nsec1... deno task start

Available tasks

TaskDescription
deno task startStart the monitoring loop
deno task interactiveLaunch the interactive CLI
deno task statusPrint a status report
deno task dbcheckRun the database integrity check utility
deno task compile:linux-x64Compile a standalone binary for Linux (x64)

Docker

Relaymon ships two Docker image variants:
ImageDescription
nostrwatch/relaymon:clearnetMonitors clearnet relays only
nostrwatch/relaymon:multinetMonitors clearnet, Tor (.onion), and I2P (.i2p) via transparent proxy routing
nostrwatch/relaymon:latestAlias for clearnet
docker compose -f apps/relaymon/.docker/docker-compose.yml up -d
The multinet variant includes supplementary containers for network routing:
  • tor-proxy — Tor SOCKS proxy for .onion addresses
  • i2pd — I2P router for .i2p addresses
Traffic is routed transparently based on domain type with no application code changes needed.
For production deployments with Docker Compose, see Deploy with Docker Compose stacks. Those stacks include .env templates, volume mounts, and composed service definitions.

Configuration reference

Relaymon is configured via config.yaml. Copy config.sample.yaml as a starting point.
logLevel: info

db:
  path: ./data/relays.db
  enableWAL: true

monitor:
  slug: relaymon
  info:
    name: a monitor
    about: monitors nostr relays
  owner: <hex-pubkey>
  relays:
    - 'wss://relay.nostr.watch'
    - 'wss://relaypag.es'

publisher:
  relays:
    - 'wss://relay.nostr.watch'
    - 'wss://relaypag.es'

announce:
  relays:
    - 'wss://purplepag.es'
    - 'wss://user.kindpag.es'

relaymon:
  networks:
    - clearnet
    # - tor
    # - i2pd

  retry:
    expiry:
      - max: 2
        delay: 1m
      - max: 5
        delay: 20m
      - max: 8
        delay: 3h
      - max: 22
        delay: 24h
      - max: 99
        delay: 7d

  seed:
    interval: 1m
    sources:
      - events
      # - config
      # - db
    options:
      events:
        pubkeys:
          - <hex-pubkey>
        relays:
          - 'wss://relay.nostr.watch'
          - 'wss://relaypag.es'

  checks:
    enabled:
      - open
      - read
      - info
      - dns
    options:
      expires: 1d
      interval: 30s
      statusInterval: 100
      timeout:
        open: 30000
        read: 5000
      max: 200

health:
  enabled: true
  server:
    enabled: true
    host: "127.0.0.1"
    port: 8080
    authEnabled: false
  kuma:
    enabled: true
    intervalMs: 1m
    degradedAsUp: true
    startupGraceMs: 30s
    msgVerbosity: "summary"
  thresholds:
    checkIdleMs: 5m
    publishBacklogMax: 100
    errorRatePerMin: 10
    startupGraceMs: 30s

queue:
  workerConcurrency: 10

Key configuration options

KeyDescription
logLevelLogging verbosity: debug, info, warn, error
db.pathPath to the SQLite database file
db.enableWALEnable Write-Ahead Logging for concurrent access
monitor.slugUnique identifier for this monitor instance
monitor.info.nameHuman-readable name published in monitor announcements
monitor.ownerOwner public key (hex) associated with this monitor
monitor.relaysRelays used for monitor announcement publishing
publisher.relaysRelays that receive NIP-66 check result events
announce.relaysRelays used for Kind 10002 relay list announcements
relaymon.networksNetwork types to monitor: clearnet, tor, i2pd
relaymon.retry.expiryStepped retry backoff rules — each entry sets max retries before advancing to delay
relaymon.seed.intervalHow often to refresh the seed relay list
relaymon.seed.sourcesWhere to discover relays: config, static, api, events, db
relaymon.seed.options.events.pubkeysPubkeys whose NIP-66 events are used as a relay seed source
relaymon.checks.enabledWhich check types to run: open, read, info, dns
relaymon.checks.options.expiresHow long a check result is considered fresh
relaymon.checks.options.intervalHow often to re-run checks on each relay
relaymon.checks.options.timeout.openWebSocket open timeout in milliseconds
relaymon.checks.options.timeout.readRead check timeout in milliseconds
relaymon.checks.options.maxMaximum number of relays to monitor concurrently
health.server.portPort for the HTTP health endpoint
health.kuma.enabledEnable Uptime Kuma push integration
queue.workerConcurrencyMaximum parallel relay checks

Known limitations

Relay URL filtering with pipe character: daemon.ts logs relay URLs containing the | character but does not properly clean or reject them. This is a hotfix-level workaround for a structural URL validation gap at ingestion. Remove malformed relay entries from the database directly via the SQLite CLI if this causes issues.
Database inspection function incomplete: interactive/index.ts has a debugInspectDatabase function that logs start and end markers but contains no actual SQL inspection queries. Use the SQLite CLI directly to inspect the database.

Build docs developers (and LLMs) love