refactor(quartz, geode): derive NIP-86 publicUrl from relay.url; drop isEnabled branch

Two simplifications:

1. Derive admin URL from RelayEngine.url. NIP-86 spec mandates that
   the admin endpoint is "the same URI as ws(s)://, called via
   http(s)://" — so KtorRelay derives the NIP-98 binding URL by
   calling relay.url.toHttp() instead of accepting publicUrl as a
   separate config. Single source of truth (info.relay_url),
   accidental misconfiguration impossible, no Host-header fallback.
   StaticConfig.AdminSection.public_url is gone.

2. Uniform code path for admin-enabled vs disabled. Empty allow-list
   isn't a special case anywhere: Nip86Server.isAuthorized returns
   false for everyone, dispatch rejects, Nip86HttpHandler returns
   NotAdmin → 403. KtorRelay always assembles the Nip86HttpRoute
   and registers the POST endpoint; Nip86Server.isEnabled() and the
   "is admin on?" branches in the handler and route are deleted.

Nip86EndToEndTest's admin-disabled case is now a behavioral test:
sign a valid token, expect 403 NotAdmin (was 405 with the no-route
variant, was 403 with the fake-disabled-route variant).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vitor Pamplona
2026-05-12 18:19:24 -04:00
parent a53b3339b3
commit 445fbf8cda
10 changed files with 192 additions and 129 deletions
+6 -7
View File
@@ -51,7 +51,7 @@ file = "/var/lib/geode/events.db"
# recommended for any relay accepting traffic from real clients.
# Verify Schnorr signatures on every EVENT. Default: true. Disable
# only for trusted-input scenarios (test fixtures, mirror replays).
# verify_signatures = true
verify_signatures = true
# Run signature verification in parallel inside the IngestQueue
# (across all CPU cores) instead of serially on each connection's
@@ -80,13 +80,12 @@ require_auth = false
# authenticated with NIP-98 HTTP-Auth. Only events signed by one of
# the listed pubkeys can run admin RPCs (banpubkey / banevent /
# changerelayname / …). Empty (the default) disables the endpoint.
# pubkeys = ["abcdef...64hex..."]
#
# Canonical URL the relay is reachable at, e.g. behind a reverse proxy.
# NIP-98 binds requests to this URL via the `u` tag. **Required** in
# any production deployment — without it, an attacker can spoof the
# Host header to bypass URL binding.
# public_url = "https://relay.example.com/"
# NIP-98 binds admin tokens to the relay's HTTP URL, which is derived
# from [info].relay_url with the scheme swapped (ws -> http, wss ->
# https) per NIP-86. Make sure [info].relay_url is set to the
# canonical public URL when behind TLS termination or a reverse proxy.
# pubkeys = ["abcdef...64hex..."]
# Path for the JSON snapshot that persists NIP-86 admin state (ban
# lists + the live NIP-11 doc) across restarts. When unset, admin