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/publisher handles the construction and transport of NIP-66 events — the standardized Nostr events that relay monitors use to announce themselves and share relay health data. Rather than building raw signed JSON objects by hand, you instantiate the appropriate kind class, call generateEvent(), sign it with signEvent(), and hand the result to Publisher for delivery. The package is used internally by @nostrwatch/announce during monitor boot and by the relaymon daemon for each relay check cycle.
@nostrwatch/publisher is an internal monorepo package. It is not published to npm. Add it as a workspace dependency with pnpm add @nostrwatch/publisher --filter @nostrwatch/your-package.

Installation

pnpm add @nostrwatch/publisher --filter @nostrwatch/your-package
Peer dependency: nostr-tools@2.11.0 must be installed in the consuming package.

Quick start

import {Publisher, Kind30166} from '@nostrwatch/publisher'

const pubkey = 'abc123...' // 64-char hex public key
const relays = ['wss://relay.damus.io', 'wss://nos.lol']
const sk = 'deadbeef...' // 64-char hex secret key

// Build a kind 30166 relay status event from nocap check results
const $kind = new Kind30166(pubkey)
$kind.generateEvent({
  url: 'wss://relay.damus.io',
  open: {duration: 120},
  info: {data: {supported_nips: [1, 11, 42]}}
})
const signed = await $kind.signEvent(sk)

// Publish to one or more relays
const publisher = new Publisher(pubkey, relays)
await publisher.publishEvent(signed)

Core API

Publisher

Publisher handles transport — it connects to relays and calls pool.publish via a nostr-tools SimplePool.
class Publisher {
  constructor(pubkey: string, relays: string[], config?: Config)
  publishEvent(signedEvent: NostrEvent): Promise<any>
  publishEvents(signedEvents: AsyncIterable<NostrEvent>): Promise<any[]>
}
ParameterTypeDescription
pubkeystring64-char hex public key of the signing identity
relaysstring[]WebSocket relay URLs to publish to
configConfigOptional — no active configuration properties in current implementation
publishEvent(signedEvent) — Publishes one signed event to all configured relays using Promise.any. Resolves as soon as any relay accepts the event; throws if all relays reject. publishEvents(signedEvents) — Publishes each event in the async iterable in sequence, returning an array of results.

Event base class

All kind classes extend Event. You can subclass it directly to implement event types not covered by the built-in kind classes:
class Event {
  constructor(kind: number, pubkey: string)
  generateEvent(data: any): NostrEvent
  signEvent(sk: string): Promise<SignedEvent>
}
Override _generateEvent(data) in your subclass to assemble the tags and content fields for your custom event kind. Call generateEvent() before signEvent()signEvent throws if generateEvent has not been called first.

Event kind classes

All kind classes extend Event and accept pubkey: string in their constructor.
1

Instantiate the kind class

const $kind = new Kind30166(pubkey)
2

Generate the event

$kind.generateEvent(checkData)
3

Sign the event

const signed = await $kind.signEvent(sk)
signEvent calls finalizeEvent and verifyEvent from nostr-tools. It throws if signature verification fails.
4

Publish via Publisher

await publisher.publishEvent(signed)

Kind30166 — NIP-66 relay status

Publishes a kind 30166 relay status event. The check parameter is assembled from nocap check results and maps relay health data — RTT timings, NIP-11 info, SSL validity, DNS records, geo data — into the NIP-66 event tag structure.
class Kind30166 extends Event {
  generateEvent(check: CheckData): NostrEvent
  generateTags(check: CheckData): NostrEventTags
}
This is the most commonly used kind class. Every relay check cycle in relaymon produces one kind 30166 event per relay. See raw NIP-66 events for the full list of event kinds and their tag structures.

Kind10166 — NIP-66 monitor registration

Publishes a kind 10166 monitor announcement event. This declares the monitor’s capabilities — check frequency, supported networks, check types, timeouts, and geo metadata — to the network.
class Kind10166 extends Event {
  generateEvent(data: Kind10166Data): NostrEvent
  static generateTags(data: Kind10166Data): NostrEventTags
}

Kind10002 — NIP-65 relay list

Publishes a NIP-65 kind 10002 relay list event. Used to announce which relays a monitor publishes to.
class Kind10002 extends Event {
  generateEvent(relays: string[]): NostrEvent
  static generateTags(relays: string[]): NostrEventTags
  parse(event: NostrEvent): {relays: string[]}
}

Kind0 — NIP-01 user metadata

Publishes a kind 0 user metadata event. Used by @nostrwatch/announce to publish the monitor’s public profile.
class Kind0 extends Event {
  generateEvent(data: object): NostrEvent
  static generateContent(data: object): string
  static parse(event: {content: string}): any
}

Builder helpers

Low-level helpers for constructing NIP-66 event fields manually. The kind classes use these internally; reach for them only when building a custom event kind not covered by the built-in classes:
function buildBaseEvent(opts: BaseEventOptions): UnsignedEvent
function relayTag(url: string): string[]
function statusTag(status: string): string[]
function rttOpenTag(ms: number): string[]

Monitor announcements with @nostrwatch/announce

@nostrwatch/announce wraps @nostrwatch/publisher to publish the three events a monitor needs on every boot — kind 10166 (monitor registration), kind 10002 (relay list), and kind 0 (profile) — in a single call sequence.
@nostrwatch/announce is also an internal monorepo package. Add it with pnpm add @nostrwatch/announce --filter @nostrwatch/your-package. Peer dependency: nostr-tools@2.1.4.
import {AnnounceMonitor} from '@nostrwatch/announce'

const pubkey = 'abc123...' // 64-char hex public key
const sk = 'deadbeef...' // 64-char hex secret key

const monitor = new AnnounceMonitor(pubkey, {
  relays: ['wss://relay.damus.io', 'wss://nos.lol'],
  userDataRelays: ['wss://purplepag.es'],
  frequency: '3600',
  networks: ['clearnet'],
  checks: ['websocket', 'info', 'dns', 'geo', 'ssl'],
  profile: {name: 'my-monitor', about: 'nostr-watch relay monitor'}
})

monitor.generate()
await monitor.sign(sk)
const publishedIds = await monitor.publish()
// ['<event-id-10166>', '<event-id-10002>', '<event-id-0>']

AnnounceMonitor options

OptionTypeRequiredDescription
relaysstring[]YesRelays the monitor publishes NIP-66 events to
userDataRelaysstring[]NoRelays for kind 0 and 10002 events; defaults to ['wss://purplepag.es', 'wss://user.kindpag.es']
frequencystringNoCheck interval in seconds as a string (e.g., '3600')
networksstring[]NoNetwork types the monitor covers (e.g., ['clearnet', 'tor'])
checksstring[]NoCheck types to declare; pass ['all'] to expand to all supported checks
timeoutsobjectNoPer-check timeout configuration
geoobjectNoMonitor geolocation data
profileobjectNoNIP-01 profile fields for the kind 0 event
After generate(), call sign(sk) and then publish(). publish() routes kind 0 and 10002 to userDataRelays and kind 10166 to relays.

When to use publisher vs nocap-route66

Use publisher

When you need to sign and broadcast NIP-66 events to the network — relay status updates, monitor registration, relay lists, or profile metadata. Publisher handles construction and transport.

Use nocap + route66

When you need to perform relay checks and read or aggregate existing NIP-66 events from the network. nocap probes relays; route66 queries and stores the results.
The two concerns are complementary: nocap produces CheckData objects that Kind30166.generateEvent() consumes, and route66 aggregates the kind 30166 events that publisher emits.

Build docs developers (and LLMs) love