Add NIP-05 identifier server for otherwhere.app

Dynamic lookup from nostr-rs-relay SQLite DB; runs as nostr-rs-relay user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
randogoth 2026-06-08 15:13:13 +03:00
parent 288ce840ad
commit d4b9b209c7
2 changed files with 100 additions and 0 deletions

View file

@ -0,0 +1,99 @@
{ pkgs, lib, ... }:
let
domains = [ "otherwhere.app" "www.otherwhere.app" ];
port = 8892;
relayUrl = "wss://relay.otherwhere.app/";
dbPath = "/var/lib/nostr-rs-relay/nostr.db";
script = pkgs.writeText "nip05-server.py" ''
import json, sqlite3
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
DB_PATH = "${dbPath}"
RELAY_URL = "${relayUrl}"
PORT = ${toString port}
class NIP05Handler(BaseHTTPRequestHandler):
def do_GET(self):
parsed = urlparse(self.path)
if parsed.path != "/.well-known/nostr.json":
self.send_response(404)
self.end_headers()
return
params = parse_qs(parsed.query)
name_filter = params.get("name", [None])[0]
if name_filter:
name_filter = name_filter.strip().lower()
try:
con = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
rows = con.execute("""
SELECT pub_key, content FROM event
WHERE kind = 0 AND (hidden IS NULL OR hidden = 0)
GROUP BY pub_key
HAVING created_at = MAX(created_at)
""").fetchall()
con.close()
except Exception:
self.send_response(500)
self.end_headers()
return
names = {}
relays = {}
for pub_key, content in rows:
try:
meta = json.loads(content)
name = meta.get("name", "").strip().lower()
if not name:
continue
if name_filter and name != name_filter:
continue
names[name] = pub_key
relays[pub_key] = [RELAY_URL]
except (json.JSONDecodeError, AttributeError):
continue
body = json.dumps({"names": names, "relays": relays}).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, fmt, *args):
pass
HTTPServer(("127.0.0.1", PORT), NIP05Handler).serve_forever()
'';
in
{
systemd.services.nip05 = {
description = "NIP-05 identifier server";
after = [ "network.target" "nostr-rs-relay.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
User = "nostr-rs-relay";
Group = "nostr-rs-relay";
ExecStart = "${pkgs.python3}/bin/python3 ${script}";
Restart = "on-failure";
RestartSec = 5;
};
};
environment.etc."caddy/Caddyfile.d/nip05.caddyfile".text = ''
${lib.concatStringsSep ", " domains} {
handle /.well-known/nostr.json {
reverse_proxy localhost:${toString port}
}
handle {
header Content-Type "text/html; charset=utf-8"
respond `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>otherwhere.app</title></head><body><p>otherwhere.app</p></body></html>` 200
}
}
'';
}