Nostr and IPFS get compared constantly, usually as rivals. They are not. They answer two different questions, and an application that needs both answers needs both protocols — or something that plays each part.

  • Nostr answers “who said what, and when.” Its unit is a signed event.
  • IPFS answers “what are these bytes.” Its unit is content-addressed data.

That difference is worth getting straight, but it is not the interesting part. The interesting part is that both protocols are decentralized by design and neither is automatically decentralized by deployment, and the ways each one collapses back toward a single point of failure are mirror images of each other. IPFS falls over on the gateway and the pin; Nostr falls over on the relay. Both are deployment choices that inherit the protocol’s good name without inheriting the property that earned it.

This article works through where each one actually gets centralized, then holds the framework against a real system: Buzz, a genuine Nostr relay that made a deliberate, and deliberately un-distributed, deployment choice — and that stores its media on something that is not IPFS.

The Buzz observations are against Buzz Desktop 0.5.18 and the relay image ghcr.io/block/buzz:main reporting v0.2.1, on macOS 26.5.2.

Prerequisites

Nothing to install. The identifiers discussed here are computed in a companion article, Compute a Nostr Event ID, an IPFS CID, and a Blossom Hash for the Same Bytes, and its output is quoted below rather than rebuilt.

If you want the Buzz section to be more than reading, Install and Use Buzz on macOS stands up the relay the commands in that section run against.

Two questions, two kinds of identifier

Most confusion about these protocols dissolves once you notice that “identifier” means two unrelated things. One names a statement; the other names content. They change independently, and a system usually needs both.

Nostr is worth defining before comparing it to anything. The name is an acronym for notes and other stuff transmitted by relays, and the distinction its community leans on hardest is that it is a protocol, not a platform. In a 2024 introductory talk, Derek Ross frames the payoff as a portable digital social identity that you control, in the same way that email and the web are protocols you can pick a client for. Hold on to the protocol/platform framing — the argument below is that it is necessary and nowhere near sufficient.

A Nostr event id is the SHA-256 of a canonical serialization of the event itself — author, timestamp, kind, tags, content. Edit one character of a note and it becomes a different event with a different id, even though the photo it links to is untouched. Post the same photo twice and you get two events.

An IPFS CID is derived from the bytes. The same photo has the same CID no matter who posts it, how many times, or which machine is holding a copy. That is what “content addressing” means: the name is a function of the content, so it can be verified by anyone who has the bytes and trusts nobody.

A third system belongs in this comparison, and leaving it out is what makes “Nostr or IPFS” look like a binary. Blossom stores blobs on ordinary HTTP servers, named by their bare SHA-256: PUT /upload to store, GET /<sha256> to retrieve. It is content-addressed in exactly the sense above, and it is not IPFS. It is also the option Buzz chose, which is why it gets a column here rather than a footnote.

NostrIPFSBlossom
Primary purposeMessaging, social and event dataStoring and distributing contentServing blobs for Nostr apps
Data modelSigned eventsContent-addressed blocks and filesContent-addressed opaque blobs
Identitysecp256k1 keypairs (Schnorr, BIP-340)None; peer IDs name nodes, not content or authorsThe same Nostr keypairs
Main identifierEvent id (SHA-256 of the event)CID (multibase + version + codec + multihash)Bare SHA-256 hex
DistributionRelaysNodes exchanging blocks, found via a DHTHTTP servers; redundancy by explicit mirroring
Typical payloadPosts, profiles, DMs, reactionsImages, video, documents, whole sitesImages, video, attachments
PersistenceWhatever relays choose to keepWhatever nodes choose to pinWhatever servers choose to keep

Three things in that table are worth reading carefully, because they are where casual comparisons go wrong.

Content addressing is a family, not a technology. IPFS and Blossom both name data by its hash, and both therefore give you the property that matters: anyone holding the bytes can verify the name, and no server can substitute different content without detection. They differ in how much the identifier says about itself and in how it is retrieved — a CID declares its version, codec, and hash algorithm, while a Blossom name is 32 bytes of SHA-256 and nothing else. The next section prints both for the same file, and they contain the same digest.

