No description
  • TypeScript 99.1%
  • Shell 0.6%
  • JavaScript 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Ricardo 6c182cf54b bump version to 2.11.2
Picks up the @atproto/api 0.20.38 -> 0.20.41 patch bump (merged via
Renovate PR #25 on 2026-08-19, never released since - v2.11.1 was
tagged before it landed). The ESLint 10 fix (b8ccaaf) touches only
devDependencies, which the Dockerfile excludes via
`yarn workspaces focus --production`, so it has no image impact and
needs no release.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R13RT7KqwCTh7TSShxZZ8r
2026-08-24 14:39:35 +02:00
.do Add cloud deployment configurations and health monitoring 2026-08-01 14:52:05 +00:00
.github chore(deps): Update docker/setup-buildx-action digest to 37fe631 (#26) 2026-08-19 18:24:15 +02:00
.vscode feat: add developer experience improvements (Quick Wins) 2026-08-05 10:59:28 +00:00
.yarn/releases chore(deps): Update Yarn to v4.18.0 (#15) 2026-08-10 19:17:29 +02:00
app fix: bound Bluesky API network calls with a 30s timeout 2026-08-12 17:38:41 +02:00
certs fix(fleet): supply the missing intermediate cert for rappel.conso.gouv.fr 2026-07-30 11:34:55 +02:00
config.example Improve feedSource API ergonomics and fleet startup configuration 2026-08-11 22:26:55 +00:00
data Fix adaptive spacing for two posts 2026-01-11 17:49:02 +01:00
documentation Improve feedSource API ergonomics and fleet startup configuration 2026-08-11 22:26:55 +00:00
fleet fix: bound Bluesky API network calls with a 30s timeout 2026-08-12 17:38:41 +02:00
scripts Add cloud deployment configurations and health monitoring 2026-08-01 14:52:05 +00:00
shared fix: bound Bluesky API network calls with a 30s timeout 2026-08-12 17:38:41 +02:00
test-fixtures docs+test(feedSource): clarify itunes:author is item-level only, cover 6 untested mappedValues fields 2026-08-08 03:34:39 +02:00
.dockerignore Add cloud deployment configurations and health monitoring 2026-08-01 14:52:05 +00:00
.editorconfig feat: add developer experience improvements (Quick Wins) 2026-08-05 10:59:28 +00:00
.gitignore fix(fleet): address Phase 3 whole-branch review findings 2026-07-29 13:05:45 +02:00
.prettierrc.cjs feat: flip bsky.rss to native ESM 2026-08-11 08:17:17 +02:00
.yarnrc.yml chore(deps): Update Yarn to v4.18.0 (#15) 2026-08-10 19:17:29 +02:00
CHANGELOG.md bump version to 2.11.2 2026-08-24 14:39:35 +02:00
CONTRIBUTING.md chore: close post-migration debt from feedsmith audit 2026-08-06 21:54:00 +02:00
docker-compose.example.yml fix: address final-review findings on the fleet-template branch 2026-07-30 13:29:24 +02:00
docker-compose.fleet.example.yml docs+deploy: switch fleet template to pull the published GHCR image 2026-08-06 09:59:01 +02:00
Dockerfile fix: address final-review findings on the fleet-template branch 2026-07-30 13:29:24 +02:00
eslint.config.js feat: flip bsky.rss to native ESM 2026-08-11 08:17:17 +02:00
fly.toml Add cloud deployment configurations and health monitoring 2026-08-01 14:52:05 +00:00
LICENSE update copyright name 2024-02-11 17:13:12 -06:00
package.json bump version to 2.11.2 2026-08-24 14:39:35 +02:00
railway.toml Add cloud deployment configurations and health monitoring 2026-08-01 14:52:05 +00:00
README.md Revise README content and formatting 2026-08-12 09:39:24 +02:00
release.sh fix: address final-review findings on the fleet-template branch 2026-07-30 13:29:24 +02:00
render.yaml Add cloud deployment configurations and health monitoring 2026-08-01 14:52:05 +00:00
renovate.json chore(deps): enable forkProcessing so Renovate actually runs on this fork 2026-08-10 18:37:54 +02:00
SECURITY.md Update SECURITY.md 2026-08-12 09:34:56 +02:00
tsconfig.json feat: flip bsky.rss to native ESM 2026-08-11 08:17:17 +02:00
yarn.lock fix(deps): pin @typescript-eslint/* to ^8.67.0 for ESLint 10 support 2026-08-23 16:52:53 +02:00

bsky.rss

A configurable RSS poster for Bluesky

Important

New User?Quick Start Guide - Get posting in 10 minutes 🆘 Having Issues?Troubleshooting Guide - Common problems & solutions


This app runs in two modes:

  • Single-bot mode (this README) - one Bluesky account per container/process. Start here if you're running one bot.
  • Fleet mode - many independent bots running out of a single process, instead of one container per bot. See the Fleet mode section below.

Deployment

Choose your preferred deployment method:

Cloud Platforms (Managed)

Deploy with one command and let the platform handle infrastructure:

📖 Full Deployment Guide · 🔄 Platform Comparison

Self-Hosted

Run on your own infrastructure:

Copy this docker-compose.yml file to your Docker host:

version: "3"
services:
  bsky-rss:
    restart: always
    image: ghcr.io/rmdes/bsky.rss
    environment:
      - APP_PASSWORD=
      - INSTANCE_URL=https://bsky.social
      - FETCH_URL=
      - IDENTIFIER=
    volumes:
      - /your/data/directory:/build/data

Mount /build/data to a directory on your host for persistent storage.

See Deployment Guide - Docker for details.

Manual / Development

For local development or custom setups:

git clone github.com/rmdes/bsky.rss
cd bsky.rss
yarn install
cp .env.example .env  # Edit with your credentials
yarn start

See Environment Variables below for configuration.


Not sure which to choose? See the Platform Comparison Guide for recommendations based on your needs.

Environment Variables & Configuration File

Environment Variables

Here's an outline of the environment variables:

  • APP_PASSWORD: The password of the account that will be posting the RSS feed (you can use an account password, but it's recommended to use an app password)
  • INSTANCE_URL: The URL of your Bluesky instance (if using the official one, use "https://bsky.social")
  • FETCH_URL: The URL of the RSS feed you want to fetch & post from (e.g. "http://rss.cnn.com/rss/cnn_latest.rss")
  • IDENTIFIER: The identifier for the account to post from (this can be the email address or the username of the account)

Configuration File

  • If you set up the RSS poster using Docker, create the config.json file in your data directory that you mounted in the compose file.
  • If you set up the RSS poster manually, rename the config.example.json file in the data directory to config.json.

Here's an example of the config.json file:

{
  "string": "$title - $link",
  "publishEmbed": true,
  "embedType": "card",
  "languages": ["en"],
  "ogUserAgent": "",
  "truncate": true,
  "runInterval": 60,
  "dateField": "",
  "publishDate": false,
  "imageField": "",
  "imageAlt": "$title",
  "forceDescriptionEmbed": false,
  "removeDuplicate": false,
  "descriptionClearHTML": false,
  "titleClearHTML": false,
  "adaptiveSpacing": false,
  "spacingWindow": 600,
  "minSpacing": 1,
  "maxSpacing": 60
}
  • string: The string to post to Bluesky. You can use the following variables in the string:
    • $title: The title of the RSS post
    • $link: The link to the RSS post
    • $description: The description of the RSS post
    • $georss: An OpenStreetMap link built from the item's geographic coordinates, if the feed provides any - <georss:point> (GeoRSS Simple) or geo:lat/geo:long (W3C Basic Geo) as a fallback. Renders as an empty string when the item has neither.
    • [text](url): Markdown-style link syntax - text and url can both contain $placeholders (e.g. [$title]($link)). See CONFIGURATION.md for fallback behavior and more examples.
  • publishEmbed: Whether to publish the post as an embed or not. If set to true, the post will be published as an embed with the title, description (if available), and link to the RSS post.
  • embedType: Type of embed. If set to card, the post will be published with an Open Graph/link card. If set to image an image will be uploaded.
  • languages: The languages to set the posts to. This can be an array of ISO 639-1 language codes. If not set, it will default to en.
  • ogUserAgent: The user agent to use when fetching the Open Graph data of the URL provided by the RSS post. By default, this is set to bsky.rss/1.0 (Open Graph Scraper).
  • truncate: Whether or not to truncate the body of the post if it is over 300 characters. By default, this is set to true.
  • runInterval: The interval (in seconds) to run the RSS poster. By default (and recommended), this is set to 60 seconds.
  • dateField: The field to use for the date of the RSS post. This can be any field that is available in the RSS feed. If not set, it will default to pubDate and/or date.
  • publishDate: Whether or not to use the RSS item's date for the Bluesky post's createdAt. When false, the current date is used. When true, the item's date is used. By default, this is set to false.
  • imageField: The field to use for fetching the image of the RSS post. This can be any field that is available in the RSS feed. If not set, the poster will fetch the Open Graph data of the URL provided by the RSS post and use the image from there.
  • mappedValues: Maps specific dc:*/itunes:* feed fields into new $key template placeholders. See CONFIGURATION.md for the full recognized-value list and examples.
  • imageAlt: Alt text for the uploaded image if the embedType is set to image. Can be any variable (+ string) used in the string configuration (e.g. $title).
  • forceDescriptionEmbed: Force the description of the embed to be the description of the RSS post (as opposed to using Open Graph data).
  • removeDuplicate: Instead of using the last date to track which items need to be published, use a text-based database to track duplicate items.
  • descriptionClearHTML: Remove HTML from the description of the Open Graph description and RSS-provided description (to make it more readable).
  • titleClearHTML: Remove HTML from the title of the post (to make it more readable).
  • adaptiveSpacing: Enable adaptive spacing between posts based on the queue size.
  • spacingWindow: Time window (in seconds) used when calculating adaptive spacing.
  • minSpacing: Minimum number of seconds between posts when adaptive spacing is enabled.
  • maxSpacing: Maximum number of seconds between posts when adaptive spacing is enabled.

A docker-compose.yml file can be found in the root directory as docker-compose.example.yml, which you can use to set up the RSS poster using Docker.

Contributing & Testing

This project has comprehensive test coverage with 129+ tests across both single-bot and fleet modes (90%+ coverage).

Run tests:

# All tests
yarn test

# Single-bot mode only
yarn test:app

# Fleet mode only
yarn test:fleet

# Coverage report
yarn test:coverage

Contributing:

All pull requests require tests for new features and bug fixes. See the guides above for detailed information.

Fleet mode

If you want to run many bots (different accounts, different feeds) at once, fleet mode runs them all as independent workers inside a single process instead of one container per bot - the same image, just a different entry point (yarn fleet / fleet/runFleet.ts instead of yarn start / app/index.ts) and a different command: in your compose file.

Fleet mode gives each bot its own config, its own SQLite-backed dedup/session state, and a staggered login schedule, while sharing fleet-wide rate limits (Open Graph scraping, image processing) across all bots so N bots polling concurrently doesn't spawn unbounded work.

Two ways to get started:

  • Deploying, not building - use the bsky-rss-fleet-template repo. It's a docker-compose.yml and example config tree that pulls the prebuilt image from ghcr.io/rmdes/bsky.rss, no source checkout needed.
  • Building from this repo, migrating an existing single-bot setup, or rolling back - see documentation/fleet.md for the architecture, the legacy importer/exporter, and the full cutover sequence.

License

This project is licensed under the MIT License. See the LICENSE file for more information.