fix(relay): harden admission (fail-safe, loopback-only, bounded, size cap) [review]

Adversarial review of the relay admission surface:

- FAIL-SAFE on admission death: nostr-rs-relay fails OPEN if its gRPC admission
  server is unreachable, so a dead admission = an unrestricted relay. The backend
  now EXITS if the admission service stops (supervisor restarts a clean pair);
  documented in RELAY.md. Pin the relay image to a digest (compose note).
- Registration-oracle protection: validate() now refuses a non-loopback
  RELAY_ADMISSION_BIND unless RELAY_ADMISSION_ALLOW_PUBLIC=true (+ test).
- No N+1 on crafted events: recipient p-tags are capped (20, deduped) and resolved
  in ONE batched query (db.any_registered_npub) instead of one-per-tag.
- Defence-in-depth size cap: admission denies events over max_event_bytes before
  any DB work (the relay enforces its own limit too).
- Docs: RELAY.md Security section (fail-open, loopback oracle, default PoW=0 spam
  footgun, client sender-verification); config.toml relay port 8088 (not 8080);
  Docker Hub image; .env.example RELAY_ADMISSION_ALLOW_PUBLIC.

Note: the client-side sender-impersonation blocker is fixed separately in the
monorepo (verifying gift-wrap unwrap).
This commit is contained in:
jwinterm
2026-07-02 08:35:37 -04:00
parent 17bb7bcc20
commit 635cb64601
9 changed files with 133 additions and 17 deletions
+2 -1
View File
@@ -27,7 +27,8 @@ event_admission_server = "http://127.0.0.1:8090"
[network]
address = "0.0.0.0"
port = 8080
# NOT 8080 — the Smirk backend uses that. Front this with your TLS terminator.
port = 8088
# Conservative, self-host-friendly resource ceilings (the anti-abuse floor even
# for the `open` policy). Tune to your box.
+3 -1
View File
@@ -16,7 +16,9 @@
# version you pin (https://github.com/scsibug/nostr-rs-relay).
services:
relay:
image: ghcr.io/scsibug/nostr-rs-relay:latest # pin a digest in production
# SECURITY: pin a digest in production — the relay's admission fail-open
# behavior can change across versions (see docs/operations/RELAY.md).
image: scsibug/nostr-rs-relay:latest
restart: unless-stopped
network_mode: host # reach the backend admission socket on 127.0.0.1
user: "1000:1000"