## Summary - Supersedes #1913 with a KLIPY-hosted URL implementation. - Adds KLIPY GIF search and trending results to desktop message and forum composers. - Keeps selected GIFs hosted by KLIPY; Buzz stores only the external URL and media metadata (no imeta tag, since relays only accept hash-backed local `/media/` entries). - Aligns the Emoji/GIF picker with Buzz's standard segmented control, theme surfaces, and motion behavior. ## Relay-to-provider boundary - The relay proxies KLIPY search/share so the `BUZZ_KLIPY_API_KEY` never reaches the desktop; the key stays server-side behind a redacted `Debug` impl. - The dedicated GIF `reqwest` client sets `redirect::Policy::none()`. Because the API key rides in the request path, following a provider `3xx` could replay a key-bearing URL to an attacker-chosen host (an SSRF/key-disclosure primitive). With redirects disabled, a `3xx` returns as a non-success status that the handlers map to a generic `502`; the `Location` target is never read or forwarded. - Admission reuses the established NIP-98, membership, replay, and per-pubkey rate-limit gates, with an upstream response-size cap and allowlisting so KLIPY error bodies never cross the relay boundary. ## Accessibility - Under `prefers-reduced-motion: reduce`, the picker grid renders a static provider poster (a normalized `jpg` asset) instead of the animated preview, or a named static placeholder when no poster is available. It reacts to preference changes while mounted. `no-preference` keeps the animated preview. - Selected GIFs carry their title through `ImetaMedia`'s `displayLabel`, so the composer thumbnail, preview dialog, editor, lightbox, and remove control all derive one non-empty accessible name instead of an empty `Attachment ` label. Ordinary hashed uploads keep their existing hash-derived names. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
1.7 KiB
Relay-proxied GIF search
Buzz relays can optionally provide GIF search without distributing a provider
credential to desktop clients. An operator configures BUZZ_KLIPY_API_KEY in
the relay's secret store. When present, the relay advertises this NIP-11 shape:
{
"supported_extensions": ["buzz-gif"],
"gif": {
"provider": "klipy",
"search": "/gifs/search",
"share": "/gifs/share"
}
}
The descriptor is provider-agnostic so a relay can advertise another provider
or paths later. Clients must require buzz-gif, recognize the provider, and use
only safe relay-relative search and share paths.
POST /gifs/search requires NIP-98 authentication and relay membership. Its
per-pubkey, per-community Redis admission limit defaults to 30 requests per
minute and can be tuned with BUZZ_RATE_LIMIT_GIF_SEARCHES_PER_MIN. The relay
sends the provider credential upstream and returns only allowlisted successful
result data. Provider error bodies are never returned to clients or written to
logs.
POST /gifs/share uses the same authentication and membership boundary. It
accepts a bounded GIF slug plus anonymous customer ID and forwards KLIPY's share
signal so selected media can enter the user's provider-backed Recents. The
endpoint returns no provider body.
Message and rendering boundary
Selecting a GIF sends a normal Buzz message containing a KLIPY CDN image URL.
Because Buzz's imeta validator permits only hash-verified local /media/ paths,
external GIFs are deliberately content-only and carry no imeta tag. Buzz does
not download, cache, or store the GIF bytes. Existing image URL rendering
handles the message, including pasted GIF URLs on relays that do not advertise
buzz-gif. Only the picker is capability-gated.