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

Live Streaming and Spaces

Advertise a live stream, a chat room, or a meeting space as an event. Update it while people join, speak, and leave.

1 Overview

This NIP defines event kinds that advertise live spaces and the participation of pubkeys in them. A live activity is a stream, a talk, or a meeting that happens now or soon. The event that advertises it lists the title, the media URL, the status, and the participants.

Five kinds work together. Kind 30311 advertises a live streaming event. Kind 1311 carries a live chat message for that stream. Kind 30312 defines a persistent meeting space. Kind 30313 schedules a meeting inside a space. Kind 10312 signals room presence of one listener.

The advertising events are addressable events, as defined in NIP-01. The owner updates one event in place while the activity runs. Viewers subscribe once and watch the state change.

Hint!

The streamer does not send video over Nostr. The streaming tag carries a normal media URL, for example an HLS .m3u8 playlist. Nostr carries only the metadata, the chat, and the presence.

Host mgmt client Relay kind 30311 Viewer player + chat 30311 updates REQ / EVENT 1311 chat ("a" tag)
Fig. 53-1 — Live activity flow. The host updates one addressable event. Viewers subscribe to it and send chat messages that point back to it.

2 Live Streaming Event (kind 30311)

Kind 30311 is the "Live Streaming Event". It is an addressable event. Its tags advertise the content and the participants of a live stream. The owner updates the event constantly as participants join and leave.

Wire format - kind 30311
{
  "kind": 30311,
  "tags": [
    ["d", <unique identifier>],
    ["title", <name of the event>],
    ["summary", <description>],
    ["image", <preview image url>],
    ["t", "hashtag"],
    ["streaming", <url>],
    ["recording", <url>],          // edited video, added after the end
    ["starts", <unix timestamp, seconds>],
    ["ends", <unix timestamp, seconds>],
    ["status", <planned | live | ended>],
    ["current_participants", <number>],
    ["total_participants", <number>],
    ["p", "91cf9..4e5ca", "wss://provider1.com/", "Host", <proof>],
    ["p", "14aeb..8dad4", "wss://provider2.com/nostr", "Speaker"],
    ["p", "612ae..e610f", "ws://provider3.com/ws", "Participant"],
    ["relays", "wss://one.com", "wss://two.com", ...],
    ["pinned", <event id of pinned live chat message>]
  ],
  "content": ""
}
Tag reference - kind 30311
TagValueRule
dunique identifierUse a distinct d tag for each activity. All other tags are optional.
titlestringName of the event.
summarystringDescription of the event.
imageURLPreview image.
thashtagTopic hashtag. May repeat.
streamingURLWhere the live media plays.
recordingURLThe edited video, placed once the activity is over.
startsunix secondsStart time. Update it when the status changes to live.
endsunix secondsEnd time. Update it when the status changes from live.
statuskeywordOne of planned, live, ended.
current_participantsnumberParticipants active now.
total_participantsnumberParticipants over the whole activity.
ppubkey, relay, role, proofA participant. The relay entry may be empty. See the role and proof rules below.
relaysURL listPreferred relays for the activity. One tag holds many URLs.
pinnedevent idA pinned kind 1311 live chat message. May repeat.

Each p tag SHOULD carry a displayable marker name for the current role of the user in the event. Examples are Host, Speaker, and Participant. The relay information MAY be empty.

Providers SHOULD keep the participant list small, for example under 1000 users. When the limit is reached, providers SHOULD select which participants get named in the event. Clients should not expect a comprehensive list. Once the activity ends, the owner can delete the event, or update it to summarize the activity and provide async content such as the recording.

Clients are expected to subscribe to kind 30311 events in general, or for given follow lists and statuses. Clients MAY display participants' roles in activities, and access points to join the activity. Live activity management clients are expected to update kind 30311 constantly during the event. Clients MAY treat a status=live event as ended after one hour without any update. The starts and ends timestamps SHOULD be updated when the status changes to and from live.

Link to the activity with the NIP-19 naddr code along with the a tag. This is a MUST.

planned live ended update starts update ends No update for 1 hour while live? Clients MAY treat the event as ended.
Fig. 53-2 — Status lifecycle. The owner moves the status forward and refreshes starts and ends at each change.

3 Proof of Agreement to Participate

An event owner can name any pubkey as a participant. The named user may not know. A malicious owner could add large account holders to lure their followers into a trap. The proof field defends against this.

The owner adds the proof as the 5th term of a p tag. To build the proof, follow these steps. Take the complete a tag value of the event: kind:pubkey:dTag. Hash it with SHA256. Sign the hash with the participant's private key. Encode the signature in hex.

Proof recipe
a_tag  = "30311:<owner pubkey>:<d value>"
proof  = hex( sign_<participant privkey>( sha256(a_tag) ) )
["p", <participant pubkey>, <relay>, "Speaker", <proof>]

Clients MAY display a participant only if the proof is available. Or clients MAY display a participant without proof as "invited".

4 Live Chat Message (kind 1311)

Kind 1311 is the live chat's channel message. It is a regular event. Clients MUST include the a tag of the activity. An e tag denotes the direct parent message this post replies to.

