Skip to content
GitHubRSS

Data Flow

BigBrotr’s six services are independent processes that share a PostgreSQL database. There is no direct communication between services. Data flows through the database: one service writes, another reads.

A relay URL progresses through several states as different services process it:

Seed files / APIs / NIP-65 events
┌───────────────┐
│ Candidate │ Seeder and Finder insert URLs
│ (URL only) │ into the candidate pool
└───────┬───────┘
┌───────────────┐
│ Validated │ Validator tests WebSocket connectivity
│ Relay │ and promotes to the relay table
└───────┬───────┘
┌───────────────┐
│ Monitored │ Monitor fetches NIP-11 metadata and
│ Relay │ runs NIP-66 health tests
└───────┬───────┘
┌───────────────┐
│ Synced │ Synchronizer collects events
│ Relay │ using cursor-based pagination
└───────────────┘

Each transition is independent. A relay can be validated but not yet monitored. A relay can be monitored but not yet synced. Services pick up work based on their own schedules and the current state of the database.

The Seeder and Finder insert relay URLs. These services detect the network type (clearnet, Tor, I2P, Lokinet) from the URL structure:

  • wss://relay.example.com → clearnet
  • ws://abc...xyz.onion → Tor
  • ws://abc...xyz.b32.i2p → I2P
  • ws://abc...xyz.loki → Lokinet

The Monitor produces multiple metadata types per relay through NIP-11 and NIP-66:

SourceMetadata Types
NIP-11Relay information document (software, version, supported NIPs, limitations)
NIP-66 RTTWebSocket round-trip time
NIP-66 SSLCertificate validity and expiration
NIP-66 DNSResolution time and IP addresses
NIP-66 GeoGeographic location via GeoIP
NIP-66 NetNetwork/ASN information
NIP-66 HTTPHTTP status and headers

All metadata is content-addressed using SHA-256. The hash is computed from canonical JSON of the data field. Same data always produces the same hash, providing automatic deduplication.

The Synchronizer collects Nostr events from validated relays. Events are stored in the event table with relay associations in the event_relay junction table. The cascade function event_relay_insert_cascade atomically inserts across relay, event, and junction tables in a single SQL call.

The Refresher periodically refreshes 11 materialized views that pre-compute analytics:

ViewPurpose
relay_metadata_latestMost recent metadata per relay per type
event_statsGlobal event statistics
relay_statsPer-relay statistics
kind_countsEvent counts by kind
kind_counts_by_relayEvent counts by kind per relay
pubkey_countsUnique pubkey counts
pubkey_counts_by_relayUnique pubkey counts per relay
network_statsStatistics by network type
relay_software_countsRelay software distribution
supported_nip_countsNIP support distribution
event_daily_countsDaily event counts

BigBrotr uses two cascade functions for atomic multi-table inserts:

event_relay_insert_cascade: Given arrays of relay URLs, event data, and associations, inserts into relay, event, and event_relay in a single call.

relay_metadata_insert_cascade: Given arrays of relay URLs, metadata objects, and associations, inserts into relay, metadata, and relay_metadata in a single call.

These eliminate partial inserts and maintain referential integrity without application-level transaction management.