Identity is where the three genuinely diverge. On Nostr, identity is a keypair and it is the account: the same secp256k1 key signs every event, with no server-side record of you. A CID carries no authorship whatsoever: it names bytes, and peer IDs name nodes rather than documents or people. (IPFS does have a signed naming layer, IPNS, which comes up again below.) Blossom’s answer is the interesting one, because it does not invent anything — an upload is authorized by a signed Nostr event (kind:24242) carrying the blob’s SHA-256, so the storage layer reuses the identity layer wholesale and needs no accounts of its own. That is the clearest illustration of the seam these systems join along: IPFS leaves provenance to someone else, and Blossom takes Nostr’s.

No row says “permanent.” A CID is a permanent name, not a promise that anyone still holds what it names, and the same is true of a Blossom hash and a Nostr event id. The distinction between naming and durability is the subject of the rest of this article, and it is the one that costs money.

The three identifiers, side by side

The claim that two of these three names are the same digest is easier to accept from a terminal than from a table. The companion article builds a small Python tool that computes all three for one twelve-byte input; this is what it prints:

source            built-in sample (hello world\n)  (12 bytes)

WHAT THESE BYTES ARE — content addressing
  IPFS CIDv1      bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4
    version       1
    codec         raw
    hash          sha2-256
    digest        a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
  Blossom hash    a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447
    ^ the same 32 bytes; Blossom just does not wrap them

WHO SAID WHAT — event addressing
  Nostr event id  8d819e41e8d9fceb7d014076470d2dcebbc3774f2e49e9f80cbd31e7daec1cfa
  kind            1
  imeta url       ipfs://bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4
  imeta x         a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447

  The event id changes if the words change; the CID does not.
  The CID changes if the bytes change; the event id does not.

The line worth staring at is the repeated a948904f…. The IPFS CID and the Blossom hash are the same SHA-256 digest. IPFS wraps it in a version, a codec, a hash identifier, and a base32 encoding so the identifier describes itself; Blossom prints it bare because a Blossom server only ever speaks SHA-256 over HTTP and has nothing to disambiguate. Two systems people file under “competing approaches to storage” are, at the level of the name, doing the identical thing.

None of those three names says anything about where the data is or whether anyone still has it. That silence is the whole subject of what follows.

Where IPFS gets centralized in practice

IPFS the protocol is genuinely distributed: content is addressed by hash, any node can serve it, and a DHT is used to find who has it. IPFS applications are frequently not distributed at all, and the gap between those two statements is where most disappointment lives.

Four failure modes account for nearly all of it.

The gateway is a single point of failure. A CID does not depend on any one server. But the moment your application hands users https://gateway.example.com/ipfs/<cid>, that gateway is a centralized dependency with all the usual properties: it can rate-limit you, block a CID, receive a takedown notice, change its policy, or simply go down. The content is still on IPFS; your users cannot reach it through that URL.

The most common version of this is duller than censorship and just as fatal: the gateway shuts down. Free public gateways are run at somebody’s expense, and several well-used ones have been retired over the years. Every link anyone ever published through one dies with it, including links in places you no longer control: other people’s posts, archived pages, printed QR codes. The content is untouched and perfectly addressable, and the links are still dead, which is a precise illustration of the difference between an identifier and a route. Mitigation is straightforward and rarely done — treat gateways as interchangeable, carry several, and prefer clients that can resolve ipfs:// natively or run a local node.

Nothing is stored unless somebody pins it. “It’s on IPFS” is not a durability claim. IPFS guarantees that content can be retrieved from any node that has it, which is not the same as promising that a node still does. Upload a file, pin it on one node, lose that node, and the CID resolves to nothing — a permanent name for bytes nobody holds. Durability comes from several independent parties pinning it, which raises a question the protocol does not answer: who pays to keep it there? That is the real difference between “decentralized” and “durable.”