Wire format - kind 1311
{
  "kind": 1311,
  "tags": [
    ["a", "30311:<activity author pubkey>:<d identifier>", <optional relay url>]
  ],
  "content": "Zaps to live streams is beautiful."
}

q tags MAY be used when the content cites other events with NIP-21 URIs:

Citation tag
["q", <event-id> or <event-address>, <relay-url>, <pubkey-if-a-regular-event>]

Hosts may pin one or more live chat messages. To pin, the host updates the pinned tags in the kind 30311 live event.

Tech Note

A 2025-02 revision removed reply markers from live chat and added q tags. Yet the source's kind 1311 example still ends its a tag with a "root" marker, and the kind 10312 presence template does the same. Expect both marked and unmarked a tags in the wild.

5 Meeting Spaces (kind 30312)

A meeting space contains one or more video or audio rooms. Users join a room and take part in the streaming. Kind 30312 "Space Host" is an addressable event. It defines the configuration and the properties of one virtual interactive space. Each space has a unique identifier and can host multiple events and meetings.

Wire format - kind 30312
{
  "kind": 30312,
  "tags": [
    ["d", <unique identifier>],           // Required: room identifier
    ["room", <name of the room>],         // Required: display name
    ["summary", <description>],           // Optional
    ["image", <preview image url>],       // Optional
    ["status", <open | private | closed>], // Required: accessibility
    ["service", <url>],                   // Required: URL to access the room
    ["endpoint", <url>],                  // Optional: API endpoint for status/info
    ["t", <hashtag>],                     // Optional: may repeat
    ["p", <pubkey>, <url>, <role>, <proof>], // Required: at least one provider
    ["relays", <url>, <url>, ...]        // Optional: preferred relays
  ],
  "content": ""  // usually empty, may contain additional metadata
}

Space rules:

  • The status MUST be open, private, or closed. Closed means the room is not in operation.
  • The space MAY specify an access control policy for private rooms, for example invite-only or payment required.
  • The space MAY persist when not in use.
  • The space MUST have at least one provider with the Host role.
  • The space MAY have multiple providers with different roles.
Provider roles (p tags)
RoleCapability
HostFull room management capabilities.
ModeratorRoom moderation capabilities.
SpeakerAllowed to present and speak.

The optional proof field in a provider p tag verifies the role, as in §3.

Space 30312 d = room id Meeting 30313 planned / live / ended Meeting 30313 another event Presence 10312 one listener "a" 30312:pk:room "a" + "root"
Fig. 53-3 — Space structure. Meetings and presence events point at the parent space with a tags.

6 Meeting Room Events (kind 30313)

Kind 30313 represents a scheduled or ongoing meeting within a room. It is an addressable event. It MUST reference its parent room.

Wire format - kind 30313
{
  "kind": 30313,
  "tags": [
    ["d", <event unique identifier>],      // Required
    ["a", "30312:<pubkey>:<room-id>", "wss://nostr.example.com"], // Required: parent space, 'd' from 30312
    ["title", <meeting title>],            // Required
    ["summary", <description>],            // Optional
    ["image", <preview image url>],        // Optional
    ["starts", <unix timestamp>],          // Required
    ["ends", <unix timestamp>],            // Optional
    ["status", <planned | live | ended>],  // Required
    ["total_participants", <number>],      // Optional: total registered
    ["current_participants", <number>],    // Optional: currently active
    ["p", <pubkey>, <url>, <role>]         // Optional: participant with role
  ],
  "content": ""  // usually empty, may contain additional metadata
}

Meeting rules:

  • The meeting MUST reference its parent room.
  • The meeting MUST have a status: planned, live, or ended.
  • The meeting MUST have a start time.
  • The meeting MAY track participant counts.
  • The meeting MAY include participant roles specific to the event.

Meeting management follows the live streaming pattern. Clients SHOULD update the event status regularly while it is live. A meeting without updates for one hour MAY be considered ended. The starts and ends timestamps SHOULD be updated when the status changes.

Tech Note

The source text says the meeting "MUST reference its parent room using the d tag". But in the wire format the d tag holds the meeting's own identifier. The parent reference is the a tag 30312:<pubkey>:<room-id>, whose last segment is the d value of the space. Follow the a tag form shown above.

7 Room Presence (kind 10312)

Kind 10312 signals the presence of a listener in a room. It is a replaceable event. Because the relay keeps only the latest event per pubkey, a user can indicate presence in only one room at a time.

Wire format - kind 10312
{
  "kind": 10312,
  "tags": [
    ["a", <room a tag>, <relay hint>, "root"],
    ["hand", "1"]  // hand raised flag
  ]
}

The presence event SHOULD be updated at regular intervals. Clients SHOULD filter out presence events older than a given time window. The hand tag with value "1" means the listener raises a hand.

Warning!

The source calls kind 10312 a "regular replaceable event". In NIP-01 terms it is a plain replaceable event: the range 10000-19999. Do not confuse it with a regular event. The relay keeps only the newest one per pubkey.

8 Worked Examples

