I've been exploring non-standard ways of hosting web content. It's led from NFC cards with data URLs and ENS+IPFS to an unexpected destination: Bluesky's AT Protocol as a hosting platform.
This experiment was inspired by Daniel Mangum's exploration of atproto's storage capabilities.
Most know Bluesky as a decentralized social platform, but the AT Protocol that powers it has intriguing capabilities. It's a content-addressable system with built-in content distribution. Every data piece gets a unique identifier (CID) retrievable through any AT Protocol node. Sound familiar? It should – it's remarkably similar to IPFS.
The idea is simple: if we can upload arbitrary blobs to Bluesky and get stable URLs, why not use it as a minimalist web hosting platform? Here's how to do it:
- First, create a session with your Bluesky credentials:
curl -X POST 'https://bsky.social/xrpc/com.atproto.server.createSession' \ -H 'Content-Type: application/json' \ -d '{"identifier": "your-handle.bsky.social", "password": "your-password"}'
Save the accessJwt
from the response for the next steps.
- Create an HTML file (
index.html
) with your website content. - Upload your HTML file as a blob:
curl -X POST 'https://bsky.social/xrpc/com.atproto.repo.uploadBlob' \ -H 'Authorization: Bearer YOUR_ACCESS_JWT' \ -H 'Content-Type: text/html' \ --data-binary '@index.html'
The response will include a blob reference with a CID. Save this CID for the next step.
- Create a record referencing your blob:
curl -X POST 'https://bsky.social/xrpc/com.atproto.repo.createRecord' \ -H 'Authorization: Bearer YOUR_ACCESS_JWT' \ -H 'Content-Type: application/json' \ -d '{ "repo": "your-handle.bsky.social", "collection": "com.yourdomain.website", "record": { "$type": "com.yourdomain.website", "website": { "$type": "blob", "ref": { "$link": "YOUR_BLOB_CID" }, "mimeType": "text/html", "size": YOUR_FILE_SIZE } } }'
- Get the URL of your website:
curl -I 'https://bsky.social/xrpc/com.atproto.sync.getBlob?did=YOUR_DID&cid=YOUR_BLOB_CID'
The response includes a location
header with your website's permanent URL. Here's a real example:
https://amanita.us-east.host.bsky.network/xrpc/com.atproto.sync.getBlob?did=did:plc:p5xem22ammiafn5kxonaksfa&cid=bafkreih2pbifus4ed6p7kfuqjbjbqmzewlnyaz7f7ykmk5bvchkj7w3eb4
Breaking down this URL reveals the system’s elegance:
amanita.us-east.host.bsky.network
is one of Bluesky's content delivery nodescom.atproto.sync.getBlob
handles blob retrieval- The
did
parameter identifies the content owner - The
cid
parameter is the content hash
There are trade-offs. You're dependent on Bluesky's infrastructure. There are blob size restrictions. It's not designed for this use case, and there's a potential for content removal. The simplicity is appealing.
This is not "pure" internet in terms of independence, but it is close since we're relying on federated infrastructure. It raises questions about the evolution of social protocols. Could they become general-purpose content distribution networks?