arizuko

arizukocomponents › bskyd

bskyd

What it is

In plain terms, bskyd is the connector that plugs Bluesky into arizuko, so the agent can read mentions and reply as posts.

bskyd is the Bluesky channel adapter. It polls Bluesky notifications via the AT Protocol xrpc API for mentions and replies, posts inbound to routd, and writes outbound as app.bsky.feed.* records on the user’s PDS (personal data server). JID prefix is bluesky:; per-user JIDs are bluesky:<did>.

Why it exists

Bluesky is the AT Protocol social network. There is no streaming API for one account — the firehose is network-wide and too noisy — so bskyd polls listNotifications and refreshes its auth session on a loop. Posting means signing record creates against the PDS, which is what bskyd does for the agent.

How it fits

Bluesky PDS (bsky.social or custom)
        |  xrpc poll: app.bsky.notification.listNotifications
        v
      bskyd     (LISTEN_ADDR=:8080)
        |  POST /v1/messages    (signed by CHANNEL_SECRET)
        v
      routd
        |  POST /send           (callback to bskyd)
        v
      bskyd     (com.atproto.repo.createRecord on app.bsky.feed.*)
        |
        v
      Bluesky PDS

Wired verbs (bskyd/client.go, bskyd/server.go): send / reply (app.bsky.feed.post; reply_to sets the reply ref), send_file (uploadBlob + app.bsky.embed.images, single image), post (with first media_paths[0] embedded), like (app.bsky.feed.like record), delete (com.atproto.repo.deleteRecord), quote (app.bsky.embed.record), repost (app.bsky.feed.repost record). dislike, forward, edit are 501-with-hint.

The edit hint is by design: putRecord succeeds at the PDS, but the Bluesky appview intentionally ignores updates to app.bsky.feed.post records — the edit never appears in the feed. Use delete + post instead.

Inbound is public-feed only. DMs would need chat.bsky.convo proxied through did:web:api.bsky.chat, and no inbound DM polling is wired today.

Standalone usage

bskyd is a plain Go binary. It needs a reachable routd, a Bluesky handle, and an app password (not your main password).

export ROUTER_URL=http://routd:8080
export CHANNEL_SECRET=$(grep ^CHANNEL_SECRET .env | cut -d= -f2)
export LISTEN_ADDR=:8080
export BLUESKY_IDENTIFIER=you.bsky.social
export BLUESKY_PASSWORD=abcd-efgh-ijkl-mnop    # app password
export BLUESKY_SERVICE=https://bsky.social     # or your PDS
export DATA_DIR=/srv/data/arizuko_demo
./bskyd

Bluesky-side setup: Settings → Privacy and security → App passwords. App passwords skip 2FA and can be revoked individually. GET /health returns 503 when the auth session is invalid; bskyd refreshes automatically and recovers on the next poll.

Go deeper