1 Overview
Media links on Nostr rot. A note lives forever on relays, but the image it points to sits on one HTTP server. When that server goes away, the note shows a broken picture. This NIP fixes that.
Blossom is a set of standards for media servers. Each standard is called a BUD. A Blossom server stores files and addresses each file by its sha256 hash. The same file has the same address on every Blossom server. That makes every copy interchangeable.
This NIP tells Nostr clients how to use Blossom. Clients may use all the BUDs: upload files, manage their own files, and so on. Most importantly, clients SHOULD use BUD-03 to fetch each user's kind:10063 list of Blossom servers. With that list, a client can find a lost file on the user's other servers.
Think of the sha256 hash as a serial number stamped on the file itself. The URL only tells you which shelf to check first. If the shelf is empty, ask the owner's kind:10063 list which other shelves hold the same serial number.
2 The Server List: kind 10063
Each user publishes one kind:10063 event. The event lists the Blossom servers that hold the user's files. Each server appears in one server tag. The content field stays empty. The list format comes from BUD-03; clients SHOULD fetch these lists as BUD-03 describes.
{
"id": "e4bee088334cb5d38cff1616e964369c37b6081be997962ab289d6c671975d71",
"pubkey": "781208004e09102d7da3b7345e64fd193cd1bc3fce8fdae6008d77f9cabcd036",
"content": "",
"kind": 10063,
"created_at": 1708774162,
"tags": [
["server", "https://blossom.self.hosted"],
["server", "https://cdn.blossom.cloud"]
],
"sig": "cc5efa74f59e80622c77cacf4dd62076bcb7581b45e9acff471e7963a1f4d8b3406adab5ee1ac9673487480e57d20e523428e60ffcc7e7a904ac882cfccfc653"
}
| Item | Value | Rule |
|---|---|---|
| kind | 10063 | The user's Blossom server list. One list per user. The kind sits in the replaceable range of NIP-01, so relays keep only the latest event per pubkey. |
| server | ["server", <server URL>] | One tag per Blossom server. The value is the server's base HTTPS URL. |
| content | "" | Empty string. The tags carry all the data. |
This NIP defines almost nothing itself. Kind 10063, the server tag, and the server HTTP API are all normative in the external Blossom BUD documents, not in the NIPs repository. The event shown above is the source's only example; treat BUD-03 as the authority if the two ever disagree.
3 Recovering a Broken Media URL
A client renders an event and finds a media URL inside it. Sometimes that URL no longer works. Follow this procedure:
- Check the URL path. Does it end with a 64-character hex string, with or without a file extension? If yes, that string is likely a
sha256hash of the file. - The event's author may have published a
kind:10063list. The client SHOULD fetch that list. - For each
servertag in the list, build a new URL. Use the hex string as the path, optionally with the file extension at the end:https://blossom.self.hosted/<hex-string>.png. - Request each candidate URL until one returns the file.
- The client SHOULD verify the download: the
sha256hash of the file's contents must match the 64-character hex string.
More information about this lookup is in BUD-03.
Do not skip the hash check. Any server can answer any path. Only the sha256 match proves you received the file the author posted. Discard a file whose hash differs.
The trigger is a heuristic. The source says a dead URL that ends in 64 hex characters "is likely" a sha256 hash. It defines no marker that says so for certain, and it does not say how a client decides that a URL "is not available anymore" (status codes, timeouts). Implementations must pick their own failure test. False positives are harmless: the hash check in step 5 rejects any wrong file.
4 More Complex Interactions
Blossom servers expose more facilities than plain downloads. For example, a client can check that a file exists on a server without downloading it. Clients may use any of these facilities. The Blossom BUDs document them; this NIP does not restate them.
5 Worked Example
Alice posted a photo to her own Blossom server. That server is now offline. Bob's client repairs the link. Orange lines are requests the client sends. Blue lines are responses.
-- 1. The original URL in Alice's note is dead -- → GET https://media.dead.example/b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.jpg ← (connection refused) -- 2. Path ends in 64 hex chars: fetch Alice's server list -- → ["REQ", "srv", {"kinds":[10063], "authors":["781208004e09…cd036"]}] ← ["EVENT", "srv", {"kind":10063, "tags":[ ["server","https://blossom.self.hosted"], ["server","https://cdn.blossom.cloud"]], …}] ← ["EOSE", "srv"] -- 3. Try each server with the hash as the path -- → GET https://blossom.self.hosted/b1674191a88e…f4f553.jpg ← 404 Not Found → GET https://cdn.blossom.cloud/b1674191a88e…f4f553.jpg ← 200 OK (image bytes) -- 4. Verify: sha256(bytes) == b1674191a88e…f4f553 → render the photo --
6 Version History
NIP-B7 is young and short. It replaced NIP-96 as the recommended way to host media for Nostr. Changes, newest first:
- 20252025-04Renamed the source files to uppercase hex (
b7.mdbecameB7.md). No spec change. - Origin2025-04-24fiatjaf added NIP-B7 for Blossom interaction in PR #1822. The Blossom project itself predates the NIP.
Original Proposal - Credits
| Author | fiatjaf |
| First commit | 2025-04-24 — “add B7 NIP for Blossom interaction (#1822)” - PR #1822 |
| Status | draft - optional — clients that show media SHOULD implement the recovery lookup |
| Source | B7.md on GitHub - full change history |
7 See Also
- NIP-01 — the event format, replaceable kinds, and the
REQflow used to fetch the server list. - NIP-96 — the older HTTP file storage spec, now deprecated in favor of this NIP.
- NIP-92 —
imetatags that attach metadata (including asha256hash) to media URLs in events. - NIP-94 — file metadata events that describe hosted files.
- NIP-51 — the standard lists catalog; it registers kind
10063as the Blossom server list. - NIP-5A — static websites served from Blossom blobs; uses the same
kind:10063lookup.