NIPs by PolleramaCommunity NIPs, surfaced by trustConnect
npub1l5sga6xg72p...

Hierarchical Blossom Piece Indexes

Published Aug 18, 2026
kind 32176

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:

  1. A content id and ordered piece list
  2. Multi-source fetch (many Blossom servers / mirrors)
  3. 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

KindNameRole
32176Blossom Piece IndexParameterized 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

  • content MUST be empty ("") or contain a text transcript of the blob contents
  • If content is non-empty, a transcript tag SHOULD describe the format (txt, vtt, srt, or json). If that tag is missing or not one of those four, clients MUST treat the transcript as txt.
  • MUST include d (NIP-54-style: lowercase letters, numbers, hyphens)
  • MUST include title
  • Uniquely identified by kind + pubkey + d
  • Any a tags MUST be in display / assembly order
  • An index MAY have zero a tags (stub / announcement only)
  • Each index event MUST contain at most 1000 a tags
  • Each index event MUST contain at most 1000 b tags

a tag format

["a", "<kind:pubkey:dtag>", "<relay hint>", "<label>"]
  • [1] — address of a child kind 32176 index
  • [2] — optional relay hint
  • [3] — optional legible label (not an event id)

Hierarchy

Every kind 32176 event is a node in a tree:

RoleMeaning
RootEntry index clients resolve by d / naddr
BranchHas one or more child indexes via a
LeafContributes 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:

  1. Local b tags, in tag order
  2. 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

TagMeaning
summaryShort description
sizeTotal file size in bytes (decimal string)
typeHint: file, archive, media, …
transcriptTranscript format when content is not empty: txt, vtt, srt, json. Default txt if omitted
iExternal id (sha256:…, magnet:…, etc.)
tTopics
imagePreview image URL
T / NNormalized 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.

SymbolValue
Author pubkey3e1ad0f3a5d3c12245db7788546c43ade3d97c6e046c594f6017cd6cd4164690
Full-file xf40069245c728a3dbfab8ce4efee8f14d77b425c74a00f3ac06b810b166d6a83
MP4 init / header05b12ab19cb0f2567054543e061a65aea32cfc9514cc6589e61ab1776e4564ea (4096 B)
Piece 0bc56e8d587133521b0da617b0475d18f9a8afacc45ae67549b8c2673273dec50 (1048576 B)
Piece 141f0c7a44ba21e4532c46dc316fb822a3b71ecf4aa62daeb7919628cf5d8bfa2 (1048576 B)
Piece 21b13f2bf2e8c265263cba6d73665e4a82b037fe7b4e194d3c187ac57644ea9d2 (1048576 B)
Piece 359809cbdde09122d9398736a1b15a70655a4fc2ae7ba21025d4c2bace4d506f4 (1048576 B)
Piece 4a0b115beda4c834c8eb34b620cedf4387583f076802717d129bf407be2282eb4 (1048576 B)
Piece 5ba664b3efc97c992f973f02e4e196474bef26a9af1a19e1d6d9d69a7d4c1b890 (1048576 B)
Total size3221225472 (3 GiB)
Piece size1 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-dear

JSON 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-1939

Example 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)

  1. Resolve root 32176 by d / naddr.
  2. Recursively process each node: emit local b pieces in order, then recurse each a child in order (≤1000 children per node); reject cycles.
  3. Download missing pieces in parallel from Blossom servers; verify each SHA-256.
  4. Concatenate; verify full-file x if present.
  5. 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 #b filters 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 with transcript (as above), or keep VTT only in a leaf event. Missing transcript means txt.
  • #b is the discovery hook; #x is useful when relays index it, but not all do — do not rely on #x alone.
  • Keep piece size conventions consistent inside one tree (example: 1 MiB media pieces).
  • Cap of 1000 a / 1000 b per 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 a labels / d suffixes people recognize (scene names) over opaque part-000 counters when the split is editorial, not only mechanical.