Fast, lightweight Nostr relay with spider mode that syncs notes from people you follow.
  • Zig 80.8%
  • Shell 14.3%
  • Nix 4.2%
  • Python 0.4%
  • Dockerfile 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-17 13:33:08 -04:00
.github Bump the CI Nix installer to 2.35.3 (#208) 2026-10-17 13:33:08 -04:00
assets Update wisp-logo.jpeg 2025-12-09 12:58:29 -05:00
benchmark Add query performance test to CI benchmark 2025-12-14 17:21:57 -05:00
docs Rate limit spider events that only mention a follow (#196) 2026-10-06 13:21:53 -04:00
nix Release v0.9.1 (#205) 2026-10-06 15:10:58 -04:00
scripts Catch stale Nix entries and vendored http.zig mode or symlink drift (#197) 2026-10-06 13:21:30 -04:00
src Release v0.9.1 (#205) 2026-10-06 15:10:58 -04:00
tests Serve gift wraps only to their authenticated recipients (#195) 2026-10-06 13:52:18 -04:00
vendor Harden vendored httpz connection lifecycle error paths (#200) 2026-10-06 13:27:33 -04:00
.dockerignore Add Docker support (#57) 2026-03-22 10:42:56 -04:00
.gitignore Nix packaging: flake + wisp package + services.wisp module (#139) 2026-07-06 14:41:09 -04:00
book.toml Add mdBook docs for wisp portal (#65) 2026-06-11 21:42:01 -04:00
build.zig Harden vendored httpz connection lifecycle error paths (#200) 2026-10-06 13:27:33 -04:00
build.zig.zon Release v0.9.1 (#205) 2026-10-06 15:10:58 -04:00
CHANGELOG.md Release v0.9.1 (#205) 2026-10-06 15:10:58 -04:00
CONTRIBUTING.md Catch stale Nix entries and vendored http.zig mode or symlink drift (#197) 2026-10-06 13:21:30 -04:00
Dockerfile Build release binaries and the Docker image for the baseline CPU (#204) 2026-10-06 15:00:43 -04:00
flake.lock Build with Zig 0.17 (#192) 2026-10-06 10:06:47 -04:00
flake.nix Nix packaging: flake + wisp package + services.wisp module (#139) 2026-07-06 14:41:09 -04:00
LICENSE Switch license from AGPL-3.0 to MIT (#53) 2026-03-04 21:38:42 -05:00
README.md Build with Zig 0.17 (#192) 2026-10-06 10:06:47 -04:00
wisp.toml.example Rate limit spider events that only mention a follow (#196) 2026-10-06 13:21:53 -04:00

wisp - a lightweight nostr relay

Wisp Logo

A fast, lightweight nostr relay written in Zig.

Why Wisp?

  • Fast: ~9x higher throughput than strfry, sub-millisecond p99 latency
  • Small: Single binary, ~11MB RAM at idle
  • Simple: One command to run your personal relay with your feed
  • Spider Mode: Automatically syncs events from people you follow

Quickstart

Docker

docker run -d --restart unless-stopped -p 7777:7777 -v wisp-data:/data ghcr.io/privkeyio/wisp --spider-admin npub1yourkey...

Build from source

Download the latest release or build from source:

# 1. Install dependencies (requires Zig 0.17.0)
sudo apt install -y liblmdb-dev libsecp256k1-dev libssl-dev

# 2. Build
git clone https://github.com/privkeyio/wisp && cd wisp
zig build -Doptimize=ReleaseFast

# 3. Run (replace with your npub)
./zig-out/bin/wisp --spider-admin npub1yourkey...

That's it. Wisp will fetch your follow list, sync notes from popular relays, and serve them at ws://localhost:7777. Add this relay URL to your Nostr client.

For more options, see wisp.toml.example.

Features

  • NIPs: 1, 2, 9, 11, 13, 16, 33, 40, 42, 45, 50, 51, 65, 70, 77, 86
  • LMDB storage (no external database)
  • Spider mode for syncing events from external relays
  • Import/export to JSONL
  • Prometheus metrics at GET /metrics

Marmot/MLS compatible: Wisp's generic NIP-16/NIP-33 handling stores and serves kind 10050 (DM/inbox relays, replaceable), 10051 (KeyPackage relays, replaceable), 30443 (KeyPackage, addressable) / legacy 443, and 1059 gift wraps, so Haven, WhiteNoise, and other Marmot clients work without extra setup. Gift wraps are stored and served like any other event; Wisp does not implement the NIP-17 recipient-only serving rule, so operators wanting to restrict who can read kind 1059 should enable AUTH (auth_required).

Monitoring

Operational metrics are exposed in Prometheus format at GET /metrics on the relay's port (connections, events stored/rejected/broadcast, REQ count, rate limiting). The endpoint honors the relay's IP allowlist/blocklist, so restrict it there or at your reverse proxy/firewall if you don't want it public.

Import/Export

./zig-out/bin/wisp export > backup.jsonl
./zig-out/bin/wisp import < backup.jsonl

Configuration

Copy wisp.toml.example to wisp.toml and customize, or use environment variables with the WISP_ prefix:

WISP_PORT=8080 ./zig-out/bin/wisp

Environment variables override the config file. See the Configuration reference for every setting, its environment variable, default, and meaning.

Deploy (wss://)

To make your relay publicly accessible with TLS, run wisp with Caddy:

# Run wisp
docker run -d --restart always -p 7777:7777 -v wisp-data:/data \
  ghcr.io/privkeyio/wisp --spider-admin npub1yourkey...

# Install Caddy for automatic TLS
sudo apt install -y caddy

Create /etc/caddy/Caddyfile:

relay.yourdomain.com {
    reverse_proxy localhost:7777
}
sudo systemctl restart caddy

Your relay is now live at wss://relay.yourdomain.com.

Benchmarks

Peak write throughput, all relays at 100% delivery:

Relay Events/sec p99 Latency Memory (RSS)
Wisp 25,600 0.28 ms 11 MB
nostr-rs-relay 5,400 5.9 ms 20 MB
strfry 2,800 2.9 ms 3 MB

nostr-bench, 5,000 events x 4 workers at peak rate (--rate 0), native release builds on a 16-core Linux host, with the event-rate limit raised. RssAnon sampled during the run. These are peak figures with Wisp in sync = none (non-durable); Wisp's default is sync = meta (durable, never corrupts), which trades raw throughput for crash safety. strfry and nostr-rs-relay write durably by default, so a fair durable comparison runs Wisp in meta/full (see benchmarks). Wisp leads on throughput (~9x strfry, ~5x nostr-rs-relay) and p99 latency; strfry stays smallest in memory.

Contributing

See CONTRIBUTING.md for guidelines on reporting issues and submitting pull requests.

Changelog

See CHANGELOG.md for a history of changes to this project.

License

MIT