Bluesky bot hosting on the AT Protocol SDKs
Bluesky's own SDK for Node.js and the atproto client for Python both log in with a handle and an app password and post from there. Push the repository, set the two values, deploy: the bot runs around the clock and redeploys on every commit.
Node.js, with the atproto SDK
const { AtpAgent } = require("@atproto/api");
const agent = new AtpAgent({ service: "https://bsky.social" });
async function main() {
await agent.login({
identifier: process.env.BSKY_HANDLE,
password: process.env.BSKY_APP_PASSWORD,
});
await agent.post({ text: "hello from FadeHost" });
}
main();@atproto/api in dependencies, a start script, the handle and the app password in the environment. A bot that posts on a schedule keeps the schedule in the process, because the process is always on.
Python, with the atproto client
import os
from atproto import Client
client = Client()
client.login(os.environ["BSKY_HANDLE"], os.environ["BSKY_APP_PASSWORD"])
client.send_post("hello from FadeHost")atproto in requirements.txt and that is the deployment. Both SDKs speak to the same XRPC endpoints over HTTPS, so either fits the free plan's outbound list.
App passwords, and the session
Use an app password from your Bluesky settings, not the account password: it is scoped, it is revocable in one click if it leaks, and it belongs in an environment variable. Log in once and keep the session rather than logging in on every loop. Writing the session under /data means a restart resumes instead of authenticating again, which is gentler on the service and faster to start.
Watching the firehose
A bot that reacts to posts rather than writing them is a long-running WebSocket consumer, which is exactly what this runtime is for: no public address, no port, just a process that stays connected. A filtered Jetstream consumer fits the free 256 MB comfortably. Subscribing to the full firehose and validating every record is heavier and wants a paid tier with 1 GB, which you can take up to 4 GB for $1 a gigabyte.
Frequently asked questions
Which SDK should I use?
@atproto/api on Node.js and atproto on Python are the two maintained clients, and both cover posting, replying, following and reading timelines. Anything that speaks XRPC over HTTPS works.
Do I need an app password?
Yes, and it is the right thing to use: create one in Bluesky's settings, put it in an environment variable, revoke it there if it leaks. Your account password should not be in a hosted process.
Can the bot run on its own domain handle?
The handle is set on your Bluesky account, not here, so a domain handle works the same as a bsky.social one. The bot only reads the handle from its environment.
Can a free bot reach the API it needs?
A free app's outbound traffic follows a fixed list: DNS, HTTP and HTTPS on the usual ports, the database ports, and some game and voice ports. Anything that calls an API over HTTPS is covered. A paid app may reach everything, or whatever list you give it, and the Firewall tab names what was refused.
What happens when I push a commit?
With auto-deploy on, every push to the branch you chose redeploys the bot: install, build if there is one, start. The live console shows each step and the crash doctor explains a failed start in plain words.
What does the free plan include?
One bot or app per verified account: 256 MB of memory, a quarter of a vCPU, 3 GB of disk under /data and 1 GB of traffic a day, with deploys on push, the live console, the crash doctor and rollback. The process stays on; only a web address sleeps when nobody is using it. A free app runs in whichever region has room for it. A paid plan from $2 a month gives the bot 1 GB and a whole vCPU, lets you pick the region and drops the traffic limit.