Skip to content

Hackathon

Relay Shadow

A Nostr data vending machine that recommends relays against a stated threat model, using relay and social-graph observations from BigBrotr's earlier PostgreSQL toolchain.

Aug 20253rd prize, Baltic Honeybadger 2025NostrMeasurementPrivacy

Relay Shadow answers “which relays should I use?”, as a service other software can call, over Nostr itself. It takes a threat level and a use case, scores candidate relays against measured data, and returns a ranked set.

Result
3rd prize, Baltic Honeybadger 2025
Interface
NIP-90 DVM, kinds 5600 / 5601 / 7000
Data
PostgreSQL snapshot of BigBrotr relay observations
Repository
BigBrotr/relay-shadow-dvm
BigBrotr/relay-shadow-dvmData Vending Machine providing privacy-focused relay recommendations using BigBrotr data.JavaScript

The problem

A client’s relay selection affects the events it can retrieve and the operators that observe its requests. A fixed default list cannot account for every user’s contacts, availability needs and privacy concerns.

There is measured data that could inform the choice. It sits in an archive, in a shape useful to an analyst rather than to a client at runtime.

What I built

A data vending machine: a service that receives jobs as Nostr events and replies with Nostr events, so clients supporting the job format can consume it over their existing relay connections.

Requests arrive as kind 5600, carrying their parameters in tags: threat level, use case, the relay set the user currently has, how many results they want. Results are signed and published as kind 5601, with kind 7000 used for job feedback. Four request types are supported: recommend, analyze for evaluating a relay set already in use, discover for widening coverage, and health.

This implementation reads a PostgreSQL copy of relay data from BigBrotr’s earlier toolchain, not the current Go archive. Scoring is implemented as query functions that combine four weighted factors: social graph, privacy, reliability, and performance. The default weighting places most of the emphasis on the social graph. There is a React demo client and a small Express endpoint exposing service info.

Hard parts

There is no best relay set, so the request has to say who is asking. Threat level is a request parameter ranging from low to nation-state, and it reaches the scoring query rather than being cosmetic. A relay that is excellent for reach can be exactly the wrong answer for someone whose concern is who observes them, and a recommender returning one ranking for everybody is answering a question nobody asked.

Recommending your own contacts back to you. Weighting the social graph most heavily is what makes a recommendation useful. A relay where nobody you follow publishes is not useful, but this weighting also means the service reads which relays a requester’s contacts use in order to answer. That access requirement belongs in the service’s privacy model.

Answering when the data cannot. The service falls back to a static list per threat level when the query returns nothing, so a request gets a degraded answer rather than an error. The scoring weights are design inputs rather than measured optima, so the response remains a recommendation, not a privacy guarantee.