Content addresses are poor web addresses. https://example.com/images/cat.jpg means “ask this authority for this path.” ipfs://bafy… means “get me the content with this identifier,” which is strictly better for integrity and strictly worse for everything browsers, crawlers, bookmarks, and search engines are built around. DNSLink helps by putting a mutable, human-readable name in front of a CID, and reintroduces DNS as a dependency. That is worth stating more sharply than “dependency” usually implies. A domain is rented, not owned, and the registrar, the registry, and the TLD operator each have the technical means and a legal process for taking it away. A gateway that refuses to serve your CID is one adversary; a registrar that suspends the name pointing at it is a different one, higher up the stack, and no amount of content addressing reaches it. There is no free lunch here, only a choice about which authority you prefer to depend on.

A CID tells you nothing about provenance. Given ipfs://bafy…, the identifier says exactly what the bytes are and nothing about who made them, whether a newer version exists, or whether the publisher still stands behind them. Content addressing removes the need to trust the transport; it does not remove the need to trust the source.

IPFS is not empty-handed here, and it is worth being accurate rather than convenient. IPNS names are public keys: a name is a multihash of the public key, and for Ed25519 keys the key is inlined rather than hashed. Each record is signed by the corresponding private key, so anyone can verify who published it without asking an authority. That is real, self-certifying authorship, and any comparison that pretends otherwise is arguing against a weaker opponent than the one that exists.

What IPNS binds, though, is a key to a mutable pointer — this key currently says “go look at this CID.” It is not a signed statement with a timestamp, a thread it belongs to, a reaction, or a social graph around it. It answers “who controls this name and where does it point now,” not “who said what, when, and in what context.” That narrower gap is the one Nostr fills, and it is why the two compose rather than compete:

NOSTR — who, when, and in what context
  identity · authorship · timestamp · relationships · references
                        │
                        │  imeta tag carrying a CID
                        ▼
IPFS — what the bytes are
  images · video · documents · large assets

Nostr does not require IPFS; relays carry text and ordinary URLs perfectly well. IPFS does not require Nostr. The combination is worth reaching for when you want signed authorship over verifiable content.

Nostr’s mirror-image weakness

Having named the gateway problem, it would be dishonest to present Nostr as the part that is safely decentralized. Its version of the problem sits one layer over, in the relay.

A Nostr client publishes an event to a set of relays. Publish to several and the loss of any one is survivable — the event exists elsewhere, and a client configured with the others still finds it. Publish to one, and you have rebuilt the gateway problem exactly: a decentralized protocol deployed as a single point of failure.

This is not a fringe concern; it is the community’s own stock answer to censorship. Derek Ross’s talk puts the escalation plainly: if one relay bans you, your content is still on the others, and if every relay bans you, you run your own and tell your followers where to find it. That argument only works if you were publishing to several relays to begin with. Redundancy across relays is the mechanism, and a deployment that skips it keeps the protocol and discards the property.

The economics show up in the same talk, in a mundane form: relays come in free and paid varieties, and the paid ones tend to carry less spam because somebody is being paid to care. That is the “who pays” question surfacing as a product decision rather than a philosophical one.

Relays also choose what to keep. There is no protocol-level guarantee that a relay retains your events for any period, and none that it will serve them to anyone in particular. “The event is on Nostr” is the same category of claim as “the file is on IPFS” — true about naming and addressing, silent about durability.

The same uncertainty runs in the other direction, which is easy to miss. If you cannot know how long a relay keeps an event, you also cannot know that deleting one worked. A deletion request is a request — it is another event, and honoring it is a relay’s choice. Publishing widely for durability and retracting completely are therefore in direct tension: every extra relay that improves the first makes the second less certain. That is not a flaw to be fixed so much as an inherent property of handing signed copies to independent parties.

And the registrar problem above is not an IPFS problem. A relay is reached at wss://relay.example.com, a Blossom blob at https://media.example.com/<sha256>. Those are rented names too, revocable by the same authorities, so a rented name sits in front of the relay and the blob just as it does in front of a gateway.