The first example is a real live stream advertisement. The status is live and the streaming tag points at an HLS playlist.

Live stream - kind 30311
{
  "id": "57f28dbc264990e2c61e80a883862f7c114019804208b14da0bff81371e484d2",
  "pubkey": "1597246ac22f7d1375041054f2a4986bd971d8d196d7997e48973263ac9879ec",
  "created_at": 1687182672,
  "kind": 30311,
  "tags": [
    ["d", "demo-cf-stream"],
    ["title", "Adult Swim Metalocalypse"],
    ["summary", "Live stream from IPTV-ORG collection"],
    ["streaming", "https://adultswim-vodlive.cdn.turner.com/live/metalocalypse/stream.m3u8"],
    ["starts", "1687182672"],
    ["status", "live"],
    ["t", "animation"],
    ["t", "iptv"],
    ["image", "https://i.imgur.com/CaKq6Mt.png"]
  ],
  "content": "",
  "sig": "5bc7a60f5688effa5287244a24768cbe0dcd854436090abc3bef172f7f5db141…"
}

A viewer replies in the live chat. The a tag binds the message to the stream above:

Live chat message - kind 1311
{
  "id": "97aa81798ee6c5637f7b21a411f89e10244e195aa91cb341bf49f718e36c8188",
  "pubkey": "3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24",
  "created_at": 1687286726,
  "kind": 1311,
  "tags": [
    ["a", "30311:1597246ac22f7d1375041054f2a4986bd971d8d196d7997e48973263ac9879ec:demo-cf-stream", "", "root"]
  ],
  "content": "Zaps to live streams is beautiful.",
  "sig": "997f62ddfc0827c121043074d50cfce7a528e978c575722748629a4137c45b75…"
}

The second example is a meeting space with one live meeting inside it. The session shows the owner publishing both, and a listener marking presence. Orange lines go to the relay. Blue lines come from the relay.

Session transcript - wss://nostr.example.com
-- Owner publishes the space --
→ ["EVENT", {"kind":30312,"tags":[["d","main-conference-room"],
     ["room","Main Conference Hall"],["summary","Our primary conference space"],
     ["image","https://example.com/room.jpg"],["status","open"],
     ["service","https://meet.example.com/room"],["endpoint","https://api.example.com/room"],
     ["t","conference"],["p","f7234bd4c139…76e9ca","wss://nostr.example.com/","Owner"],
     ["p","14aeb..8dad4","wss://provider2.com/","Moderator"],
     ["relays","wss://relay1.com","wss://relay2.com"]],"content":"", …}]
← ["OK", "a01f83cc…", true, ""]

-- Owner publishes the meeting inside the space --
→ ["EVENT", {"kind":30313,"tags":[["d","annual-meeting-2025"],
     ["a","30312:f7234bd4c139…76e9ca:main-conference-room","wss://nostr.example.com"],
     ["title","Annual Company Meeting 2025"],["summary","Yearly company-wide meeting"],
     ["image","https://example.com/meeting.jpg"],["starts","1676262123"],
     ["ends","1676269323"],["status","live"],["total_participants","180"],
     ["current_participants","175"],["p","91cf9..4e5ca","wss://provider1.com/","Speaker"]],
     "content":"", …}]
← ["OK", "b7c20d1e…", true, ""]

-- A listener signals presence and raises a hand --
→ ["EVENT", {"kind":10312,"tags":[
     ["a","30312:f7234bd4c139…76e9ca:main-conference-room","wss://nostr.example.com","root"],
     ["hand","1"]],"content":"", …}]
← ["OK", "c93aa1f0…", true, ""]

9 Version History

NIP-53 started as "Live Activities" for streaming and chat. A 2025 addendum grew it into full meeting spaces with presence. Notable changes, newest first:

  • 20262026-06Housekeeping: code fences relabeled and NIP files renamed for clarity.
  • 20252025-09Text refinements, formatting fixes, and typo cleanup.
  • 20252025-06Addendum: added meeting spaces (kind 30312), meeting room events (kind 30313), and room presence (kind 10312).
  • 20252025-02Live chat: removed reply markers and added q citation tags. Added optional pinned chat messages to kind 30311.
  • 20242024-08Terminology: "parameterized replaceable event" became "addressable event"; JSON reformatting and small nitpicks.
  • 20232023-07Fixed the event kind in the chat example days after launch.
  • Origin2023-07-08Vitor Pamplona proposed "NIP-53 Live Activities" with kinds 30311 and 1311 and the proof of participation.

Original Proposal - Credits

AuthorVitor Pamplona
First commit2023-07-08 — “NIP-53 Live Activities” (PR #498)
Statusdraft - optional
Source53.md on GitHub - full change history

10 See Also

  • NIP-01 — addressable and replaceable event classes, the a tag, and the base protocol.
  • NIP-19 — the naddr code that MUST be used to link to a live activity.
  • NIP-21 — nostr: URIs cited by q tags in live chat.
  • NIP-10 — the e tag reply convention that live chat borrows for parent messages.
  • NIP-52 — calendar events; schedule a future activity, then go live with this NIP.