Skip to content

Instantly share code, notes, and snippets.

@oofdere
Created February 8, 2026 07:30
Show Gist options
  • Select an option

  • Save oofdere/7777f2f834313b3a3cb5f29d8eb036b6 to your computer and use it in GitHub Desktop.

Select an option

Save oofdere/7777f2f834313b3a3cb5f29d8eb036b6 to your computer and use it in GitHub Desktop.

The post says community posts go “to their backend at blacksky.app instead of your PDS, the same way a centralized platform like Twitter would.” This is incorrect. Look at communityXrpc in community.ts — it uses the atproto-proxy header through the PDS.

That means the flow is: your client → your PDS → PDS validates your credentials → PDS mints a service auth JWT signed by your keypair → PDS forwards to Blacksky’s AppView. Your PDS is fully in the authentication loop. This is standard ATProto service proxying, not a Twitter-style bypass.

The flows in the post are simplified as much as possible, because I wanted audiences that don't have a technical understanding of the protocol to be able to follow what is going on.

/**
 * Make an XRPC call to a community.blacksky.feed.* endpoint.
 *
 * Calls through the PDS using the agent's session auth and atproto-proxy
 * header. The PDS validates the user's credentials, creates a service auth
 * JWT signed by the user's keypair, and forwards the request to the appview.
 */

^ this is the doc comment for communityXrpc

it does indeed proxy requests through the user's PDS and add the JWT as you say, but that's all it does. if I connect to a centralized service through a proxy it doesn't magically make it decentralized, so I'm not sure what your point here is.

This technicality changes nothing about what I said in the post, unless I'm missing something.

Your post also implies you must use the Blacksky client to see community posts. But hydration in ATProto doesn’t happen in the client — it happens at the AppView. The Bluesky mobile app is a thin renderer. It gets back fully hydrated FeedViewPost objects and just displays them.

This is an interesting point, it won't work with the vanilla app, but it does mean it might be possible for clients to add that functionality, which would be awesome! Though I am still concerned about fragmentation, since such implementations wouldn't scale if a bunch of networks add community posts in the same way.

I'll update the post to make a note of this.

So who decides which AppView does the hydration? Your PDS does. And here’s the key: Bluesky’s own PDS is the outlier. It hardcodes a default route to api.bsky.app for app.bsky.* requests. Bluesky’s own 2025 roadmap calls this out and says clients should stop relying on it.

Every other PDS — rsky, self-hosted, any third-party — uses the atproto-proxy header as the protocol intends. If you’re on a Blacksky PDS using the official Bluesky app, your requests route through Blacksky’s AppView and community posts show up hydrated like any other post. No special client needed.

How would the client know to set the atproto-proxy to go through the right service if on a different Appview? But apart from that it seems like a very elegant solution.

I'm dissapointed but not surprised that the bsky PDS doesn't follow their own spec. I also have other issues over a year old on the PDS side that have yet to be addressed.

The comparison to XMPP proprietary extensions doesn’t hold either. Blacksky isn’t extending the protocol with a locked-down format — they’re using a custom Lexicon namespace (community.blacksky.feed.*) with standard XRPC proxying. Any AppView can choose to index and serve these posts.

This argument was made for XMPP as well, that clients could simply ignore the extensions they didn't support. I don't agree, but only time will tell.

Any Appview can index them, but they have to index the right lexicon for every service that has this kind of post, and they can't serve them as there's no way for them to access the data without going through another Appview.

You could argue just serving the stub counts, and I'd agree fully if the PDS did the hydration with its own data rather than an Appview doing it through its own DB, but with this implementation I'm iffy on it.

There IS a real but narrower concern: the full post content lives in the AppView database, not your PDS repo (just a stub with a CID). If Blacksky’s AppView goes down, those posts disappear from feeds even though stub records survive. That’s worth discussing honestly.

But “your data isn’t fully in your repo” is also true of Bluesky’s own DMs and bookmarks, which the post acknowledges. The fix is the same in both cases: push for protocol-level solutions. That’s a constructive conversation worth having without mischaracterizing the architecture.

This concern is the main point of the post, that the content is indeed centralized and out of the user's control, and at both the start of the post, I say that protocol work is what is needed to do all of this right. If that didn't adequetly come across, I am sorry.

I do however make a distinction in the post--bookmarks and to a much lesser extent DMs are more "metadata" in a sense rather than actual content that was ever meant to be federated through the network, accessible to one or two people respectively. (although I don't think they should've have been implemented without the protocol work to keep them on the PDS either)

And this also doesn't change the point that you don't own your data in this implementation; the Blacksky Appview still has control over your post, regardless of if other Appviews hydrate it. (yes, this is also true of bookmarks and DMs, and bad for them too, but those don't have an audience, unlike community posts, so I personally think the stakes aren't as high)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment