POSSE to Mastodon: State, Media Uploads, and Safer Deploys
The Bluesky flow already syndicated posts after each deploy. Mastodon needed the same behavior: no manual copy/paste, no reposting duplicates, and enough robustness for backfills.
The result is a dedicated script, shared state, and deploy wiring that mirrors the existing Bluesky setup.
A dedicated Mastodon syndication script
scripts/mastodon-syndicate.mjs reads two feeds:
const SOURCES = [
{ rssUrl: "https://adrian-altner.de/rss/articles.xml", label: "article" },
{ rssUrl: "https://adrian-altner.de/rss/notes.xml", label: "note" },
];
It parses each RSS item and posts only entries that are not yet present in state.
Idempotency and syndication links from state
State is persisted in .mastodon-posted.json using the canonical content URL as key and the Mastodon status URL as value:
{
"https://adrian-altner.de/articles/2026/03/24/.../": "https://mastodon.social/@altner/1162..."
}
This solves two things at once:
- Dedupe: already-posted URLs are skipped.
- Display: article and note pages can render
Shared on Mastodonautomatically from state.
Post text and media
Post bodies are generated as:
Title
Teaser
Canonical URL
The script fetches og:image from each content page, compresses it with sharp, uploads it to Mastodon media API, and attaches media_ids[] when creating the status.
If media upload fails, posting still continues without media instead of failing the whole run.
Deploy integration
The deploy script now reads Mastodon env vars and runs syndication after deploy:
MASTODON_BASE_URL=...
MASTODON_ACCESS_TOKEN=...
MASTODON_VISIBILITY=public
When .mastodon-posted.json changes, it is committed and pushed automatically, just like .bluesky-posted.json.
Without that commit step, state would reset and old items could be reposted on future runs.
Hardening for real-world runs
Two operational issues showed up quickly:
- large backfills can hit media upload rate limits (
429) - first-run validation often needs a dry simulation or bounded batch size
The script now supports:
- retry/backoff for media upload (including
Retry-After) MASTODON_DRY_RUNfor simulationMASTODON_LIMITfor controlled batch posting
These flags make first-run and recovery runs safer without changing the normal steady-state path.
What this changed in practice
After deployment, new articles and notes are syndicated to Mastodon automatically, with backlinks persisted to local state and rendered on each post page.
The site remains the canonical source (POSSE), while Mastodon becomes a distribution channel that can be rebuilt from feeds and state when needed.