aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-02 17:51:37 -0700
committerAdam Soltys <[email protected]>2026-08-02 17:51:37 -0700
commit1f1440d706d44181945d779547fdcb7668bab6b1 (patch)
tree36b43c3f81cf2ffae20c5a8a245a6055190177a5
parentadab07119409c813ef54d1e00929bb0704c1ce38 (diff)
downloadcoinosv3-server-1f1440d706d44181945d779547fdcb7668bab6b1.tar.gz
coinosv3-server-1f1440d706d44181945d779547fdcb7668bab6b1.zip
Recipient search: usernames and npubs, in chat and the Send form
A shared engine (src/recipient-search.js) merges three ranked sources: an exact npub/hex paste, coinos usernames from the registrar's new /search prefix endpoint, and NIP-50 kind-0 full-text search on relay.nostr.band — the approach the major web clients take, since ordinary relays don't index profiles. Results dedupe by pubkey, fill avatars in one batched kind-0 fetch, and cache per query so narrowing never re-hits the network. Input is debounced 300ms and gated by a searchable() heuristic so addresses, invoices, and keys never trigger a search. Chat's New message panel narrows as you type and opens the thread on pick; the Send form shows the same candidates under the destination input and picking one runs the normal paste path (payment address → BIP-353, npub → zap). Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
-rw-r--r--names/server.js14
1 files changed, 14 insertions, 0 deletions
diff --git a/names/server.js b/names/server.js
index 8a0cab3..d9aa299 100644
--- a/names/server.js
+++ b/names/server.js
@@ -419,6 +419,20 @@ Bun.serve({
});
}
+ // Prefix search over registered names — powers recipient search in the
+ // wallet's DMs. Public data (every name is a public DNS record).
+ if (url.pathname === '/search' && req.method === 'GET') {
+ if (!rateOk(ip, 60)) return json({ error: 'rate limited' }, 429);
+ const q = (url.searchParams.get('q') || '').toLowerCase().trim();
+ if (q.length < 2 || q.length > 30) return json({ results: [] });
+ const results = Object.entries(state.names)
+ .filter(([key]) => key.split('@')[0].startsWith(q))
+ .sort(([a], [b]) => a.length - b.length || (a < b ? -1 : 1))
+ .slice(0, 10)
+ .map(([key, r]) => ({ address: key, name: key.split('@')[0], pubkey: r.pubkey }));
+ return json({ results });
+ }
+
// Availability / lookup. Public data (it's DNS).
const m = url.pathname.match(/^\/name\/([a-z0-9._-]{1,30})$/);
if (m && req.method === 'GET') {