Note: I communicated with the Bluesky team prior to the publishing of this post. While the functionality described is not the intended use of the application, it is known behavior and does not constitue a vulnerability disclosure process. My main motivation for reaching out to them was because I like the folks and don’t want to make their lives harder.
Being able to host a website on Bluesky really has very little to do with Bluesky itself. I happen to use Bluesky for hosting my Personal Data Server (PDS), but all of the APIs leveraged in uploading the site contents are defined at the AT Protocol level and implemented by a PDS. Bluesky offers access to my PDS via their PDS entryway, which allows for the many (have you heard that they are growing by a million users per day?) PDS instances they run to be exposed via the bsky.social domain. That being said, individual PDS instances can be accessed directly, and if you clicked the link at the top of this post to access the Bluesky hosted website, then you have already visited mine at porcini.us-east.host.bsky.network.
Most social applications, and many applications in general for that matter, broadly have two primary types of content: records and blobs. Records are the core entity types that users create. They generally have some defined structure and metadata, and they may reference other records or content. Blobs are typically larger unstructured data, such as media assets, that may be uploaded by a user, but are exposed via a record referencing them. For example, on Bluesky a user may upload an image, then create a post that references it. From an end-user perspective, these two operations appear to be one action, but they are typically decoupled at the API level.