← All tools Syndicate elsewhere

Syndicate elsewhere

A simple, local-first publishing dashboard for syndicating blog posts and podcast episodes from RSS feeds to social platforms like Bluesky, Mastodon, Slack, LinkedIn, and Threads.

Overview

Syndicate Elsewhere is a lightweight, single-page HTML/JavaScript app that acts as a manual syndication inbox. It reads one or more RSS feeds, identifies content that has not yet been syndicated, and presents a queue of publishable items. You can review, edit, and publish a generated social post to one or more destinations with a single click.

The application is intentionally local-first, single-user, and manual rather than automated. RSS remains the source of truth — the app only stores references to feed entries plus your publication state. Everything lives in browser localStorage; there is no backend.

Note on Slack: Slack's webhook endpoint does not send CORS headers, so the browser cannot read its response. The app sends the post and treats a successful network request as success — it cannot detect a Slack-side rejection (e.g. a revoked webhook). Use Test Connection to confirm messages arrive.

Note on LinkedIn & Threads: Their APIs cannot be called from a backendless browser app (they block cross-origin requests and require an OAuth token that can't be stored safely client-side). These use assisted posting instead: publishing opens the platform's web composer with your draft prefilled where possible, and the item is marked published once the composer opens — you complete the post there. For Threads the text is prefilled via its web intent; for LinkedIn the share dialog opens for the article and your post text is copied to the clipboard (LinkedIn does not allow prefilling commentary). Allow pop-ups for this site so the composer can open.

Features

Usage

  1. Open index.html in a browser (or run npm start from the repo root).
  2. Go to Settings and enable the destinations you want. Bluesky, Mastodon, and Slack take credentials (use Test Connection to verify); for Slack, create an Incoming Webhook for the target channel and paste its URL. LinkedIn and Threads need no credentials — just enable them (they use assisted posting; see the note above).
  3. Go to Feeds, add an RSS feed (Name, URL, Type), optionally Validate Feed, then Save Feed.
  4. Click ↻ Refresh Feeds in the header.
  5. On the Queue, click Compose & Publish on a card, edit the draft, choose destinations, and Publish Selected.
  6. Return later and refresh — only content you haven't fully published (and haven't skipped) appears.

CORS proxy

Most RSS feeds do not send CORS headers, so browsers block fetching them directly. Syndicate Elsewhere tries a direct fetch first and falls back to a configurable CORS proxy (set in Settings → General). The Bluesky and Mastodon APIs themselves are called directly and support CORS.

The default proxy is /rss-proxy?url=, served by the bundled Netlify edge function at netlify/edge-functions/rss-proxy.ts (the deployed equivalent of a Cloudflare Worker). It works automatically on the deployed Netlify site, or locally when you run netlify dev. The function only proxies http(s) URLs and refuses localhost / private-network targets to avoid being an abusable open proxy.

For other local setups (e.g. opening the file directly or npm start), either run netlify dev, or change the proxy to a public one such as https://api.allorigins.win/raw?url=, or clear it to disable the fallback.

If a feed's site source is on GitHub, the app can record where each of its items was shared. After Publish Selected succeeds on Bluesky and/or Mastodon, it adds the new post URLs to the item's source file front matter in one commit:

syndication:
  - https://bsky.app/profile/you.example/post/3l…
  - https://mastodon.example/@you/1131…

Your site can use these to invite responses on each platform and to match backfed webmentions (e.g. from Bridgy).

It's set up per feed, so each feed records to its own repository: open the feed on the Feeds tab and expand Record syndication links on GitHub:

Feeds without a repository (e.g. a podcast) don't record anything.

The front matter is edited as text, so nothing else in it changes, and links already listed aren't added again. Bluesky links use your handle rather than your DID. If the file changed in between reading and writing, the commit is retried once. If recording fails (say, an expired token), the item stays in the queue marked Links not recorded with a Record links button to retry.

Each commit lands on the branch directly, so pull before pushing your own changes to the site.

Templates

Each content type has an editable template. Available variables:

{title} · {url} · {summary} · {shortDescription} · {description} · {author} · {feedName} · {publishedDate}

Defaults:

Blog:    New post: {title}\n\n{url}
Podcast: New episode: {title}\n\n{shortDescription}\n\nListen: {url}

Data & Security

All state — feeds, templates, credentials, and publication history — is stored under the syndicateElsewhere key in browser localStorage. Credentials (Bluesky app password, Mastodon token, Slack webhook URL, per-feed GitHub tokens) are stored in plain text locally, which is acceptable for a single-user, local-first tool with no shared backend. LinkedIn and Threads store no credentials at all (assisted posting). Use app passwords / scoped tokens, treat the Slack webhook URL as a secret, and use Reset All Data to wipe everything. GitHub tokens are left out of Export backups (importing a backup keeps the tokens already in the browser), since they can write to a repository. Bear in mind that every tool served from the same site origin can read this localStorage.

Technical Details

Browser Compatibility

Works in modern Chrome, Firefox, Safari, and Edge.

Roadmap

Planned future enhancements include image/OpenGraph support, platform-specific drafts and preview cards, scheduling, batch publishing, AI-assisted drafts, and additional destinations (Discord, Ghost, Micro.blog, etc.). Slack support could be extended with Block Kit formatting and per-post channel overrides (it currently posts the body plus a content-card attachment); LinkedIn and Threads could gain true one-click API posting if a small backend/OAuth relay is added (they currently use assisted posting).

License

See repository root for license information.