arizuko › components › bskyd
bskyd is the Bluesky channel adapter. It polls Bluesky notifications via the AT Protocol xrpc API for mentions and replies, posts inbound to gated, 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>.
Bluesky is the AT Protocol social surface. There is no streaming API for arbitrary accounts — the firehose is consumer-wide and too noisy — so bskyd polls listNotifications and the auth-session refresh flow. Writing posts means signing record creates against the PDS, which is what bskyd handles for the agent.
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
gated
| POST /v1/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.
bskyd is a plain Go binary. It needs a reachable gated, a Bluesky handle, and an app password (not your main password).
export ROUTER_URL=http://gated: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.
bskyd/README.md — verb table, file map, DM gap.