Adds docs/nip71-migration-plan.md — the agreed spec to build from: - Dual-event, opt-in (off by default): keep canonical kind 30078; add addressable 34235/34236 mirror (d=videoRootId, edits in lockstep). - Research correction: 34235/36 are NOT deprecated — current NIP-71 designates the addressable pair for content that needs post-publication updates (exactly bitvid's edit-in-place model). - WebTorrent rides STANDARD NIP-94 imeta fields (magnet, i) rather than a hack; private videos never mirrored; HTTPS url required to mirror. - Phases: 0 retarget builders → 1 outbound publish/edit/delete → 1.5 NIP-89 "Open in bitvid" handler + NIP-51 kind 30005 portable playlists → 2 inbound ingest. Also refreshes docs/nips/71.md (was stale: only 21/22; now the current 233-line spec with 34235/36 addressable kinds, waveform, origin, multi-audio). Updates TODO #17 to point at the plan. Co-Authored-By: Claude Opus 4.8 <[email protected]>
8.1 KiB
NIP-71 Interop Plan for bitvid
Status: planning / spec (no code yet). Last updated 2026-06-22.
Goal: let a publisher opt in to making a bitvid video fully NIP-71 compliant so it (a) shows up on other Nostr video clients (Nostube, Amethyst, Flare, zap.stream recordings, …) and (b) still carries bitvid's extra data (WebTorrent, flags) for a richer experience inside bitvid. Eventually bitvid also ingests other clients' NIP-71 videos. Decisions below were made with the maintainer; see "Decisions".
Decisions (locked)
- Kind: addressable
34235(normal) /34236(short). - Opt-in, off by default — per-video toggle; private videos never mirrored.
- Scope: outbound first, inbound later (Phase 1 vs Phase 2).
Why 34235/34236 (research-validated)
The earlier assumption that 34235/36 were "deprecated legacy" was wrong. The current NIP-71 (master) defines all four kinds and explicitly designates the addressable pair for editable content:
"For content that may need updates after publication (such as correcting metadata, descriptions, or handling URL migrations), addressable versions are available: Kind
34235… Kind34236… include adtag as a unique identifier and can be updated while maintaining the same addressable reference."
That is exactly bitvid's model (replaceable kind 30078, edit-in-place via
d = videoRootId). So 34235/36 is not a compromise — it's the spec-recommended
kind for what bitvid does, and it's what Nostube/Goblinbox consume today.
Hedge: keep the builder kind-parameterized so we can also emit / switch to
21/22(regular) later without a rewrite if the ecosystem leans that way.
Architecture: dual-event, additive
- Canonical stays kind 30078 (NIP-78 app-data), unchanged. Source of truth for WebTorrent, private videos, moderation, view counts — none of which NIP-71 models.
- Mirror = kind 34235/34236, addressable with
d=videoRootId(same d-tag as the 30078), so the mirror edits/deletes in lockstep with the canonical. - Correlation is automatic via
(pubkey, videoRootId). We also set:["origin", "bitvid", "<videoRootId>", "https://bitvid.network/?v=<nevent|naddr>"]so other clients can attribute + deep-link back ("watch on bitvid").- Optionally
["a", "30078:<pubkey>:<videoRootId>"]back-pointer (harmless elsewhere).
Field mapping: 30078 JSON → 34235/36 tags
Sourced against NIP-71 + NIP-92 (imeta) + NIP-94 (file metadata fields).
| bitvid (30078 content) | NIP-71 / imeta | notes |
|---|---|---|
| — | ["d", videoRootId] |
required for addressable; lockstep with 30078 |
title |
["title", …] |
required |
description |
.content and ["alt", …] |
|
| first publish time | ["published_at", …] |
stable across edits (no reordering elsewhere) |
url (HTTPS) |
imeta url <…> |
primary playable source for foreign clients |
| derived MIME | imeta m <…> |
not stored today → derive from extension, default video/mp4 |
thumbnail |
imeta image <…> |
|
fileSha256 |
imeta x <…> |
|
originalFileSha256 |
imeta ox <…> |
|
magnet |
imeta magnet <magnet:?xt=…&ws=…&xs=…> |
standard NIP-94 field — torrent-aware clients can use it |
infoHash |
imeta i <infohash> |
standard NIP-94 field |
ws / xs |
encoded into the magnet URI (&ws=/&xs=) |
also kept on 30078 canonical |
isNsfw |
["content-warning", "<reason>"] |
standard |
| hashtags (if/when added) | ["t", …] (repeated) |
bitvid has no hashtags field yet — future |
isForKids, mode, etc. |
bitvid-namespaced extra tags | ignored by other clients |
| — | ["client", "bitvid"] |
Two hard rules that fall out of this:
- Private videos are never mirrored. NIP-71 has no encryption; mirroring would leak them. The toggle is disabled (with explanation) for private videos.
- A hosted HTTPS
urlis required to mirror. Foreign clients can't play a magnet-only video; bitvid's magnet still rides along (magnet/i) for torrent-aware clients, buturlmust be present or the toggle is disabled.
Timestamp hygiene: created_at = actual signing time; published_at = display
time. (Avoids the future-created_at weirdness seen in some published events.)
Phases
Phase 0 — Retarget builders (no UX; flag stays off)
- Point existing
buildNip71VideoEventat 34235/36 +d=videoRootId(it currently targets 21/22). Implement the field mapping incl.magnet/i/ox. - Normal-vs-short selection: default 34235; choose 34236 when known-portrait (dimensions when available; otherwise a manual "short" toggle).
- Cheat-resistant tests: bitvid video → spec-valid 34235 event → round-trips back
via
convertEventToVideo; magnet/infohash present; private refused; url-less refused.
Phase 1 — Outbound publish/edit/delete (the headline feature)
- Edit form toggle: "Also publish to other Nostr video apps (NIP-71)" — off by default, gated to public + HTTPS-url videos.
- Publish: 30078 (as today) + the 34235/36 mirror to the write-relay set
(reuse
getDeletePublishRelayswrite set + the publish-outcome toast). - Lifecycle parity: edit updates both (same d-tag); delete NIP-09s both addresses; toggle-off NIP-09s the mirror (removes it from other clients).
- Mutation-verified lifecycle tests, then flip
FEATURE_PUBLISH_NIP71on.
Phase 1.5 — Discoverability glue (cheap, high interop value)
- NIP-89 handler registration (kind
31990): publish once that bitvid handles video kinds21/22/34235/34236with awebURL template. Result: other clients (Amethyst, etc.) can show "Open in bitvid" when they encounter a video event. - NIP-51 video curation sets (kind
30005): publish/read bitvid playlists as portable video sets so playlists interop across clients. (Sets reference videos bye/atags.)
Phase 2 — Inbound ingest (separate, bigger)
- Feed source querying
34235/36(and21/22) from relays; map viaconvertEventToVideo. - Dedup vs bitvid-native (by
a-address /xhash /url), moderation + trust gating (reuse blacklist / NIP-51 mute), and a distinct discovery surface so foreign videos read as "from the wider Nostrverse," not bitvid-native. - Honor
originfor attribution; respect the no-URL-allowlist rule but apply existing moderation. - (Cross-client comments/zaps via NIP-22
kind:1111addressed to the 34235 event is a further follow-on — bitvid comments currently target 30078.)
Enrichment backlog (nice-to-have, not v1)
dim, duration, bitrate, waveform, multiple imeta variants + multi-audio
(ov/language) tracks, text-track (WebVTT captions/subtitles/chapters),
segment (chapters) — all need more captured at upload time. v1 ships the minimal
spec-valid imeta (url/m/image + magnet/i) and grows from there.
Risks / watch-items
- Editing addressable events across foreign clients: some cache aggressively; edits may lag elsewhere. Acceptable; document it.
- Magnet-only catalogs: those videos simply won't appear on non-torrent clients (by design — they still work in bitvid).
- Kind drift: if the ecosystem consolidates on 21/22, add/dual-emit later (builder stays kind-parameterized).
- Ingest safety (Phase 2): arbitrary foreign media + metadata → keep moderation, no autoplay of untrusted sources, sanitize before render.
Sources
- NIP-71 Video Events (current): https://github.com/nostr-protocol/nips/blob/master/71.md (vendored at
docs/nips/71.md) - NIP-92 inline metadata (
imeta): https://github.com/nostr-protocol/nips/blob/master/92.md - NIP-94 file metadata (
magnet,i,x,ox,fallback, …): https://github.com/nostr-protocol/nips/blob/master/94.md - NIP-89 recommended application handlers (
31989/31990): https://github.com/nostr-protocol/nips/blob/master/89.md - NIP-51 lists/sets incl. kind
30005video curation: https://github.com/nostr-protocol/nips/blob/master/51.md - Existing bitvid code:
js/nostr/nip71.js(builders/parser),js/nostrEventSchemas.js(NIP71_VIDEO/NIP71_SHORT_VIDEO),js/constants.js(FEATURE_PUBLISH_NIP71).