Background
I will start with a disclaimer: Iâm all-in on Nostr. But before that, I spent 4 years building on ActivityPub. Then in Feb 2023 I built a bridge between ActivityPub and Nostr, and in Dec 2024 I built another bridge between Nostr and Bluesky. Most of all I am committed to open source and the decentralized vision. Having experience with all 3 major protocols, I still think Nostr is the best, but Bluesky outshines it in some major areas. The main reason for this blog is to explore the things Bluesky does better than us, and to point out why I still think Nostr is the best solution.
This is a technical blog. For a more high level overview of decentralized protocols, see: https://soapbox.pub/blog/comparing-protocols/
Data Model
Nostr and Bluesky both allow users to store any type of data in a single, unified format. Nostr calls them events. Bluesky calls them records. They are mostly interchangeable ideas, but the way the data is stored and retrieved is very different.
Itâs worth noting that, like Nostr and ActivityPub, Bluesky is capable of doing anything under the sun. Itâs a misconception that it canât. You can build whatever Reddit, TikTok, or other stuff clone on Bluesky just fine. Itâs limited more by the huge knowledge gap you have to overcome than it is by anything about its data model.
NSIDs vs Kinds
Nostr events use a âkindâ number, eg 1
, to distinguish different type of events. Bluesky records use what they call an âNSIDâ (namespace identifier), eg app.bsky.feed.post
, which is a reverse-DNS string notation.
Nostrâs "kind": 1
is equivalent to Blueskyâs "$type": app.bsky.feed.post
.
I think Blueskyâs system is better, because it allows developers to feel like what they built is ârealâ without having to get it merged into the NIPs repo, which is basically like an IANA of Nostr kind numbers. I believe this causes significant problems on Nostr, and makes people feel like they are being held back by others. That being said, the counter-argument is that kind numbers encourage interoperability for the very same reasons. The barriers to adding new kinds pushes people to work with whatâs already there.
Text Content Formatting
Kind 1 events (âplain text notesâ, ie âTweetsâ) on Nostr have developed in a haphazard way over time, by many people piling things onto it without taking a step back to assess a unified way to handle it. Some examples include:
- NIP-21
nostr:
URIs imeta
tags- inline media embeds as URLs without
imeta
tags - unspecâd Markdown rendering by some clients
- legacy specs such as positional mentions (eg,
#[0]
, mostly gone now)
What we have now is quite a mess, and itâs something Bluesky beats us on badly.
Bluesky has a plaintext content field that can be displayed as-is. In addition, it has a âfacetsâ field, which is a structured JSON object, that adds rich-text information such as formatting (bold, italic, etc), links, mentions of users, and whatever else metadata about the text.
This is an extensible system that beats even options like Markdown, due to its ability to include native extension like mentions, and its ability to be parsed by any programming language or software environment.
Bluesky is the winner here.
Syncronization
Nostr and Bluesky are both thought to be descended from Scuttlebutt, an older decentralized protocol.
On Scuttlebutt, a userâs whole post history needed to be available for them to make a new post. Scuttlebutt uses a linked list, so each new event would need to reference the one before it. Only linear paths are allowed, so if a âforkâ occurred (intentionally or not), only one version would be kept, and the other discarded. This lead to occasional publishing issues, but it allowed readers to assemble a complete view.
Nostr strayed from this draconian approach, removing it entirely and allowing user data to be fragmented. Meanwhile Bluesky, instead of removing it entirely, made it work more like git so that branches could be merged.
Both approaches have tradeoffs. Blueskyâs approach has much higher complexity. Also, itâs sometimes considered an advantage for events to be fragmented (eg Nostr allows sending DMs to specialized relays for enhanced privacy).
But Bluesky has a true account âsyncâ mechanism, and Nostr does not. Nostr can send filters to relays to gather events, but it cannot know when to stop looking. Proposals like Negentropy in Nostr do not solve author syncing, and only make typical relay filtering more efficient.
I think itâs important to think about Nostrâs âoutboxâ problem as a syncronization problem, and for Nostr to approach syncronization with the goal of syncronizing authors specifically.
I think Bluesky âwinsâ this one, if only because they have solved a problem that we havenât. Instead of copying their solution, I think Nostr should try to learn from this to recharacterize the âoutboxâ problem as an author syncing problem, and see if we can come up with a better solution that works for Nostr.
Knowledge Gap
One of the biggest hurdles of Bluesky, and by extension one of the greates appeals of Nostr, is in how easy it is to learn and build on.
Nostr can be understood in a couple of hours. Mastering it is difficult, but limited only by your time and imagination. Hundreds of developers are building new projects on Nostr today. Based on my experience, it seems to me that the Nostr developer community is larger than that of both ActivityPub and Bluesky combined.
Bluesky on the other hand requires a Harvard degree in Blueskyology. I believe that ameteur coders feel a sense of superiority after spending six weeks learning how to commit a post. To learn Bluesky you will have to sift through thousands of technical documents about all kinds of abstractions, especially related to IPFS and IPLD. You will have to master unnecessary technologies like CBOR (which is basically just JSON except itâs in binary and takes up the same amount of space). And you will be frequently pointed to academic topics such as âDAGâ and âgraph theoryâ, all just to say âitâs a fucking graphâ. I will never understand why people will take simple concepts such as a âtreeâ, and then make it harder to understand on purpose by saying itâs more than that.
While building on Bluesky, I began to question if the creators did this on purpose. I wondered if they tried to make it complex on purpose, as a sort of IQ test and protective measure to weed out the undeireables. In my view, a truly free and open network must be accessible to all, with the hope that even non-programmers could learn how it works.
Technical complexity is not just an issue of inclusion, but also security. ActivityPub software has been found to be littered with major security holes due to its inherently complex design, while Nostrâs simple design makes its attack surface extremely minimal. Bluesky has already had a few mishaps, and it makes me wonder how Bluesky will fare in the long term.
Decentralization
I will keep this short. Bluesky is designed to be decentralized, but isnât. It reminds me of the communist idea about the âwithering away of the stateâ. The idea is that youâre supposed to first sieze power and become the new state, and then under your rule the state will slowly disappear because you are doing all the right communist things. I think this is basically how Bluesky sees itself (whether they agree with the analogy or not).
Nostr is decentralized, but it is a loaded gun. Youâre more likely to kill yourself with a gun than someone else. This is on purpose, because it wholly embraces the consequnces of being truly decentralized.
In my view Nostr gets it right. Social media has been done to death by now, so I do not think itâs worth the compromise to prioritize UX over decentralization. If thatâs the case we have not fixed anything. Itâs best to start with the purist idea and work backwards to UX, rather than start with UX and work backwards to decentralization. Many people have already written extensively about the decentralization of Bluesky, so Iâll leave it at that.