Hierarchical Blossom Piece Indexes
NKBIP-07: Hierarchical Blossom Piece Indexes
This NKBIP defines a parameterized replaceable index kind 32176 for large binary payloads distributed via Blossom (content-addressed HTTP blobs), using a table-of-contents pattern analogous to [[NKBIP-01]] publication indexes. This creates a Nostr-native blob distribution similar to popular file distribution systems.
Motivation
Blossom stores blobs by sha256 on media servers. Swarm-like blob distribution needs:
- A content id and ordered piece list
- Multi-source fetch (many Blossom servers / mirrors)
- Discovery without putting file bytes on relays
NKBIP-01 already solved hierarchical indexing for text via kind 30040 a-tags. NKBIP-07 reuses that shape for blob pieces.
Piece hashes use a single-letter tag so relays that implement NIP-01 tag filters can answer queries such as #b: ["bc56e8d587133521b0da617b0475d18f9a8afacc45ae67549b8c2673273dec50"] (which indexes contain this blob?).
Event kind
| Kind | Name | Role |
|---|---|---|
| 32176 | Blossom Piece Index | Parameterized replaceable hierarchical index |
Blob bytes are not required in event content. They live on Blossom servers and are retrieved with GET /<sha256> (BUD-01).
Kind 32176: Blossom Piece Index
Requirements
contentMUST be empty ("") or contain a text transcript of the blob contents- If
contentis non-empty, atranscripttag SHOULD describe the format (txt,vtt,srt, orjson). If that tag is missing or not one of those four, clients MUST treat the transcript astxt. - MUST include
d(NIP-54-style: lowercase letters, numbers, hyphens) - MUST include
title - Uniquely identified by
kind+pubkey+d - Any
atags MUST be in display / assembly order - An index MAY have zero
atags (stub / announcement only) - Each index event MUST contain at most 1000
atags - Each index event MUST contain at most 1000
btags
a tag format
["a", "<kind:pubkey:dtag>", "<relay hint>", "<label>"][1]— address of a child kind32176index[2]— optional relay hint[3]— optional legible label (not an event id)
Hierarchy
Every kind 32176 event is a node in a tree:
| Role | Meaning |
|---|---|
| Root | Entry index clients resolve by d / naddr |
| Branch | Has one or more child indexes via a |
| Leaf | Contributes piece bytes via b and has no child a tags needed for assembly |
root 32176
└─ branch 32176 (≤1000 a → branches or leaves)
└─ leaf 32176 (≤1000 b → piece hashes)A node MAY be both branch and piece-bearing: it can carry local b tags and child a tags. There is no separate event kind for leaves.
Clients MUST walk with a cycle/depth guard (breadth-first or depth-first). Indexes MUST NOT form cycles.
Assembly order
For each node, clients MUST assemble bytes in this order:
- Local
btags, in tag order - Then each child from
a, in tag order (recurse)
If a node has only b tags, it is a leaf. If it has only a tags, it is a pure branch. If it has both, apply the order above.
Piece pointers (b)
Pieces MUST be listed with the single-letter tag b (NIP-01 #b discovery):
["b", "<sha256>", "<length-bytes>"][1]— lowercase hex SHA-256 of the piece bytes[2]— piece length in bytes (decimal string); SHOULD be present
b tags MUST appear in assembly order. Within one tree, piece sizing conventions MUST be consistent. If a node would need more than 1000 pieces, split into additional leaf/branch children and reference them from the parent via a.
Every tree SHOULD also carry the full-file digest (distinct from per-piece b):
["x", "<sha256-of-complete-file>"]x MAY be repeated on descendants. size (total bytes) SHOULD appear on the root when known.
Blossom servers
Indexes MAY list preferred media servers:
["blossom", "https://cdn.example"]Clients SHOULD also consult the author’s kind 10063 Blossom server list (BUD-03) and any r mirrors. Fetch pieces with GET /<sha256> (BUD-01); optional PUT /mirror (BUD-04) after download to become another source.
Optional metadata tags
| Tag | Meaning |
|---|---|
summary | Short description |
size | Total file size in bytes (decimal string) |
type | Hint: file, archive, media, … |
transcript | Transcript format when content is not empty: txt, vtt, srt, json. Default txt if omitted |
i | External id (sha256:…, magnet:…, etc.) |
t | Topics |
image | Preview image URL |
T / N | Normalized title/author slugs (same convention as d-tags, see [[NKBIP-01]]) |
Worked example: Gone with the Wind (1939)
Illustrative Blossom tree for a ~3 GiB H.264 file of the film Gone with the Wind. Digests are deterministic examples (SHA-256 of label strings), not hashes of real copyrighted media.
| Symbol | Value |
|---|---|
| Author pubkey | 3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690 |
Full-file x | f40069245c728a3dbfab8ce4efee8f14d77b425c74a00f3ac06b810b166d6a83 |
| MP4 init / header | 05b12ab19cb0f2567054543e061a65aea32cfc9514cc6589e61ab1776e4564ea (4096 B) |
| Piece 0 | bc56e8d587133521b0da617b0475d18f9a8afacc45ae67549b8c2673273dec50 (1048576 B) |
| Piece 1 | 41f0c7a44ba21e4532c46dc316fb822a3b71ecf4aa62daeb7919628cf5d8bfa2 (1048576 B) |
| Piece 2 | 1b13f2bf2e8c265263cba6d73665e4a82b037fe7b4e194d3c187ac57644ea9d2 (1048576 B) |
| Piece 3 | 59809cbdde09122d9398736a1b15a70655a4fc2ae7ba21025d4c2bace4d506f4 (1048576 B) |
| Piece 4 | a0b115beda4c834c8eb34b620cedf4387583f076802717d129bf407be2282eb4 (1048576 B) |
| Piece 5 | ba664b3efc97c992f973f02e4e196474bef26a9af1a19e1d6d9d69a7d4c1b890 (1048576 B) |
Total size | 3221225472 (3 GiB) |
| Piece size | 1 MiB (1048576) → 3072 pieces |
| Fan-out | ≤1000 b per leaf → 4 scene leaves (see tree) |
Tree (production shape) — children named for iconic scenes, in film order:
gone-with-the-wind-1939 (root)
├─ …-twelve-oaks (leaf: 1000 × b) Twelve Oaks barbecue
├─ …-atlanta-burning (leaf: 1000 × b) Burning of Atlanta
├─ …-never-hungry-again (leaf: 1000 × b) “I’ll never be hungry again”
└─ …-frankly-my-dear (leaf: 72 × b) Rhett’s farewell
gone-with-the-wind-1939-mixed (optional: local header + scenes)
├─ b: MP4 init segment (4 KiB)
├─ a → twelve-oaks
├─ a → atlanta-burning
├─ a → never-hungry-again
└─ a → frankly-my-dearJSON samples below show only the first few b tags on each leaf; a real Twelve Oaks leaf would list 1000 b tags.
Example root
{
"kind": 32176,
"pubkey": "3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690",
"created_at": 1735689600,
"content": "",
"tags": [
["d", "gone-with-the-wind-1939"],
["title", "Gone with the Wind (1939)"],
["T", "gone-with-the-wind-1939"],
["N", "victor-fleming"],
["x", "f40069245c728a3dbfab8ce4efee8f14d77b425c74a00f3ac06b810b166d6a83"],
["size", "3221225472"],
["type", "media"],
["summary", "1939 epic; ~3 GiB H.264, 1 MiB Blossom pieces, scene-named leaves"],
["t", "classic-film"],
["t", "drama"],
["image", "https://cdn.example/posters/gone-with-the-wind-1939.jpg"],
["i", "sha256:f40069245c728a3dbfab8ce4efee8f14d77b425c74a00f3ac06b810b166d6a83"],
["blossom", "https://cdn.example"],
["blossom", "https://blossom.example"],
[
"a",
"32176:3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690:gone-with-the-wind-1939-twelve-oaks",
"wss://thecitadel.nostr1.com",
"Twelve Oaks"
],
[
"a",
"32176:3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690:gone-with-the-wind-1939-atlanta-burning",
"wss://nos.lol",
"Atlanta Burning"
],
[
"a",
"32176:3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690:gone-with-the-wind-1939-never-hungry-again",
"wss://relay.damus.io",
"I'll Never Be Hungry Again"
],
[
"a",
"32176:3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690:gone-with-the-wind-1939-frankly-my-dear",
"wss://thecitadel.nostr1.com",
"Frankly, My Dear"
]
]
}Root address (replaceable coordinate):
32176:3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690:gone-with-the-wind-1939Example leaf — Twelve Oaks (first ~1000 MiB)
{
"kind": 32176,
"pubkey": "3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690",
"created_at": 1735689601,
"content": "",
"tags": [
["d", "gone-with-the-wind-1939-twelve-oaks"],
["title", "Gone with the Wind — Twelve Oaks"],
["x", "f40069245c728a3dbfab8ce4efee8f14d77b425c74a00f3ac06b810b166d6a83"],
["b", "bc56e8d587133521b0da617b0475d18f9a8afacc45ae67549b8c2673273dec50", "1048576"],
["b", "41f0c7a44ba21e4532c46dc316fb822a3b71ecf4aa62daeb7919628cf5d8bfa2", "1048576"],
["b", "1b13f2bf2e8c265263cba6d73665e4a82b037fe7b4e194d3c187ac57644ea9d2", "1048576"],
["blossom", "https://cdn.example"]
]
}*(Real event: 1000 b tags for this scene’s byte range; only the first three are shown.)*
Example leaf — Atlanta Burning
{
"kind": 32176,
"pubkey": "3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690",
"created_at": 1735689602,
"content": "",
"tags": [
["d", "gone-with-the-wind-1939-atlanta-burning"],
["title", "Gone with the Wind — Atlanta Burning"],
["x", "f40069245c728a3dbfab8ce4efee8f14d77b425c74a00f3ac06b810b166d6a83"],
["b", "59809cbdde09122d9398736a1b15a70655a4fc2ae7ba21025d4c2bace4d506f4", "1048576"],
["b", "a0b115beda4c834c8eb34b620cedf4387583f076802717d129bf407be2282eb4", "1048576"],
["b", "ba664b3efc97c992f973f02e4e196474bef26a9af1a19e1d6d9d69a7d4c1b890", "1048576"],
["blossom", "https://cdn.example"]
]
}(Real event: next 1000 pieces; digests above stand in for the first three of that range.)
Assembly from the root (no local b): Twelve Oaks ‖ Atlanta Burning ‖ I’ll Never Be Hungry Again ‖ Frankly, My Dear (3072 × 1 MiB = 3 GiB).
Example mixed branch (init segment + scenes)
Useful when the first bytes are an MP4/fMP4 init segment kept on the parent:
{
"kind": 32176,
"pubkey": "3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690",
"created_at": 1735689610,
"content": "",
"tags": [
["d", "gone-with-the-wind-1939-mixed"],
["title", "Gone with the Wind (1939) — mixed index"],
["x", "f40069245c728a3dbfab8ce4efee8f14d77b425c74a00f3ac06b810b166d6a83"],
["size", "3221225472"],
["type", "media"],
["b", "05b12ab19cb0f2567054543e061a65aea32cfc9514cc6589e61ab1776e4564ea", "4096"],
[
"a",
"32176:3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690:gone-with-the-wind-1939-twelve-oaks",
"wss://thecitadel.nostr1.com",
"Twelve Oaks"
],
[
"a",
"32176:3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690:gone-with-the-wind-1939-atlanta-burning",
"wss://nos.lol",
"Atlanta Burning"
],
[
"a",
"32176:3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690:gone-with-the-wind-1939-never-hungry-again",
"wss://relay.damus.io",
"I'll Never Be Hungry Again"
],
[
"a",
"32176:3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690:gone-with-the-wind-1939-frankly-my-dear",
"wss://thecitadel.nostr1.com",
"Frankly, My Dear"
]
]
}Assembly: init ‖ Twelve Oaks ‖ Atlanta Burning ‖ I’ll Never Be Hungry Again ‖ Frankly, My Dear.
Example with captions (transcript)
content holds searchable WebVTT; media bytes stay on Blossom:
{
"kind": 32176,
"pubkey": "3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690",
"created_at": 1735689620,
"content": "WEBVTT\n\n00:00:01.000 --> 00:00:04.000\nFrankly, my dear, I don't give a damn.\n",
"tags": [
["d", "gone-with-the-wind-1939-captions-en"],
["title", "Gone with the Wind (1939) — English captions"],
["transcript", "vtt"],
["type", "media"],
["T", "gone-with-the-wind-1939-captions-en"],
["x", "f40069245c728a3dbfab8ce4efee8f14d77b425c74a00f3ac06b810b166d6a83"],
["b", "bc56e8d587133521b0da617b0475d18f9a8afacc45ae67549b8c2673273dec50", "1048576"],
["blossom", "https://cdn.example"]
]
}Client algorithm (normative sketch)
- Resolve root
32176byd/naddr. - Recursively process each node: emit local
bpieces in order, then recurse eachachild in order (≤1000 children per node); reject cycles. - Download missing pieces in parallel from Blossom servers; verify each SHA-256.
- Concatenate; verify full-file
xif present. - Optionally mirror pieces to a configured Blossom server.
Example: Imwald web client
Relationship to other specs
- NKBIP-01 — same empty-content hierarchical
a-index pattern for publications (30040/30041). - Blossom BUD-01 / BUD-03 / BUD-04 — blob GET, server lists, mirroring.
- NIP-01 — single-letter
#bfilters for piece-hash discovery. - Not BitTorrent — no info-hash/DHT requirement; swarming is multiserver (and optional user Blossom nodes) over HTTP.
Review notes (non-normative)
- Prefer **empty
content** for the video bytes; put searchable dialogue in a separate captions index withtranscript(as above), or keep VTT only in a leaf event. Missingtranscriptmeanstxt. #bis the discovery hook;#xis useful when relays index it, but not all do — do not rely on#xalone.- Keep piece size conventions consistent inside one tree (example: 1 MiB media pieces).
- Cap of 1000
a/ 1000bper event forces fan-out for multi-GB films (here four scene leaves for a 3 GiB encode), same scaling idea as NKBIP-01 section indexes. - Prefer human
alabels /dsuffixes people recognize (scene names) over opaquepart-000counters when the split is editorial, not only mechanical.