~/side $ ./fastr // goes_brrrrrrrr @ 148k ev/s

FASTR

a Nostr relay that goes brrrrrrrr, written in Odin

148,251 events/s ingest O(limit) queries, not O(n) 130 MB RSS @ 500k events 267 package tests

[01] WHAT IT IS

A relay has one job: move events. fastr does that job at 148 thousand events per second on an 8-core box, and makes every other relay reconsider its life choices.

It's a Nostr relay written in Odin. Events live in BASED, a binary append-only store built for this exact workload; queries read straight off the mmap and stop the moment the answer is complete. Your relay is slow. Your memory usage is embarrassing. You already knew this in your heart.

the supported NIPs:

[02] BENCHMARKS

500k events, 8 CPU / 8 GB RAM, 50k queries, measured 2026-06-10. fastr vs strfry:

metricfastrstrfrydiff
ingest throughput (ev/s)148,2512,37862×
ingest OK p50 latency (µs)483,08164×
ingest OK p99 latency (µs)806,80185×
REQ query throughput (q/s)58,8686,622
REQ→EOSE p50 latency (µs)1281,107
REQ→EOSE p99 latency (µs)1812,22012×
peak RSS @ 500k events130 MB548 MB
disk usage @ 500k events119 MB526 MB
disk I/O written119 MB76,052 MB639× ← not a typo
CPU Mcycles (user)110,124265,301
CPU Mcycles (kernel)20,439777,89238×
syscalls3,761,07776,390,18320×
context switches1,105,06317,609,46816×
cold start (µs)13,12123,607

strfry wrote 83 GB of journal just to ingest 500k immutable events. fastr wrote 119 MB. The whole harness ships in the repo; reproduce it with docker build -f Dockerfile.bench -t fastr-bench . && docker run --rm --privileged fastr-bench.

[03] ENGINEERING

bytes all the way down

BASED (Binary Append Store for Event Data): id, pubkey and sig stored as raw bytes, 64-char hex strings packed to 32. An event never becomes a struct; it transcodes to JSON wire format in one pass with zero heap allocations.

O(limit) queries

Index records are scanned newest-first with cheapest-first filter gates, fields read straight from the mmap. A prefix-max of created_at stops the scan once nothing older can crack the top-N, and exact-id lookups skip the scan entirely.

writes are two appends

Append the blob, append a 92-byte index record. No B-tree to rebalance, no journal to replay. That's how 639× less disk I/O happens.

one TCP write per REQ

Every matching event is encoded into a single batch with EOSE appended, handed to the write task as one message. N events, one syscall.

fanout that never waits

Live events try_send to subscribers. A slow client drops events; it never stalls the writer or the rest of the relay.

background compaction

Every 6 hours, once tombstones pass 1,000, a background task rebuilds the files. Deletes, expirations and vanish requests cost nothing until then.

[04] RUN IT

# one-time: clone + build static libsecp256k1
$ just vendor

# build -> ./fastr
$ just build

# serve on 0.0.0.0:8080
$ ./fastr

# bulk import; bare events or ["EVENT", {...}]
# envelopes, because the world is chaotic
$ ./fastr import ./data events.jsonl

# deploy: auto-detects systemd, OpenRC, runit,
# FreeBSD rc.d or launchd
$ sudo ./deploy/install.sh

configuration: env vars

FASTR_PORT8080listen port
FASTR_ADDR0.0.0.0bind address
FASTR_DATA_DIR./datadata directory
FASTR_MAX_CONNECTIONS1024max websocket conns
FASTR_MAX_SUBSCRIPTIONS20subs per connection
FASTR_MAX_FILTERS10filters per sub
FASTR_MAX_LIMIT500max events per REQ
FASTR_MAX_MESSAGE_BYTES131072max message size
FASTR_COMPACT_INTERVAL21600compaction interval (s)

[05] SOURCE

one package per directory

  • pack/the BASED format: encode, decode, JSON transcode
  • nostr/event validation, filters
  • store/storage engine: blobs, index, tags, compaction
  • ws/WebSocket server + message handlers
  • negentropy/NIP-77 set reconciliation
  • secp256k1/bindings to the vendored static lib
  • cmd/config, NIP-11 endpoint, the binary
  • bench/ qbench/ smoke/load generators + end-to-end checks

honest footnote

strfry is respectable, battle-tested software with years of production scars. If you want safe and proven, run strfry. If you want speed, run fastr. This is young alpha software, expect bugs; the "three years on the biggest relays" achievement is still loading. But the numbers are real. Beat them by 25% with the same features and the project gets renamed to slowstr. Not even joking. AGPLv3.

READ THE CODE ⇗