A self-hosted web interface for composing and publishing posts to multiple platforms simultaneously.
  • Python 64.9%
  • HTML 35.1%
Find a file
Erick Ruiz de Chavez e3943d8ed0 README: make the no-auth warning more prominent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 10:14:59 +00:00
app Fix two low-severity issues found in code review 2026-06-25 10:00:02 +00:00
.env.example Initial implementation of multi-platform posting web app 2026-06-24 23:20:02 +00:00
.gitignore Initial implementation of multi-platform posting web app 2026-06-24 23:20:02 +00:00
CLAUDE.md Add README.md and CLAUDE.md 2026-06-25 10:12:28 +00:00
compose.yaml Initial implementation of multi-platform posting web app 2026-06-24 23:20:02 +00:00
pyproject.toml Initial implementation of multi-platform posting web app 2026-06-24 23:20:02 +00:00
README.md README: make the no-auth warning more prominent 2026-06-25 10:14:59 +00:00
uv.lock Initial implementation of multi-platform posting web app 2026-06-24 23:20:02 +00:00

poster

A self-hosted web interface for composing and publishing posts to multiple platforms simultaneously. Replaces auto-crossposters with intentional, per-post control over where content goes.

Supported platforms

Platform Notes
GoToSocial Mastodon-compatible API; supports visibility, content warnings, polls, media
Bluesky AT Protocol; long posts split into threads automatically (300 grapheme limit)
Station Gemini protocol; media embedded as Gemini link lines using GTS-hosted URLs

Platforms are enabled by presence of their credentials in .env. Any combination works.

Features

  • Compose and post to one or more platforms in a single action
  • Per-platform visibility and content warning options
  • Image/video attachments with alt text (up to 10 on Bluesky)
  • Poll builder (GoToSocial only; other platforms skipped automatically when a poll is set)
  • Save drafts, schedule posts, edit and delete sent posts
  • Post history with per-platform status
  • REST API for programmatic use (/api/v1/)
  • Mobile-first responsive UI (works on iPhone, iPad, desktop)
  • Dark/light theme toggle

Stack

  • Python 3.14, FastAPI, Jinja2, HTMX 2, Pico CSS v2
  • SQLite (WAL mode) for drafts, history, and results
  • uv for dependency management
  • No build step — pure server-side rendering with HTMX partials

Setup

1. Configure credentials

Copy .env.example to .env and fill in the platforms you want to use:

# GoToSocial — token needs read + write scope
GTS_BASE_URL=https://your.gotosocial.instance
GTS_ACCESS_TOKEN=your_token

# Bluesky
BSKY_HANDLE=you.bsky.social
BSKY_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx

# Station (Gemini) — paths inside the container
STATION_CERT_PATH=/app/data/station.pem
STATION_KEY_PATH=/app/data/station.key

# Optional: public base URL used as fallback for media links when GTS is not selected
BASE_URL=https://poster.example.com

# Storage and logging
DATA_DIR=/app/data
LOG_LEVEL=INFO

2. Run with Docker Compose

docker compose up -d

The app listens on port 8000.

⚠️ No authentication is built in. Anyone who can reach the app can post, edit, and delete content on your behalf. Always place it behind a reverse proxy (nginx, Caddy, etc.) that enforces access control — e.g. HTTP basic auth, IP allowlist, or a VPN — and never expose it directly to the internet.

3. Run locally (development)

uv run uvicorn app.main:app --reload

REST API

The API lives at /api/v1/. Interactive docs at /api/docs.

POST   /api/v1/posts              # Post immediately
POST   /api/v1/drafts             # Save as draft
GET    /api/v1/drafts             # List drafts + scheduled
PUT    /api/v1/drafts/{id}        # Update draft
DELETE /api/v1/drafts/{id}        # Delete draft
POST   /api/v1/drafts/{id}/send   # Send a draft now
GET    /api/v1/posts              # Post history
GET    /api/v1/posts/{id}         # Single post + results
PUT    /api/v1/posts/{id}         # Edit a sent post
DELETE /api/v1/posts/{id}         # Delete from all platforms
POST   /api/v1/media              # Upload a media file
GET    /api/v1/platforms          # List configured platforms

Data

All persistent data lives in data/ (gitignored):

  • data/poster.db — SQLite database
  • data/uploads/ — uploaded media files
  • data/station.pem / data/station.key — Station TLS client cert (if using Station)