Skip to content

Bluesky post action

ModernLeft/bluesky-post-action is a reusable Forgejo Action that publishes a plain-text post to a Bluesky account through the AT Protocol. It authenticates with an app password and writes an app.bsky.feed.post record. It runs as a Docker action, so the host needs nothing beyond Docker.

Inputs

Input Required Default Description
identifier yes n/a Bluesky handle (such as you.bsky.social) or account email
password yes n/a App password, not the main account password
text yes n/a Post text, up to 300 graphemes
pds-host no https://bsky.social PDS base URL. Set this to post through a self-hosted PDS
footer no false When true, append a clickable footer linking back to the action

Usage

- name: Announce release
  uses: ModernLeft/bluesky-post-action@v1
  with:
      identifier: ${{ secrets.BLUESKY_HANDLE }}
      password: ${{ secrets.BLUESKY_APP_PASSWORD }}
      text: "New release ${{ github.ref_name }} is out!"

Store the handle and app password as repository secrets. Use an app password rather than the main account password, since app passwords are scoped and revocable.

Posting through the self-hosted PDS

To post as an account on the ModernLeft PDS, point pds-host at https://modernleft.network and use that account's handle and app password:

- name: Post as the org account
  uses: ModernLeft/bluesky-post-action@v1
  with:
      identifier: modernleft.network
      password: ${{ secrets.ORG_APP_PASSWORD }}
      pds-host: https://modernleft.network
      text: "Hello from the org account"

App passwords for PDS accounts are minted through the XRPC API. See Bluesky PDS usage.

With footer: "true" the post gains a second paragraph:

<your text>

posted via bluesky-post-action

The phrase posted via bluesky-post-action is a clickable link back to the action repository, attached as an AT Protocol rich-text link facet. Everything else the action posts stays plain text.

Scope

The action posts plain text plus the optional footer link. Embed cards, images, threads, and rich-text facets inside your own text are out of scope.

The step fails if authentication fails or the PDS rejects the post, for example when the text runs past the 300-grapheme limit.