Content addressing does bound the damage, and this is where it earns its keep. Lose the domain in front of a blob and you lose a route, not the object: the SHA-256 still names the same bytes, so the same blob served from anywhere else satisfies the reference and can still be verified. A location-addressed URL offers nothing equivalent, since there is no way to check that a replacement server returned what the original did.

Which gives the principle worth taking from all of this:

A decentralized protocol does not give you a decentralized deployment. Both are choices, and only the second one costs money.

Applied honestly, it produces a short checklist for any system claiming to be decentralized. How many independent parties hold a copy? How many independent paths can a client take to reach it? What breaks if the single most convenient one disappears? Who is paying to keep each answer above one?

That bound on the damage is a bound on content, though, not on workspaces. The next section shows what happens when a hostname is load-bearing for something other than fetching bytes.

Where Buzz fits

Buzz is a useful thing to hold this framework against, because it is unambiguously a Nostr system and it answers both questions above in ways that a slogan would get wrong. It is also not a toy: it comes out of Block, and it is shipping.

Buzz is a real Nostr relay, not a system inspired by Nostr. Its relay serves the NIP-11 information document, speaks NIP-01 over a WebSocket, and authenticates clients with NIP-42. A running instance says as much without a Buzz client — an ordinary curl fetches the relay’s own self-description, which is a claim about what it supports rather than a demonstration of it:

curl -s -H "Accept: application/nostr+json" http://localhost:3000/ \
  | uv run --no-project python -c "
import json, sys
d = json.load(sys.stdin)
for k in ('name', 'software', 'version'):
    print(f'{k:15} {d[k]}')
print(f'{\"supported_nips\":15} {d[\"supported_nips\"]}')
"

The command selects four of the document’s fields. Note that 43 appears in that list only when the relay is configured to enforce membership and has a stable signing key; a relay left on other settings advertises the other fourteen and nothing is wrong:

name            Buzz Relay
software        https://github.com/block/buzz
version         0.2.1
supported_nips  [1, 2, 10, 11, 16, 17, 23, 25, 29, 33, 38, 42, 50, 56, 43]

Every message, reaction, workflow step, and git event in Buzz is a signed Nostr event, and identity is a secp256k1 keypair exactly as described above. Agents get their own keypairs, which is what lets Buzz treat them as members rather than bots — an agent’s actions are attributable in the same log, by the same mechanism, as a person’s.

But its deployment is deliberately not distributed. The upstream README is explicit: “In the single-relay setup that ships today, the relay URL selects exactly one community.” One relay, one community, and the relay is one you own. Cross-relay features, such as web-of-trust reputation across relays, sit in the project’s “strong opinions, pending code” column rather than the shipped one.

That is a considered choice rather than an oversight, and it is worth naming precisely because it is the distinction this article is about. It is also a deliberate departure from the multi-relay redundancy that the previous section describes as the protocol’s own answer to a relay disappearing. Buzz uses Nostr’s data model (signed events, keypair identity, one auditable log) to get portable identity and verifiable history. It does not use Nostr’s topology to get redundancy. A Buzz community lives or dies with its relay, and the answer to “how many independent parties hold a copy” is one, unless you are taking backups. The project’s own framing is sovereignty, “a relay you own”, which is a different goal from resilience, and the two are easy to conflate.

There is a sharper edge to this than “back up your data”, and it is worth testing rather than assuming. Buzz keys a community to the authority of RELAY_URL, so the hostname is not merely how clients find the workspace — it is part of the workspace’s identity. Changing it on a running relay does not migrate anything:

# with RELAY_URL=ws://localhost:3000, then changed to ws://newdomain.example:3000
docker compose exec postgres psql -U buzz -d buzz -c \
  "select c.id, c.host, count(ch.*) as channels
     from communities c left join channels ch on ch.community_id = c.id
    group by c.id, c.host order by c.host;"
                  id                  |          host          | channels
