Files
amethyst/commons
nrobi144 e091f6d3d3 feat(commons): DmInboxRelayResolver with strict kind:10050-only fan-out
Three-layer resolver for "where do I publish this NIP-17 gift wrap":

1. LocalCache hit — if the caller already saw the user's kind:10050
   via the regular feed pipeline, skip I/O entirely.
2. In-memory LRU cache — TTL 1h, 100 entries; avoids re-querying
   indexers when opening several conversations in sequence.
3. Indexer fan-out — RecipientRelayFetcher against a curated set
   (DefaultDmIndexerRelays: relay.nos.social, relay.damus.io,
   nos.lol, relay.nostr.band, purplerelay.com — purplepag.es
   deliberately excluded for poor kind:10050 coverage).

Strictness vs. the existing User.dmInboxRelays():
  - filters to kind:10050 ONLY; NEVER falls back to NIP-65 read
    marker (kind:10002). User.dmInboxRelays() silently substitutes
    that, which is the same metadata-leak class fixed by 5293dae65.
  - empty list = canonical "unreachable" signal; caller refuses to
    publish (DesktopIAccount.resolveDmInboxRelaysStrict already
    does this).

Security: the NostrClient passed in MUST be a dedicated
unauthenticated instance — no RelayAuthenticator attached. An
authenticated indexer fan-out (the current state with the primary
client) would extract identity-key signatures during the kind:10050
probe, escalating "indexer learns we want to DM pubkey X" into
"indexer learns user U wants to DM pubkey X". KDoc warning is
explicit; Phase 4 follow-up creates the unauth client in Main.kt
and injects it.

LocalLookup callback is plugged via lambda so CLI / headless
callers (amy) can use this without a Compose LocalCache.

Not yet wired into DesktopIAccount.resolveDmInboxRelaysStrict —
that wire-up is the next commit and converts the sync helper to
suspend, threading through sendNip17* batch construction.
2026-07-09 07:33:22 +03:00
..