docs: update multi-tenant spec search obligation to Postgres FTS

The #1285 conformance checklist and formal spec described the search
isolation obligation against the Typesense backend that no longer exists. Keep
the obligation intact (search query carries community_id; results never cross
tenants; refetch is (community_id, event_id)) and restate the mechanism in
terms of the Postgres FTS implementation #1321 ships: the events.search_tsv
generated tsvector column, GIN index, community_id filter BitmapAnd-ed with the
@@ probe, and ChannelScope::ChannelLessOnly in place of the __global__ sentinel.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
This commit is contained in:
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d
2026-06-27 12:04:11 -04:00
parent 2c6b1b925c
commit 84de626963
2 changed files with 9 additions and 8 deletions

View File

@@ -950,13 +950,14 @@ The model's obligations map to concrete code seams:
shared atomic seen-set keyed by community and NIP-98 event id, or an explicitly
admitted sticky/single-replica deployment; otherwise S2's freshness premise is
not carried in production.
- **Search / C2.1** — Typesense is shared infrastructure, not a shared result
space. Indexed documents carry `community_id`; document ids are collision-safe
across communities (for example `community_id:event_id`) or every upsert/delete
path includes a community filter. All query `filter_by` strings include
`community_id`, and Postgres refetch by hit id is `(community_id, event_id)`.
The existing `__global__` channel sentinel means channel-less within one
community, never operator-global.
- **Search / C2.1** — the Postgres FTS index (the `events.search_tsv` generated
column, backed by a GIN index) is shared infrastructure, not a shared result
space. Searchable rows carry `community_id`, and every search query filters by
`community_id` so the FTS predicate is BitmapAnd-ed with the community-leading
btree filters; a hit never crosses tenants and refetch by hit id is
`(community_id, event_id)`. The channel-less scope (`ChannelScope::ChannelLessOnly`,
formerly the `__global__` sentinel) means channel-less within one community,
never operator-global.
- **Redis / subscription refinement** — Redis pub/sub keys, presence keys, typing
keys, cache invalidation channels, and local-echo dedup labels include
community context in any shared multi-tenant deployment. The safe shape is