Files
buzz/docs
Kalvin C 3d26ed7dfd feat(relay): match verified names in profile search (#9019)
## Summary

`POST /profiles/search` only matches kind:0 `name` and `display_name`.
In communities that publish NIP-FI verified profiles, a person's surname
or directory username often appears only in their relay-signed
kind:30382, so typeahead cannot find them by it.

- Where the community advertises verified profiles, the search also
matches the `display_name`, `username` and `full_name` tags of each
current kind:30382 head signed by the relay key.
- Verified names need a trailing prefix of at least three characters, as
in NIP-50, so a short prefix doesn't make the relay rank every verified
head. kind:0 keeps one-character typeahead. Lookups from heads to
`events` also match `created_at`, so Postgres prunes to one partition.
- Results are paged by person. Each person returns their current kind:0,
when they have one, then their current kind:30382, when they have one,
whichever matched. A person is ranked by their best match across both
sources, using the existing tiers.
- NIP-11 `federated_identity.public_profile` gains `searchable: true`,
so clients can rely on the response instead of a separate kind:30382
read.
- Migration `0081` adds a GIN expression index over the head's name tags
on `nip_fi_profile_heads` (built-in `jsonb_path_query_first` and
`to_tsvector`, no new functions). The table holds one row per verified
person. Like `0078`, the migration builds the index only while the table
is empty; populated databases prebuild it with `CREATE INDEX
CONCURRENTLY` (`docs/profile-search.md`), and the migration then checks
it is valid and has the expected definition.

Compatibility: the request schema is unchanged. Existing clients that
keep only kind:0 events work as before and also find people who match
only by verified name. Trust stays pinned to the relay `self` key; the
event shape follows NIP-85 user assertions, but discovery is
relay-advertised rather than kind:10040.

## Testing

- `buzz-search` Postgres integration: new
`profile_names_match_verified_names` covers forged-key heads, replaced
heads, other communities, people without kind:0, per-person grouping,
paging, and words split across sources; the existing profile-name tests
pass unchanged. The test fails when the author filter is inverted.
- `buzz-relay` end to end in enforce mode:
`profile_search_matches_verified_names_where_advertised` publishes
through the real writer and finds the person by surname only where
verified profiles are advertised.
- Desired-state and migration index definitions match.
- `profile_names_use_verified_name_index` asserts the search scans the
new index. Migration `0081` checked against an empty table, a rerun, a
populated table without the index (raises), the documented concurrent
prebuild, and an index with a different definition (raises).
- Latency, 50k people each with a kind:0 and a verified head, release
build, local Postgres, best of 5, 30 people per page: 11–22 ms with
verified names versus 1–8 ms kind:0 only. `EXPLAIN` shows a bitmap scan
on the new index.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Kalvin Chau <kalvin@block.xyz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 21:25:15 -05:00
..
…