The State of the Decentralized Web in 2026
ActivityPub, AT Protocol, and Solid compared on identity, data, followers, adoption, and migration in the decentralized web of 2026.
A social network is decentralized in a practical sense when a user can switch providers without losing their identity, their data, or their followers, and ActivityPub, the AT Protocol, and Solid each pass a different subset of that test.
Picking a server and following a few accounts is the easy part. The vocabulary that comes with these networks (PDS, relay, AppView, Pod, WebID) piles up quickly, and none of it matters much until the day you want to move.
This is a status report on the three protocols that matter for the decentralized web today. It explains how each one models identity, data and audience, states where each stands in adoption and specification terms, keeps registered accounts separate from active users in the figures, and ends with a service you can run in an afternoon.
Key Takeaways
- On ActivityPub a handle such as @user@instance.example belongs to the instance, so an account that was not migrated before its server shut down cannot be recovered anywhere else.
- On the AT Protocol identity is a DID that no server owns, and an account can move between Personal Data Servers without the DID changing, though migration is still CLI-driven rather than in-app.
- Solid Protocol is a W3C Community Group draft (v0.11.0, May 2024), not a W3C standard, and no new version has been published since.
- Bluesky’s 2025 Transparency Report counted 41.41 million accounts at the end of 2025 and does not disclose monthly actives; third-party trackers put Mastodon at around 10 million registered accounts against under one million monthly actives.
- A Bluesky custom feed is an HTTP service that returns an ordered list of post URIs; the AppView hydrates them, so the feed never stores post content.
What Does Decentralized Mean in Practice?
Decentralized means you can leave whoever hosts your account and arrive somewhere else with three things intact: your identity (the name people know you by and the credentials behind it), your data (posts, media, follows, settings), and your audience (the people who follow you and keep receiving what you publish). Lose any one of the three and the switch is a fresh start with extra steps.
Each protocol below is measured against that test, not against how many servers exist or who wrote the software. Server count tells you about redundancy. The three-part test tells you about lock-in.
How Does ActivityPub Federate Between Instances?
ActivityPub, a W3C Recommendation since 23 January 2018, federates by having each server deliver a user’s activities directly to the inboxes of the servers hosting that user’s followers. Every user is an actor with an inbox and an outbox; publishing writes to your outbox, and your server POSTs the activity to each follower’s inbox. There is no central index, only servers pushing JSON to one another.
Mastodon, Pixelfed, Lemmy and PeerTube all implement it, which is why a Mastodon account can follow a PeerTube channel or a Lemmy community.
Identity is where the test fails. A handle such as @user@instance.example belongs to the instance, so an account that has not been migrated before its server shuts down cannot be recovered anywhere else. Mastodon’s account move sends a Move activity that switches followers over to the new account, but the documentation is clear that everything you posted stays behind, and the archive you can download of it cannot be loaded into another Mastodon account. Scored against the test: audience survives a planned move, data partly survives as an export, identity does not survive at all.
AT Protocol: Portable Identity, Concentrated Infrastructure
On the AT Protocol, a user’s identity is a DID that no server owns, and their posts, follows, and profile live in a Personal Data Server (PDS) that can be moved to a different host without the DID changing. Above the PDS sit relays, which crawl every PDS and emit one firehose, and AppViews, which index that firehose into the product a client renders. The specification is published and maintained by Bluesky Social PBC.
Portability has limits. The official account-migration guide treats these steps as something sitting outside the protocol proper that may change later, and it sends you to the goat CLI or a community tool rather than an in-app flow. Bryan Newbold, a Bluesky protocol engineer, wrote in October 2025 that moving an account has worked on the live network since early 2025 but is still a job for developers, and that the PDS hosts Bluesky runs will not take an account moving in.
The second qualification is operational. Bluesky’s 2025 Transparency Report says the company holds more accounts than anyone else and is where most people sign up, with the rest of the network spread across thousands of Personal Data Servers, most of them run by other people. No official figure quantifies what share of the network flows through the relay and AppView Bluesky operates. Critics argue that independent PDS hosts and community infrastructure projects still lean on Bluesky-operated core services, and some conclude the network is not decentralized in practice; that is a judgment, not a measurement. Scored against the test: identity and data survive a provider change by design, followers come along because follow records reference DIDs, and the layers above the PDS remain mostly one company’s.
Solid: Data Pods Without a Consumer Market
Solid stores a user’s data in a Pod the user controls and requires each application to request access. A WebID identifies the person, the Pod is an HTTP server holding RDF resources, and apps read and write those resources once authorization rules permit it. On the provider-change test Solid has the cleanest design of the three: identity, data and access rules all live with the user, and an app is just a client.
Adoption does not match the design. Solid Protocol is a W3C Community Group draft (v0.11.0, 12 May 2024), not a W3C standard, and the TR index shows no new version published since; the preceding releases were 0.9.0 (December 2021) and 0.10.0 (December 2022), and work on 0.12.0 sits in the editor’s draft. The extension specs apps depend on, WebID Profile, Type Indexes and Application Interoperability, are still drafts, and the spec permits two incompatible authorization systems, WAC and ACP, with servers free to implement either. Tooling and documentation trail both other protocols.
The practical blocker is where to sign up. solidproject.org lists around fifteen hosted pod services, most small or experimental, and Inrupt, the company Tim Berners-Lee co-founded, now markets enterprise wallet infrastructure to businesses. Solid app developer Noel De Martin, writing in 2024, named the missing consumer pod market as the biggest thing holding Solid back: asking someone to log in with Solid sends them off to find a provider first, and that is where most of them stop.
| ActivityPub | AT Protocol | Solid | |
|---|---|---|---|
| Standards body | W3C Recommendation (2018) | Bluesky Social PBC | W3C Solid Community Group draft |
| Identity object | Instance-bound handle | DID | WebID |
| Where data lives | Your instance | Your PDS | Your Pod |
| Survives a provider change | Followers (planned move only) | Identity, data, followers | Identity, data, access rules |
| Largest deployment | Mastodon | Bluesky | No consumer deployment at scale |
How Many Users Do Bluesky and Mastodon Have?
Bluesky’s 2025 Transparency Report (29 January 2026) counted 41.41 million accounts at the end of 2025, up from 25.94 million, across Bluesky-hosted and independent PDSs; the company tracks but does not publish a monthly active user figure. The report normalises its moderation data per 1,000 monthly active users without stating the denominator.
Mastodon does publish a network-wide figure of its own, on the servers page at joinmastodon.org, and third-party trackers that poll instance statistics APIs publish theirs. The numbers do not agree. TechCrunch reported in February 2026 that Mastodon’s own site gave around 785,000 monthly actives, while trackers ranged from about 750,000 to a million depending on which one you asked. Those trackers put registered accounts at somewhere around 10 million across roughly 10,000 servers, though the totals move by a million or more between trackers, depending on how each one handles servers that have gone quiet.
| Network | Registered accounts | Monthly actives | Source and date |
|---|---|---|---|
| Bluesky | 41.41M (end of 2025) | Not published | Bluesky 2025 Transparency Report, Jan 2026 |
| Mastodon | ~10M, varies by tracker | ~785k on Mastodon’s own page, 750k to 1M per trackers (Feb 2026) | Mastodon servers page, third-party trackers |
| Solid | No published figures | No published figures | None |
Registrations and actives differ by an order of magnitude on Mastodon, and Bluesky’s ratio is unknown. A comparison that sets one network’s registrations against another’s actives is comparing different quantities.
What Can You Build on ActivityPub, Solid and Bluesky?
All three protocols are plain HTTP, which is why a terminal is enough to inspect them:
# ActivityPub: resolve a handle with WebFinger, then fetch the actor document
curl -s 'https://mastodon.social/.well-known/webfinger?resource=acct:Mastodon@mastodon.social'
curl -s -H 'Accept: application/activity+json' https://mastodon.social/users/Mastodon
# Solid: read an RDF resource from a pod as Turtle (replace with a real pod URL)
curl -s -H 'Accept: text/turtle' https://alice.example.org/profile/card
WebFinger is RFC 7033 and a Mastodon-ecosystem convention, not part of ActivityPub itself. Solid servers must serve RDF resources as Turtle or JSON-LD on request.
The most approachable project is a Bluesky custom feed. A Bluesky custom feed is an HTTP service that returns an ordered list of post URIs; the AppView fetches and renders those posts itself, so the feed service never has to store or serve post content. Two XRPC routes are required, and the getFeedSkeleton lexicon defines the parameters (feed, limit up to 100, cursor) and the UnknownFeed error:
// feedgen.mjs — run with: node feedgen.mjs
import { createServer } from 'node:http';
const SERVICE_DID = 'did:web:feeds.example.com';
const FEED_URI = 'at://did:plc:yourpublisherdid/app.bsky.feed.generator/weekend';
// A real generator fills this from a firehose consumer or a database.
const POSTS = [
'at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.feed.post/3jux6xlrdb42v',
'at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.feed.post/3jux7x2uvip2v',
];
const json = (res, status, body) => {
res.writeHead(status, { 'content-type': 'application/json' });
res.end(JSON.stringify(body));
};
createServer((req, res) => {
const { pathname, searchParams } = new URL(req.url, 'http://localhost');
if (pathname === '/xrpc/app.bsky.feed.describeFeedGenerator') {
return json(res, 200, { did: SERVICE_DID, feeds: [{ uri: FEED_URI }] });
}
if (pathname === '/xrpc/app.bsky.feed.getFeedSkeleton') {
if (searchParams.get('feed') !== FEED_URI) {
return json(res, 400, { error: 'UnknownFeed', message: 'Feed not found' });
}
// Requests may carry a JWT signed by the user's key. A feed that is
// identical for every user can ignore it, as this one does.
const limit = Math.min(Number(searchParams.get('limit') ?? 50), 100);
const start = Number(searchParams.get('cursor') ?? 0);
const page = POSTS.slice(start, start + limit);
const cursor = start + limit < POSTS.length ? String(start + limit) : undefined;
return json(res, 200, { feed: page.map((post) => ({ post })), cursor });
}
json(res, 404, { error: 'NotFound' });
}).listen(3000);
The cursor is opaque to the AppView and entirely your own; a position index is enough for a fixed list, and the official tutorial recommends a timestamp-plus-CID compound cursor once posts come from a live index. To go live you deploy over HTTPS, publish a DID document that points at the host (the starter kit sets this up as a did:web by default), and run its publishFeedGen.ts script to create the app.bsky.feed.generator record in your own repo. From then on the feed appears in the Bluesky app like any other.
Where Things Stand
Measured against the provider-change test, ActivityPub carries followers but not identity, the AT Protocol carries all three on paper while most of the network still runs through one company’s relay and AppView, and Solid carries everything in principle without a consumer market to carry it for. Pick the protocol whose failure mode you can live with, then spend a weekend on the feed generator above: it is the shortest path from reading about the decentralized web to running a piece of it.
FAQs
Can Bluesky and Mastodon users follow each other?
Not natively: ActivityPub and the AT Protocol do not interoperate, so cross-network follows go through a bridge. Bridgy Fed, run by the non-profit A New Social, is opt-in: a Bluesky user follows @ap.brid.gy, and a fediverse user follows @bsky.brid.gy@bsky.brid.gy. Bridged accounts appear as user.instance.ap.brid.gy on Bluesky and handle@bsky.brid.gy in the fediverse. Only fully public posts are bridged, and fediverse posts over 300 characters are truncated on Bluesky.
What is the difference between did:plc and did:web on the AT Protocol?
The AT Protocol supports only two DID methods. did:plc, developed by Bluesky Social PBC, registers identifiers in the PLC directory, supports key rotation and recovery, and is what the official PDS installer creates for new accounts. did:web derives the identifier from a hostname you control, allows only hostname-level identifiers with no paths, and provides no recovery if you lose the domain, so it suits services such as feed generators.
Can I use my own domain as my Bluesky handle without hosting a PDS?
Yes. A handle is a DNS hostname linked bidirectionally to your DID, and that link is independent of which PDS stores your data. Publish the DID in one of two places: a DNS TXT record named _atproto.yourdomain with the value did= followed by your full DID, or a plain-text response at https://yourdomain/.well-known/atproto-did. The spec recommends the DNS method for individuals; the HTTPS method targets large services.
If I self-host a Personal Data Server, can I still use the Bluesky app?
Yes. The official PDS ships as a Docker image for a VPS with a public IPv4 address, a wildcard DNS record and ports 80 and 443 open; Bluesky recommends 1 GB RAM and 20 GB storage for 1 to 20 users. Sign in by entering your PDS URL in the app. Default configuration points at Bluesky's AppView, relay and PLC directory, so the PDS still depends on Bluesky-operated services above the data layer.