Chapter F4 - Media
F4
Nostr Implementation Possibility - Hex № F4

Podcasts

A podcast feed as signed events: one keypair per show, one event per episode, fetched from relays instead of an RSS URL.

1 Overview

This NIP defines how podcast clients fetch podcast episodes from relays. The design fits into existing podcast players with little change. A feed stops being one RSS file at one URL. It becomes a set of Nostr events: one metadata event for the show, and one event per episode.

RSS feeds have three structural problems. First, a feed depends on a URL. Hosting a URL is hard for most people, so podcasters use service providers. Providers charge money, seal shows into walled gardens as Spotify has done, and can censor podcasters or block migration. On Nostr a banned podcaster moves to other relays. The move is trivial as long as the podcaster holds the key, because clients can discover the new relay URLs.

Second, an RSS feed loads only in full. There is no pagination and no filtering. Sync in RSS clients is slow and cumbersome, which also nudges people toward centralized services. This gap led to broken schemes such as Podping, which a Nostr-native feed does not need. A single episode also cannot be referenced directly; it exists only inside the full list. So people share links to centralized platforms or to YouTube copies instead, which is an antipattern. On Nostr each episode is its own event with its own ID.

Third, an RSS feed allows no interaction. Listeners cannot like an episode, comment on it, or signal that they listened. This pushes both listeners and podcasters toward closed platforms that offer engagement and analytics. On Nostr, episode events can receive NIP-25 reactions and NIP-22 comments like any other event.

Podcast key signs 10154 + 54 Relay 1 Relay 2 Player REQ + filter EVENT episodes Audio files live at plain HTTP URLs; events carry only the links.
Fig. F4-1 — A feed without a feed URL. The podcast key publishes show metadata and episodes to relays. Players subscribe with normal NIP-01 filters.

2 Concept: One Keypair per Podcast

Each podcast is its own Nostr keypair. The podcast public key signs the show metadata and every episode. This is simple. It also lets a podcast combine its podcasting presence with a normal kind:0 / kind:1 microblogging presence under the same key.

A shared keypair allows shared ownership. Co-hosts can hold the key together, via MuSig2 or a plain agreement between friends. Ownership can also change over time, based on human trust in how the key material is handled.

Hint!

The podcast key is the feed address. Share it as an npub or nprofile (NIP-19) and any client can find the show on any relay that carries it.

3 Podcast Metadata — Kind 10154

Show-level information lives in a kind:10154 replaceable event (see the storage classes in NIP-01). The podcast key signs it. It contains everything a podcast client needs to display the show.

Similar information may exist in the podcast's kind:0 profile. Podcast-specific clients should read directly from kind:10154 and may ignore kind:0 entirely.

Wire format - kind 10154 podcast metadata
{
  "id": "...",
  "pubkey": <podcast-pubkey>,
  "kind": 10154,
  "created_at": 1700682555,
  "tags": [
    ["title", <podcast-title>],
    ["image", <podcast-cover-image-URL>],
    ["description", <podcast-description>],
    ["website", <podcast-website-URL>],   // optional, multiple
    ["p", <podcast-author-pubkey>, <role>] // optional, multiple
  ],
  "content": "",
  "sig": "..."
}
Kind 10154 tags
TagValueRule
titleshow titleThe name of the podcast.
imageURLThe cover image of the podcast.
descriptiontextA description of the podcast.
websiteURLA website of the podcast. Optional. May appear multiple times.
pauthor pubkey + roleAn author of the podcast. Optional. May appear multiple times. The third element is an optional role: host, cohost, or editor.

4 Authored Podcasts — Kind 10064

Do not trust the p tags in kind:10154 blindly. Any podcast can falsely claim any person as its author. Before a client displays the authorship, it must check the claim against an equivalent counter-claim signed by that author.

The counter-claim is a kind:10064 replaceable event, signed by the user's own key. In it, an author lists the public keys of the podcasts they author. Clients can also use this event in reverse: fetch it to discover all podcasts authored by a given user.

Wire format - kind 10064 authored podcasts
{
  "id": "...",
  "pubkey": <user-pubkey>,
  "kind": 10064,
  "created_at": 1700682555,
  "tags": [
    ["p", <podcast-pubkey-1>],
    ["p", <podcast-pubkey-2>]
  ],
  "content": "",
  "sig": "..."
}
Tech Note

The source spec contradicts itself here. The prose introduces this event as kind:10164, but the example shows kind:10064. The NIP-51 standard-lists table settles it: "Authored podcasts" is registered as kind 10064. This page uses 10064 throughout; treat 10164 in the source as a typo.

Podcast key 10154: "p" → Alice Alice's key 10064: "p" → podcast Client match both? Display "Host: Alice" claim counter-claim both agree
Fig. F4-2 — Authorship handshake. The client shows an author only when the show's claim and the author's counter-claim point at each other.

5 Podcast Episodes — Kind 54

Each episode is a kind:54 event. The podcast public key authors it directly. Kind 54 is a regular event, so relays store every episode and clients can filter and paginate with since, until, and limit. The content holds the episode notes as Markdown. The audio itself stays at an external URL.

Wire format - kind 54 podcast episode
{
  "id": "...",
  "pubkey": <podcast-pubkey>,
  "kind": 54,
  "created_at": 1700682555,
  "tags": [
    ["title", <episode title>],
    ["image", <optional episode image>],
    ["description", <a brief description>],
    ["audio", <audio-url>, <optional_media_type>] // multiple allowed
  ],
  "content": <markdown content>,
  "sig": "..."
}
Kind 54 tags
TagValueRule
titletextThe episode title.
imageURLAn episode image. Optional.
descriptiontextA brief description of the episode.
audioURL + media typeThe audio file. The second element is an optional media type (for example audio/mpeg). May appear multiple times for alternative encodings.
Tech Note

The episode tag list is explicitly open-ended. The source says "other important fields to be specified here later after further discovery". Common RSS fields such as duration, episode number, season, and transcript have no tags yet. Expect this list to grow while the NIP is a draft.

6 Favorite Podcasts — Kind 10054

Listeners can publicly advertise the podcasts they listen to, as a soft recommendation. This is the kind:10054 "Favorite podcasts" list defined in NIP-51. Per that NIP it holds p tags with podcast pubkeys and url tags with RSS/XML podcast URLs. See NIP-51 for the full list rules.

7 Worked Example

A player follows the show behind pubkey 7f9edc21…. It fetches show metadata, the latest episodes, and the authorship proof in one REQ each. Orange lines go to the relay. Blue lines come back.

Session transcript - wss://relay.example.com
-- fetch the show card --
→ ["REQ", "show", {"kinds":[10154], "authors":["7f9edc21…"]}]
← ["EVENT", "show", {"kind":10154,"pubkey":"7f9edc21…","tags":[
     ["title","Signals from Nowhere"],
     ["image","https://cdn.example.com/cover.jpg"],
     ["description","Weekly radio noise from the edge of the mesh."],
     ["website","https://signals.example.com"],
     ["p","a1b2c3d4…","host"]], "content":"", …}]
← ["EOSE", "show"]

-- fetch the 2 newest episodes (pagination via limit/until) --
→ ["REQ", "eps", {"kinds":[54], "authors":["7f9edc21…"], "limit":2}]
← ["EVENT", "eps", {"kind":54,"pubkey":"7f9edc21…","created_at":1749762600,
     "tags":[["title","E23: Antennas"],
       ["description","We climb the roof."],
       ["audio","https://cdn.example.com/e23.mp3","audio/mpeg"],
       ["audio","https://cdn.example.com/e23.ogg","audio/ogg"]],
     "content":"Show notes in **markdown**.", …}]
← ["EVENT", "eps", {"kind":54, "tags":[["title","E22: Ground Loops"], …], …}]
← ["EOSE", "eps"]

-- verify the host claim before displaying it --
→ ["REQ", "auth", {"kinds":[10064], "authors":["a1b2c3d4…"]}]
← ["EVENT", "auth", {"kind":10064,"pubkey":"a1b2c3d4…",
     "tags":[["p","7f9edc21…"]], …}]
-- counter-claim matches: the client may now show "Host: a1b2c3d4…" --

8 Version History

NIP-F4 is young. It landed in one commit and has not changed since:

  • Origin2026-05fiatjaf published the proposal as "NIP-F4: podcasts" in PR #1093. Kinds 10154, 10064, and 54 defined in the initial text; the matching 10054 and 10064 rows were registered in NIP-51.

Original Proposal - Credits

Authorfiatjaf
First commit2026-05-28 — “NIP-F4: podcasts (#1093)”
Pull requestnostr-protocol/nips #1093
Statusdraft - optional
SourceF4.md on GitHub - full change history

9 See Also

  • NIP-01 — the base protocol: events, filters, and the replaceable/regular storage classes used here.
  • NIP-51 — lists; defines the kind 10054 favorite-podcasts list and registers kind 10064.
  • NIP-19 — npub/nprofile encodings for sharing a podcast key with its relays.
  • NIP-25 — reactions; lets listeners like an episode event.
  • NIP-22 — comments on events of any kind, including episodes.
  • NIP-65 — relay lists; how clients discover where a podcast key publishes.
  • NIP-A0 — voice messages; a related audio-over-Nostr event format.