Where It's at://
From handles to hosting.
Where It's at://
October 2, 2025
You might have heard about the AT protocol (if not, read this!)
Together, all servers speaking the AT protocol comprise the atmosphere—a web of hyperlinked JSON. Each piece of JSON on the atmosphere has its own at:// URI:
at://ruuuuu.de/app.bsky.feed.post/3lzy2ji4nms2z
at://danabra.mov/sh.tangled.feed.star/3m23ddgjpgn22
at://tessa.germnetwork.com/pub.leaflet.publication/3lzz6juivnc2d
But where do they point, exactly?
Given an at:// URI, how do you locate the corresponding JSON?
In this post, I’ll show you the exact process of resolving an at:// URI step by step. Turns out, this is also a great way to learn the details of how at:// works.
Let’s start with the structure of a URI itself.
The User as the Authority
As you might know, a URI often contains a scheme (for example, https://), an authority (like wikipedia.com), a path (like /Main_Page), and maybe a query.
In most protocols, including https://, the authority part points at whoever’s hosting the data. Whoever created this data is either not present, or is in the path:
The at:// protocol flips that around.
In at:// URIs, whoever created the data is the authority, in the most literal sense:
The user is the authority for their own data. Whoever’s hosting the data could change over time, and is not directly included in an at:// URI. To find out the actual physical server hosting that JSON, you’re gonna need to take a few steps.
A Post in the Atmosphere
Let’s try to resolve this at:// URI to the piece of JSON it represents:
An easy way to resolve an at:// URI is to use an SDK or a client app. Let’s try an online client, for example, pdsls or Taproot or atproto-browser. They’ll figure out the physical server where its JSON is currently hosted, and show that JSON for you.
The above at:// URI points at this JSON, wherever it is currently being hosted:
"uri": "at://did:web:iam.ruuuuu.de/app.bsky.feed.post/3lzy2ji4nms2z",
"cid": "bafyreiae4ehmkk4rtajs5ncagjhrsv6rj3v6fggphlbpyfco4dzddp42nu",
"value": {
"text": "posting from did:web, like a boss",
"$type": "app.bsky.feed.post",
"langs": ["en"],
"createdAt": "2025-09-29T12:53:23.048Z"
You can guess by the $type field being "app.bsky.feed.post" that this is some kind of a post (which might explain why it has fields like text and langs).
However, note that this piece of JSON represents a certain social media post itself, not a web page or a piece of some app. It’s pure data as a piece of JSON, not a piece of UI. You may think of the $type stating the data format; the app.bsky.* prefix tells us that the bsky.app application might know something about what to do with it. Other applications may also consume and produce data in this format.
A careful reader might notice that the uri in the JSON block is also an at:// URI but it’s slightly different from the original at:// URI we requested:
// What's at://ruuuuu.de/app.bsky.feed.post/3lzy2ji4nms2z ?
"uri": "at://did:web:iam.ruuuuu.de/app.bsky.feed.post/3lzy2ji4nms2z",
// ...
In particular, the short ruuuuu.de authority has expanded into a longer did:web:iam.ruuuuu.de authority. Maybe that’s the physical host?
Actually, no, that’s not the physical host either—it’s something called an identity. Turns out, resolving an at:// URI is done in three distinct steps:
- Resolve the handle to an identity (“who are you?”)
- Resolve that identity to a hosting (“who holds your data?”)
- Request the JSON from that hosting (“what is the data?”)
Let’s go through each of these steps and see how they work.
From Handles to Identities
The at:// URIs you’ve seen earlier are fragile because they use handles.
Here, ruuuuu.de, danabra.mov, and tessa.germnetwork.com are handles:
at://ruuuuu.de/app.bsky.feed.post/3lzy2ji4nms2z
at://danabra.mov/sh.tangled.feed.star/3m23ddgjpgn22
at://tessa.germnetwork.com/pub.leaflet.publication/3lzz6juivnc2d
(Read more about domains as “internet handles” here.)
The user may choose to change their at:// handle later, and it is important for that not to break any links between pieces of JSON already on the network.
This is why, before you store an at:// URI, you should turn it into a canonical form by resolving the handle to something that never changes—an identity. An identity is like an account ID, but global and meant for the entire web. There are two mechanisms to resolve a handle to an identity (also known as a “DID”):
- Query the DNS TXT record at
_atproto.<handle>looking fordid=???
- Make an HTTPS GET to
https://<handle>/.well-known/atproto-did
The thing you’re looking for, the DID, is going to have a shape like did:something:whatever. (We’ll revisit what that means later.)
For example, let’s try to resolve ruuuuu.de via the DNS mechanism:
$ nslookup -type=TXT _atproto.ruuuuu.de
Server: 192.168.1.254
Address: 192.168.1.254#53
Non-authoritative answer:
_atproto.ruuuuu.de text = "did=did:web:iam.ruuuuu.de"
Found it!
The ruuuuu.de handle claims to be owned by did:web:iam.ruuuuu.de, whoever that may be. That’s all that we wanted to know at this point:
Note this doesn’t prove their association yet. We’ll need to verify that whoever controls the did:web:iam.ruuuuu.de identity “agrees” with ruuuuu.de being their handle. The mapping is bidirectional. But we’ll confirm that in a later step.
Now let’s try to resolve danabra.mov using the DNS route:
$ nslookup -type=TXT _atproto.danabra.mov
Server: 192.168.1.254
Address: 192.168.1.254#53
Non-authoritative answer:
_atproto.danabra.mov text = "did=did:plc:fpruhuo22xkm5o7ttr2ktxdo"
That also worked! The danabra.mov handle claims to be owned by the did:plc:fpruhuo22xkm5o7ttr2ktxdo identity, whoever that may be:
This DID looks a bit different than what you saw earlier but it’s also a valid DID. Again, it’s important to emphasize we’ve not confirmed the association yet.
Subdomains like barackobama.bsky.social can also be handles.
Let’s try to resolve it:
$ nslookup -type=TXT _atproto.barackobama.bsky.social
Server: 192.168.1.254
Address: 192.168.1.254#53
Non-authoritative answer:
*** Can't find _atproto.barackobama.bsky.social: No answer
The DNS mechanism didn’t work, so let’s try with HTTPS:
$ curl https://barackobama.bsky.social/.well-known/atproto-did
did:plc:5c6cw3veuqruljoy5ahzerfx
That worked! This means that barackobama.bsky.social handle claims to be owned by the did:plc:5c6cw3veuqruljoy5ahzerfx identity, whoever that is:
[...]