Nostr Relay Tray
Nostr Relay Tray turns relay hosting into a desktop surface: start a local Nostr relay, store events in SQLite, filter what gets accepted, inspect event counts, export JSONL data and, when you choose, expose the relay through a public proxy.
A relay you can touch
Nostr Relay Tray is one of those apps that looks small until you remember what it is making visible. Most Nostr users talk about relays as if they are weather: sometimes helpful, sometimes unavailable, mostly outside your control. This project moves the relay onto your own desktop. You install an app, it sits in the system tray, and underneath that tray icon is a real Nostr relay rather than a bookmark to somebody else's infrastructure.
The project is maintained by CodyTseng on GitHub. The repository was created on January 27, 2024, is written primarily in TypeScript, is MIT-licensed, and describes itself as a Nostr relay for desktop across macOS, Windows and Linux. The latest release available from GitHub is v2.0.0, published on March 23, 2025, with downloadable builds for Apple Silicon and Intel macOS, Windows setup, Linux AppImage, Debian package and Snap. The README is upfront about one practical wrinkle: the app is unsigned, so users may need to approve it manually in system settings. On Apple Silicon, the README even gives the quarantine-removal command for the damaged-app warning.
That is not a minor packaging note. A relay is infrastructure, and infrastructure lives or dies by boring operational trust. Nostr Relay Tray lowers the barrier to starting a relay, but it still asks you to run software on your machine, open ports or use a proxy, store data, decide what gets accepted and deal with whatever arrives. The value is not that it removes responsibility. The value is that it gives a normal user a visible place to practice that responsibility.
NostrApps summarizes the product as a system tray application for managing Nostr relays, and that is accurate. But the more interesting description is this: Nostr Relay Tray turns the relay from an invisible backend into a thing you can configure, inspect and stop. If you have only used Nostr through clients, this is a useful shift. It reminds you that a relay is not an ideology. It is a server that accepts, stores and serves signed events according to rules.
What runs under the tray icon
The app is built with Electron, Electron Vite, React, TypeScript, Tailwind-flavored UI components and Electron Builder. The relay side uses Fastify, a WebSocket server, @nostr-relay/core, @nostr-relay/common, @nostr-relay/validator, @nostr-relay/event-repository-sqlite, better-sqlite3, kysely, nostr-tools and ws. In other words, this is not a skin over a hosted service. It is a desktop shell around a local relay stack.
The relay service creates a SQLite database named nostr.db in Electron's user data path and wraps it with EventRepositorySqlite. It initializes the repository, starts a Fastify server, attaches a WebSocket server, validates incoming Nostr messages, hands valid messages to NostrRelay, and sends a Nostr NOTICE when validation fails. It listens on 0.0.0.0:4869, which means it is not just a loopback-only toy. On your local network, that detail matters. You should know whether your machine is reachable, what firewall rules apply and whether you meant to expose it.
The HTTP root endpoint returns relay information: name nostr-relay-tray, description a nostr relay for desktop, software URL pointing back to the GitHub repo, the app version and supported_nips containing NIP-01 and NIP-50. NIP-01 is the basic relay/client protocol. NIP-50 is search. That lines up with the README's full-text search claim and the source that initializes a search index by walking stored events and inserting them into search.
The settings are concrete rather than ornamental. You can set the maximum WebSocket payload in kilobytes, set the default filter limit, choose tray icon color, change appearance and enable start-at-login through the auto-launch package. The home screen shows total event count and event statistics by kind. That is the correct level of honesty for a local relay: not just "running" or "not running", but what kind of data it has accumulated.
The filtering is the product
A relay without a policy is just an open bucket. Nostr Relay Tray's most important feature is therefore not the tray icon. It is the event filtering system. The README explains a two-layer mechanism: first, block rules get a chance to reject matching events immediately. If an event is not blocked, it moves into the second layer, where allow rules, Web of Trust and Proof of Work can accept it. That order is easy to miss, but it changes how you should think about the app. A block rule is a hard stop. The second layer is a set of independent ways to decide what you are willing to store.
The code matches that description. GuardService runs a block guard first. If the event survives, and no other guards are active, it can be accepted. If guards are active, their results are checked. Allow rules are converted into filters. Block rules are converted into filters. The restriction guard can match event kind, author, tags and content, including negative forms of those fields. The rule editor exposes the same idea to the user: create a named rule, choose Block or Allow, add conditions for author, kind, tag or content, and enable it.
This matters because relay policy is where Nostr gets real. It is easy to say "censorship resistant" until you run a relay and discover spam, illegal content risk, storage growth, low-quality floods, repeated event kinds you do not care about, and the tension between openness and usefulness. Nostr Relay Tray gives a non-server-admin a way to build a policy without writing a custom relay from scratch. You can block known garbage, allow a narrow community, accept only certain event kinds or combine local rules with social trust.
Web of Trust and Proof of Work add two different philosophies. The WoT screen asks for a trust anchor in npub1... format, supports trust depth, refresh interval, immediate refresh, trusted pubkey count and checking whether a pubkey is in the trust set. The code uses @nostr-relay/wot-guard with default seed relays such as wss://relay.damus.io, wss://relay.nostr.band and wss://nos.lol. PoW uses @nostr-relay/pow-guard and a configurable minimum difficulty. WoT says "I trust a social neighborhood." PoW says "I accept events that paid a small computational cost." Neither is magic. Both are useful knobs.
Public access is a serious switch
The README advertises instant public access, and the source shows how that works. The proxy connector talks to wss://proxy.nostr-relay.app/register. When you connect, the app generates or loads a private key for proxy authentication, signs an auth event with nostr-tools, sends relay information as the event content, and receives a public address if the proxy accepts the registration. The renderer then shows connection status, lets you connect or disconnect, and gives you a copyable public address.
That is a powerful idea. It means a desktop relay can be reachable without asking every user to understand NAT, TLS, reverse proxies and router configuration on the first day. For experimentation, workshops, small communities and people who simply want to feel how relay hosting works, this is exactly the kind of product move that makes the protocol less abstract.
It is also where you should slow down. The proxy key is generated by the app and stored in the app config; the source includes a comment saying it may need encryption. That key is not your personal Nostr posting key, but it is still a local secret used for proxy registration. More importantly, public access changes the threat model. A local relay used by your own clients is one thing. A public relay can receive traffic from strangers, fill storage, trigger moderation questions and expose your machine to operational mistakes.
So read the proxy switch as a real switch, not a toy. Turn it on when you know why you want a public address. Keep it off when you only need a local cache, a workshop demo or a private test relay. Nostr Relay Tray makes public access easy enough to try, but it cannot decide your policy, your risk tolerance or your data retention plan for you.
Data work, not just server work
The data section is one of the best signs that Nostr Relay Tray understands what a relay actually is. It includes export events, import events, delete events by filter and clear all events. The main service implements export as JSONL and import by reading JSONL line by line, validating each event and upserting it into the repository. That is unglamorous, and it is exactly what you want when the app is storing protocol data on your desktop.
Why does that matter? Because a personal relay is only useful if you can reason about what it remembers. You may want to back up events. You may want to move a dataset. You may want to clear the relay after a test. You may want to delete all events matching a filter instead of wiping everything. A good relay tool gives you those data-handling paths without forcing you into direct SQLite surgery.
The event statistics by kind also help. A relay full of text notes behaves differently from a relay full of metadata, reactions, relay lists or long-form events. If you are using the app as a personal archive, statistics show what kind of archive you are actually building. If you are using it as a community relay, the same counts can reveal whether the relay is drifting away from the community's purpose.
This is where Nostr Relay Tray earns its place next to more visible clients. Clients show you what Nostr feels like. Relay tools show you what Nostr costs. Disk space, filters, search indexes, payload limits, event limits, data export, validation and abuse handling are not glamorous, but they are the difference between a slogan and a working network.
How I would run it
Start locally. Install the release for your platform, read the unsigned-app warning, and launch it without turning on public proxy access. Point a Nostr client at ws://localhost:4869 or the reachable local-network address if that is what you intend. Publish a few harmless events, query them back and watch the event count change. Then open the root HTTP endpoint and confirm the relay info. You are not trying to win uptime awards yet. You are trying to understand the surface.
Next, make one rule that is easy to verify. For example, allow only a narrow event kind, block a content pattern or limit writes to a known author. Do not begin with a complicated ideology of moderation. Begin with one rule and prove that it behaves the way you think. Then test WoT separately. Give it a trust anchor, refresh the trusted set, and check a pubkey. Then test PoW separately by setting a minimum difficulty and seeing whether clients can still publish. Each control should teach you something observable.
After that, test the data tools. Export a small event set to JSONL, clear the relay, import it again and confirm the count returns. If delete-by-filter is part of your intended workflow, test it on a disposable dataset before you trust it on a real archive. Relay maintenance is not heroic. It is mostly remembering that data you cannot restore is data you do not really control.
Only then try the public proxy. Treat the returned public address as a public service, even if the project makes it feel casual. Watch whether strangers can write, whether your rules are strict enough, whether storage grows, and whether your machine and network can tolerate the traffic. Nostr Relay Tray is a generous tool because it lets a normal person touch relay operation. That generosity works best when you bring grown-up habits to the tray icon.
Sources worth opening
This article keeps claims close to the public project trail, release page and source code. Start here when you want to verify how the desktop relay actually works.
- Nostr Relay Tray GitHub repository
- Nostr Relay Tray releases
- NostrApps listing for Nostr Relay Tray
- Nostr Relay Tray README
- Nostr Relay Tray package.json
- Nostr Relay Tray Electron Builder config
- Relay service source
- Guard service source
- Restriction guard source
- Proxy connector source
- Auto-launch service source
- Home statistics UI source
- Data tools UI source
- Proxy UI source
- Max payload setting source
- Default filter limit setting source
- WoT and PoW settings source
- Rules editor source
- CodyTseng nostr-relay framework
- nostr-relay framework README
- NIP-01 basic protocol flow
- NIP-11 relay information document
- NIP-50 search capability
- NIP-42 relay authentication
- NIP-86 relay management API





