Publish Own Site, Syndicate Elsewhere for Ghost.
POSSE is a Docker-ready Python service that receives Ghost webhooks, syndicates posts to Mastodon and Bluesky, and can sync social interactions back to your blog.
Use the README for quick setup, then follow the focused guides:
- Syndication Guide - multi-account posting, tag routing, media handling, catch-up syndication
- Social Interaction Sync Guide - syncing likes/reposts/replies back to Ghost
- Webmention Reply Guide - self-hosted reply form and webmention publishing
- Webmention Sending Guide - tag-triggered webmention sending to configurable targets
- Security Hardening Guide - endpoint protection and deployment hardening
- Interaction Sync Architecture - internals and data flow
- Widget Documentation - embedding the social interactions widget
- Ghost webhook receiver with JSON schema validation
- Multi-account Mastodon and Bluesky syndication with per-account tag filters
- Optional image splitting for multi-image posts (with
#nosplitoverride) - Optional LLM-generated alt text for missing image descriptions
- Automatic Bluesky image compression to fit blob size limits
- Social interaction sync API for Ghost widgets
- Tag-triggered webmention sending to configurable targets (e.g. IndieWeb News)
- Optional self-hosted webmention reply form and W3C-compliant receiver (
/webmention) - Optional Pushover notifications for syndication events and new social interaction replies
Prerequisite: Docker installed locally.
- Clone and configure:
git clone https://github.com/wpowiertowski/posse.git
cd posse
cp config.example.yml config.yml
mkdir -p secrets- Add at least one platform credential:
echo "your_mastodon_token" > secrets/mastodon_access_token.txt
# or
echo "your_bluesky_app_password" > secrets/bluesky_app_password.txt-
Set platform accounts in
config.yml. -
Start POSSE:
docker compose up -d- Configure Ghost webhook(s):
POST http://your-posse-host:5000/webhook/ghostfor publish events- Optional catch-up:
POST http://your-posse-host:5000/webhook/ghost/post-updated
Start with a minimal config and add optional sections from the feature guides.
timezone: "UTC"
mastodon:
accounts:
- name: "personal"
instance_url: "https://mastodon.social"
access_token_file: "/run/secrets/mastodon_personal_access_token"
# tags: ["tech", "python"]
# max_post_length: 500
# split_multi_image_posts: false
bluesky:
accounts:
- name: "main"
instance_url: "https://bsky.social"
handle: "user.bsky.social"
app_password_file: "/run/secrets/bluesky_main_app_password"
# tags: ["tech", "python"]
# max_post_length: 300
# split_multi_image_posts: falsePOSSE expects credentials in files (typically Docker secrets), for example:
mkdir -p secrets
echo "token" > secrets/mastodon_personal_access_token
echo "app-password" > secrets/bluesky_main_app_passwordReference those files from config.yml or mount them at /run/secrets/... in Docker.
- Interaction sync and widget: docs/INTERACTION_SYNC_README.md
- Webmention reply form: docs/WEBMENTION_REPLY_GUIDE.md
- Webmention receiver: docs/WEBMENTION_RECEIVER_DESIGN.md
- Webmention sending: docs/WEBMENTION_SENDING_GUIDE.md
- Security controls and reverse proxy hardening: docs/SECURITY_HARDENING.md
POST /webhook/ghost: primary Ghost publish webhookPOST /webhook/ghost/post-updated: catch-up webhook for already-published postsGET /health: liveness endpointGET /api/interactions/<ghost_post_id>: interaction payload for a Ghost postPOST /api/interactions/<ghost_post_id>/sync: manual sync trigger (protected withX-Internal-Tokenwhen configured)GET /webmention: reply form page (whenwebmention_replyis enabled)POST /webmention: W3C webmention receiver — accepts incoming webmentions from external sites (whenwebmention_receiveris enabled)POST /api/webmention/reply: reply submission endpoint (whenwebmention_replyis enabled)GET /reply/<reply_id>: published h-entry source page for a replyGET /api/webmentions?target=<url>: query verified received webmentions for a target URL (whenwebmention_receiveris enabled)
Common commands:
make help
make build
make up
make down
make test
make test-verbose
make shellFor a complete production stack (Ghost + POSSE + supporting services), see:
MIT. See LICENSE.