Hackathon
Relay Shadow
A Nostr data vending machine that recommends relays against a stated threat model, scoring measured privacy, reliability, performance and social-graph data from the BigBrotr archive.
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.
- Built for
- Bitcoin++ Privacy Edition hackathon, 2025
- Result
- 3rd prize, Baltic Honeybadger 2025
- Interface
- NIP-90 DVM, kinds 5600 / 5601 / 7000
- Data
- BigBrotr archive, in PostgreSQL
- Repository
- BigBrotr/relay-shadow-dvm
The problem
Every Nostr client ships a default relay list, and almost nobody changes it. That list decides what a user can see, who can see them, and which operators observe their traffic, and it is usually a handful of well-known hostnames chosen once, by a developer, for everyone.
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 any client that already speaks the protocol can consume it without a new API.
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.
Underneath is a PostgreSQL copy of the BigBrotr relay data with the scoring 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 is a tension a privacy-focused recommender sits with rather than resolves.
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.