diff --git a/.env.example b/.env.example index 7839b01..a41c0ea 100644 --- a/.env.example +++ b/.env.example @@ -19,6 +19,13 @@ FLOONET_BASE_URL=https://floonet.example # at your own wss:// URL (normally wss://FLOONET_DOMAIN). FLOONET_RELAYS=wss://floonet.example +# Co-located names (FLOONET_AUTHORITY_COLOCATED): this compose stack is single +# domain, so names AND the relay are already served on FLOONET_DOMAIN +# (`name@FLOONET_DOMAIN` resolves) — on by default, nothing to set here. Only a +# SPLIT deploy that puts the relay and the authority on separate subdomains +# behind nginx needs to opt in; see "Co-locating names on the relay domain" in +# the README and deploy/us-east/colocated-authority.conf. + # --- The kind whitelist (the keystone) --- # Comma-separated event kinds the relay stores. DEFAULT-DENY: anything not diff --git a/README.md b/README.md index aaed913..4be3da5 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,35 @@ NIP-98 requests are verified fully: signature, kind 27235, `u`/`method`/ `payload` tags against `FLOONET_BASE_URL`, a freshness window, and one-time event ids (replay rejection). +## Co-locating names on the relay domain + +`FLOONET_AUTHORITY_COLOCATED` controls whether the authority's NIP-05 lookup +(`/.well-known/nostr.json`) is served on the **relay's own domain**, so +`name@relay.example` resolves without the authority needing its own hostname. + +- **Docker Compose / Caddy: on by default.** The whole stack lives on one + `FLOONET_DOMAIN`; `deploy/Caddyfile` routes `/.well-known/nostr.json` (and + `/api/*`) to the authority and everything else to the relay, so + `name@FLOONET_DOMAIN` just works. Nothing to configure. + +- **Split nginx deploy: opt in.** When the relay and the authority run on + separate subdomains (the `deploy/us-east/` pattern — relay on + `relay.example`, the authority's own vhost on `nm.example`), enable it by + including the shipped snippet in the relay vhost's `:443` server block, + ahead of the WebSocket catch-all: + + ```nginx + # inside server { listen ...:443 ssl ...; server_name relay.example; } + # BEFORE location / { ...websocket... } + include /etc/nginx/snippets/floonet-colocated-authority.conf; # deploy/us-east/colocated-authority.conf + ``` + + Then `nginx -t && nginx -s reload`, and + `https://relay.example/.well-known/nostr.json?name=` returns the + authority's JSON. Only the exact-match read path is co-located; registration + and the rest of `/api/*` stay on the authority's own domain. The snippet sets + `X-Real-IP` (load-bearing — the authority's per-IP rate limiter keys off it). + ## Mixnet exit (optional) Uncomment `COMPOSE_PROFILES=exit` in `.env` and the package also runs diff --git a/deploy/Caddyfile b/deploy/Caddyfile index 4a4a7a4..01b7888 100644 --- a/deploy/Caddyfile +++ b/deploy/Caddyfile @@ -17,9 +17,12 @@ # forwardable client header. # NIP-05 resolution and the registration/paid API go to the authority. - # gzip is scoped here (HTTP/JSON) and deliberately NOT applied to the - # relay path: strfry already negotiates permessage-deflate on the - # WebSocket. + # This single-domain stack is co-located by design: names AND the relay + # answer on FLOONET_DOMAIN (the FLOONET_AUTHORITY_COLOCATED=on default; + # a split nginx deploy opts in via deploy/us-east/colocated-authority.conf + # instead — see the README). gzip is scoped here (HTTP/JSON) and + # deliberately NOT applied to the relay path: strfry already negotiates + # permessage-deflate on the WebSocket. @authority { path /.well-known/nostr.json /.well-known/nostr.json/* /api/* } diff --git a/deploy/us-east/colocated-authority.conf b/deploy/us-east/colocated-authority.conf new file mode 100644 index 0000000..1022fef --- /dev/null +++ b/deploy/us-east/colocated-authority.conf @@ -0,0 +1,30 @@ +# Co-located Floonet name authority — the FLOONET_AUTHORITY_COLOCATED=on toggle. +# +# Serve NIP-05 names on the RELAY's own domain (so `name@relay.example` +# resolves) WITHOUT giving the authority its own vhost/cert. This is only +# needed for a split deploy where the relay and the authority live on separate +# subdomains behind nginx (the deploy/us-east/ pattern: relay.floonet.dev + +# nm.floonet.dev). The Docker Compose / Caddy stack is already co-located on a +# single FLOONET_DOMAIN, so it does not need this file. +# +# ENABLE (== FLOONET_AUTHORITY_COLOCATED=on): include this inside the relay's +# `:443` server block, BEFORE its `location /` WebSocket catch-all, e.g. +# +# include /etc/nginx/snippets/floonet-colocated-authority.conf; +# +# then `nginx -t && nginx -s reload`. DISABLE by removing the include. +# +# Only the exact-match READ lookup is exposed; registration and the rest of +# /api/* stay on the authority's own domain. The `location =` exact match wins +# over the relay catch-all regardless of file order, but keep it above +# `location /` for readability. +# +# Port 8193 is this box's authority bind (FLOONET_NAMES_BIND); the compose +# stack uses 8191 — match your own. X-Real-IP is SECURITY-CRITICAL: the +# authority keys ALL per-IP rate limiting off it, so a missing value collapses +# every client into one bucket and defeats the limiter. +location = /.well-known/nostr.json { + proxy_pass http://127.0.0.1:8193; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; +}