--------------------------------------+------------------------+----------
 8e3ac0c4-8bf7-4e5b-b4db-7ca81ea07c96 | localhost:3000         |        1
 3654c1a2-43d6-4331-bfc8-9a55248622a3 | newdomain.example:3000 |        0

The relay provisions a second, empty community and leaves the first intact. Nothing is deleted and no error is raised; clients arriving at the new name land in a blank workspace while the history sits under the old host key. So losing a domain here is worse than losing a route to content — a CID survives its gateway, but a Buzz community does not obviously survive its hostname. Treat a domain change as a migration to plan, not a config edit, and note that the recovery is at least straightforward: the old community is still in Postgres, and pointing RELAY_URL back at the old authority restores it.

Sovereignty does buy something real, though, and it is worth stating because it is the honest counterweight. A relay sees more than the events it stores: the IP address a client connects from, and therefore a link between that address and a public key, plus the timing of everything that key does. Spreading events across several third-party relays for durability hands that metadata to several operators. Running the only relay your community uses hands it to you. Which trade is right depends on whether you are defending against your infrastructure disappearing or against it watching, and those call for opposite architectures.

And it does not use IPFS. Faced with the problem every event log has, that large media does not belong in it, Buzz answered with the third column of the table above: Blossom, with the media itself in an S3-compatible bucket behind the relay. Uploads are authorized by a signed kind:24242 event whose x tag must match the SHA-256 of the body, so the media layer authenticates with the same keypair that signs messages and no second account system exists.

Buzz implements a deliberately small slice of the spec: the only verbs it accepts are upload and get. No mirroring, and no per-user list of servers. Combined with one relay per community, that means a Buzz blob has exactly one place to be fetched from, the relay’s own media endpoint, and inherits that relay’s availability precisely. That is the IPFS trade-off taken in the opposite direction: no DHT to consult, no pinning economy to join, and no ambiguity about who is holding your data, because the answer is one server you run.

One footnote worth knowing if you deploy the bundled stack. Buzz’s production Compose bundle backs Blossom with MinIO, pinned at minio/minio:RELEASE.2025-09-07T16-13-09Z. The minio/minio repository has since been archived, with its README now opening THIS REPOSITORY IS NO LONGER MAINTAINED and pointing users to AIStor. Its last push was on 2026-04-24. The pinned release predates that, so it runs, and nothing about the Blossom design depends on MinIO specifically: it is one S3-compatible implementation and the relay talks to it over the S3 API. But an archived object store is not what you want under a production media layer indefinitely, and it is worth checking whether upstream has moved before standing one up for real.

Recap

Nostr and IPFS answer different questions, and the identifiers they hand you reflect that: an event id names a signed statement, a content address names bytes. Two of the three names in the table are the same SHA-256 digest wearing different amounts of clothing, which is a good reminder that “IPFS versus Blossom” is a question about packaging and retrieval rather than about the idea.

The takeaway that survives whichever protocols you pick is the harder one. IPFS is distributed as a protocol and routinely centralized as a deployment, via a single gateway and a single pin. Nostr is distributed as a protocol and routinely centralized as a deployment, via a single relay. Neither protocol has an answer for who pays to keep content available, and durability is the thing that costs money rather than the thing that comes free with the design.

Buzz is a clean illustration because it declines to pretend otherwise: a genuine Nostr relay, running as exactly one relay, keying a community to its hostname, storing media in an S3 bucket by SHA-256 rather than on IPFS — sovereignty rather than resilience, chosen on purpose. Whether that is the right trade depends on whether you are defending against your infrastructure disappearing or against it watching.

Where to go next:

  • Count your copies. For anything you have called decentralized, write down how many independent parties hold the data and how many independent paths reach it. If either number is one, you have a protocol choice and not a deployment.
  • Compute the identifiers yourself. Compute a Nostr Event ID, an IPFS CID, and a Blossom Hash for the Same Bytes builds the tool whose output is quoted above, in about 250 lines of standard library Python.
  • Run a relay and look at the events. Install and Use Buzz on macOS stands up the relay quoted here and includes a preflight tool for inspecting its NIP-11 document.