{"id":"dc7afd660687c7a3c008b6afa2db39b050c9e3c1500ab9da4b27b386f8e7df82","pubkey":"daa41bedb68591363bf4407f687cb9789cc543ed024bb77c22d2c84d88f54153","created_at":1782954451,"kind":30817,"tags":[["d","animated-media-reaction-clips"],["title","Animated Media (Reaction Clips)"],["alt","Nostr Implementation Possibility: Animated Media (Reaction Clips)"],["client","NostrHub"]],"content":"`draft` `optional`\n\nDefines a content-addressed event for short animated media (\"GIFs\") and a tagging convention for discovering it, so GIF-style browse and search run on the nostr network layer with no centralized catalog or paid API. Rendering animated media needs no protocol; this NIP is about discovery. Using a clip in a note requires nothing from this NIP (see [Using clips in notes](#using-clips-in-notes)).\n\nStorage, trust, moderation and curation are delegated to existing NIPs (94/95, 85, 32/56, 30). A prior attempt to put discovery on the file server ([nips#1337](https://github.com/nostr-protocol/nips/pull/1337)) was rejected for overloading it; this keeps storage dumb and does discovery with events, NIP-50 and trusted pubkeys.\n\n## The clip event\n\nA clip is a **regular** event (kind `TBD`, in the `1000`–`9999` range; this draft uses `1090`). It is immutable and permanent.\n\nThe media is identified by the `x` sha256 of its bytes, carried in `imeta` (NIP-92). Identical bytes from different publishers share one hash; clients dedup by hash and aggregate signal across copies (see [Spam and trust](#spam-and-trust)).\n\n```jsonc\n{\n  \"kind\": 1090,\n  \"content\": \"Optional caption (indexed by NIP-50).\",\n  \"tags\": [\n    [\"imeta\",\n      \"url https://blossom.example/<sha256>.webp\",\n      \"m image/webp\",\n      \"x <sha256-hex of the media>\",\n      \"dim 480x270\",\n      \"dur 2.4\",\n      \"image https://blossom.example/<sha256>-poster.webp\",\n      \"alt person covering their face in disbelief\"\n    ],\n    [\"x\", \"<sha256-hex of the media>\"],\n    [\"L\", \"emotion\"],\n    [\"l\", \"disbelief\", \"emotion\"],\n    [\"l\", \"facepalm\", \"emotion\"],\n    [\"t\", \"this is fine\"]\n  ]\n}\n```\n\n- REQUIRED: an `imeta` with `url` (or embedded ref, see [Storage](#storage)), `m`,`x`, `dim`; a top-level `x` tag mirroring the media hash (so relays can index it); at least one emotion `l`.\n- RECOMMENDED: `image` (poster frame), `alt`.\n- OPTIONAL: `dur`, `blurhash`, `size`, more `l` facets (capped at 6), `t` descriptors, `content`.\n\nMimetype SHOULD be `image/webp`, `video/mp4` or `image/gif`.\n\n**Why regular, not addressable?** An earlier draft used an addressable event keyed by the media hash so metadata stayed editable. But this spec's authoritative labeling is emergent from engagement aggregated across all events for a hash, not any single author's editable record, so replaceable semantics bought nothing the model uses. Corrections are a new event plus a NIP-09 delete. The one property addressability provided for free is recovered at read time; see [Spam and trust](#spam-and-trust).\n\n## Storage\n\nIdentity is always the `x` sha256, so either model works and clients need not care which:\n\n- **Referenced** (default): bytes behind a Blossom / NIP-94 URL in `imeta url`. Use for anything above sticker size.\n- **Embedded**: bytes stored in-relay per NIP-95. Use only for tiny stickers.\n\n### Resilience against dead links\n\nBecause the media hash `x` is the identity, a rotted `imeta url` is recoverable and SHOULD NOT be treated as clip loss. Clients SHOULD:\n\n- **Publish to redundancy.** Upload to every server in the publisher's Blossom server list ([BUD-03](https://github.com/hzrd149/blossom/blob/master/buds/03.md), `kind:10063`), mirroring where supported ([BUD-04](https://github.com/hzrd149/blossom/blob/master/buds/04.md)), so a single server outage does not kill the clip.\n- **Heal on read.** When `imeta url` fails, take the `x` hash and refetch the blob from the publisher's `kind:10063` servers, then from well-known servers. The blob is byte-identical by definition, so recovery is safe and automatic.\n- **Optionally carry hints.** A clip or note MAY reference the blob as a [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md) `blossom:` URI (`?xs=` server hints, `?as=` author pubkeys, `?sz=` size) so any client can resolve it without the origin server.\n\nServers that return NIP-94 tags on upload ([BUD-08](https://github.com/hzrd149/blossom/blob/master/buds/08.md)) let clients populate `imeta` directly from the upload response.\n\n## Tagging\n\nTwo tiers, both on top of the free NIP-50 text index over `content`/`alt`:\n\n- **Emotions** (`l` with `L:emotion`): a controlled, relay-indexed, flat set, at least one and at most six, all equal. Labels come from a published emotion namespace (an addressable event clients fetch); one reference namespace is the default, forks are allowed, adoption decides the canonical set. There is no author-declared \"primary\"; the dominant bucket is emergent from engagement. Browse is a `#l` filter.\n- **Long tail** (`t` + text): freeform memes/characters/shows. Powerful but stuffable, so clients SHOULD weight `t` by publisher trust. Search is NIP-50.\n\n## Discovery\n\n- Browse: `{\"kinds\":[1090], \"#l\":[\"facepalm\"]}`, relay-indexed.\n- Search: NIP-50 over `content`/`alt`/`t`.\n- Rank by zaps/engagement and NIP-85 publisher rank, not recency. The emotion a clip ranks highest in is its dominant bucket.\n\nA catalog-scale corpus is millions of events; this NIP moves the indexing cost off generic file servers and onto **opt-in catalog relays** that choose to index this kind. Size them deliberately.\n\n## Using clips in notes\n\nNothing in this NIP is required to post a GIF. A clip is inserted into a kind-1 note (or any content event) as its **media URL**, exactly like any other media on nostr:\n\n- The note content carries the `imeta url` (e.g. `https://blossom.example/<sha256>.webp`).\n- The note SHOULD carry a matching `imeta` tag (NIP-92) so rendering clients get the mime type, dimensions, and alt text.\n\nBecause the note transports a *URL*, not this custom kind, it renders in every client that previews media URLs or honors NIP-92 so no adoption of this NIP required. Clients that do not implement NIP-92 still show the raw URL, which most render inline by file extension. Prefer `image/gif` or `image/webp` for the note URL when maximum cross-client rendering matters; use `video/mp4` where the target clients are known to render video.\n\nA client posting a clip SHOULD include the media `x` hash in the note's `imeta` (it is already present there per NIP-92) and MAY add a `q`/`e` reference to the clip event, so figgy-aware readers can link back to the catalog entry and so usage feeds the ranking signal ([Spam and trust](#spam-and-trust), item 2). This is strictly additive since the note renders identically in clients that ignore it.\n\n## Spam and trust\n\nPublishing cannot be gated; filtering is at read/index time, layered:\n\n1. **Publisher rank** (NIP-85 `30382`): the load-bearing signal. Hide or down-rank low-rank pubkeys, but never hard-gate; unranked newcomers SHOULD be shown deprioritized so they are not filtered identically to spam.\n2. **Per-clip quality**: read zaps/engagement directly, aggregated by media `x` across all events (and notes) that carry it. (A provider MAY publish NIP-85 `30384` per-clip assertions as a precomputed convenience, but this is optional and off the critical path.)\n3. **Hash dedup**: aggregate across copies so reposting cannot flood. When aggregating, clients MUST collapse events sharing the same `(pubkey, x)` to the newest one, so a single publisher cannot stuff emotion buckets by reposting the same bytes with different labels. This recovers the anti-stuffing property that an addressable event would have enforced at the relay.\n4. **Cap**: at most six emotion facets, and no self-declared primary, so a publisher cannot stuff buckets or self-promote into top results.\n\n## Moderation, curation\n\nNIP-32 content-warning labels and NIP-56 reports. Curated \"clip packs\" (the NIP-30 set pattern, referencing clips by `e` tag + media `x`) are deferred to a companion NIP.\n\n## Open questions\n\n- Final kind number (draft placeholder `1090` chosen as unassigned and free of live traffic; the earlier `1064` was dropped for colliding with nostrsite theme events); emotion cap value.\n- Reference emotion namespace: seed label set and steward pubkey.\n- Whether `dur`/`loop` are standardized `imeta` keys or extensions.\n- The note-side clip reference is settled on `imeta x` (always present) as the interoperable minimum; whether to *also* standardize a `q` vs `e` reference to the clip event for richer back-linking is still open.","sig":"0ac2347180833abb9dbc236d1696e1530d7a7a9e86daabedfbda0af3d7b5471fef0d8e9488129355dc5f813942447c576e10a475adbab1f66bfa964383450d64"}