# alexandru/social-publish: Self-hosted service that helps me share to multiple social media platforms at the same time.
Canonical: https://social-archive.org/hyungyunlim/ABZ4jknk3J
Original URL: https://github.com/alexandru/social-publish
Author: alexandru
Platform: web
## Content
## Social Publish https://github.com/alexandru/social-publish/actions/workflows/build.yaml https://github.com/alexandru/social-publish/actions/workflows/deploy.yml In implementing [POSSE](https://indieweb.org/POSSE) (publish on your own site, syndicate elsewhere), I need to publish to multiple social networks. Social Publish provides direct API integration for X (Twitter), Mastodon, Bluesky, and LinkedIn, plus an RSS feed for automation. ## What it does - Publish the same post to multiple networks from one form - Upload images with alt-text, or let an LLM generate alt-text automatically - Provide an RSS feed for external automation tools like IFTTT ## Supported networks - [X (Twitter)](https://twitter.com/) - [Mastodon](https://joinmastodon.org/) - [Bluesky](https://bsky.app/) - [LinkedIn](https://linkedin.com/) [![Social Publish post form screenshot](https://github.com/alexandru/social-publish/raw/main/docs/form-screenshot.png)](https://github.com/alexandru/social-publish/blob/main/docs/form-screenshot.png) **Table of Contents** - [What it does](#what-it-does) - [Supported networks](#supported-networks) - [Self-hosting](#self-hosting) [Bluesky credentials](#bluesky-credentials) - [Mastodon credentials](#mastodon-credentials) - [Twitter setup](#twitter-setup) - [LinkedIn setup](#linkedin-setup) - [LLM setup (Optional)](#llm-setup-optional) [CLI Commands](#cli-commands) [RSS feed](#rss-feed) [Developing](#developing) [License](#license) ## Self-hosting My `docker-compose` setup: ``` version: "3.8" services: # ... social-publish: container_name: social-publish image: ghcr.io/alexandru/social-publish:latest restart: always healthcheck: test: ["CMD-SHELL", "curl --head http://localhost:3000/ || exit 1"] ports: - "3000:3000" env_file: - ./envs/social-publish.env networks: - external_network ``` Where `./envs/social-publish.env` contains: ``` # Where the server is hosted — needed for correctly generating an RSS feed BASE_URL="https://your-hostname.com" # The server's Basic AUTH credentials SERVER_AUTH_USERNAME="your-username" SERVER_AUTH_PASSWORD="your-password" # Bluesky credentials BSKY_HOST="https://bsky.social" BSKY_USERNAME="your-username" BSKY_PASSWORD="your-password" # Mastodon credentials MASTODON_HOST="https://mastodon.social" MASTODON_ACCESS_TOKEN="your-access-token" # Twitter OAuth1 key and secret (Consumer Keys in the Developer Portal) TWITTER_OAUTH1_CONSUMER_KEY="Api Key" TWITTER_OAUTH1_CONSUMER_SECRET="Api Secret Key" # LinkedIn OAuth2 credentials LINKEDIN_CLIENT_ID="your-client-id" LINKEDIN_CLIENT_SECRET="your-client-secret" # LLM for alt-text generation (optional) # Configure the API endpoint, key, and model for your LLM provider # For OpenAI: LLM_API_URL="https://api.openai.com/v1/chat/completions" LLM_API_KEY="your-openai-api-key" LLM_MODEL="gpt-4o-mini" # For Mistral: # LLM_API_URL="https://api.mistral.ai/v1/chat/completions" # LLM_API_KEY="your-mistral-api-key" # LLM_MODEL="pixtral-12b-2409" # Used for authentication (https://jwt.io) JWT_SECRET="random string" ``` ### Bluesky credentials For Bluesky, you'll need an "app password". - Go here to create one: [https://bsky.app/settings/app-passwords](https://bsky.app/settings/app-passwords) - Copy the password - Set the `BSKY_PASSWORD` environment variable to it Keep it safe, as it grants access to everything. ### Mastodon credentials For Mastodon, you'll need an "access token". Here's how to get one: - Go to: [https://mastodon.social/settings/applications](https://mastodon.social/settings/applications) - Create a "New Application" - Select `write:statuses` and `write:media` for permissions, and unselect everything else - Click on the newly created application - Copy " *your access token* " - Set the `MASTODON_ACCESS_TOKEN` environment variable to it ### Twitter setup For Twitter, we're working with Oauth1. - Go to: [https://developer.twitter.com/en/portal/projects-and-apps](https://developer.twitter.com/en/portal/projects-and-apps) - Create a project and app - In the " *Keys and tokens* " section of the app, generate " *Consumer Keys* " and copy the generated " *App Key and Secret* " - In the app's settings, go to " *User authentication settings* " and add as the " *Callback URL* ": `https:///api/twitter/callback` (replace `` with your domain, obviously) - Set the `TWITTER_OAUTH1_CONSUMER_KEY` and the `TWITTER_OAUTH1_CONSUMER_SECRET` environment variables - Once the server is running, go to `https:///account` and click on " *Connect Twitter* " ### LinkedIn setup For LinkedIn, we're working with OAuth2. - Go to: [https://www.linkedin.com/developers/apps](https://www.linkedin.com/developers/apps) - Click " *Create app* " and fill in the required details - In the " *Auth* " tab, copy the " *Client ID* " and " *Client Secret* " - Add the following redirect URL: `https:///api/linkedin/callback` (replace `` with your actual domain) - In the " *Products* " tab, request access to: " *Sign In with LinkedIn using OpenID Connect* " (provides `openid` and `profile` scopes) - " *Share on LinkedIn* " (provides `w_member_social` scope) Set the `LINKEDIN_CLIENT_ID` and `LINKEDIN_CLIENT_SECRET` environment variables Once the server is running, go to `https:///account` and click on " *Connect LinkedIn* " **Note:** LinkedIn access tokens expire after 60 days. The system automatically refreshes tokens using the refresh token, which is valid for 1 year. You'll need to reconnect if the refresh token expires. ### LLM setup (Optional) The application can integrate with LLM providers to automatically generate alt-text descriptions for images. This feature is optional and supports any OpenAI-compatible API (including OpenAI, Mistral AI, and other providers). **Supported providers:** - **OpenAI** (e.g., GPT-4o-mini): [https://platform.openai.com/api-keys](https://platform.openai.com/api-keys) - **Mistral AI** (e.g., Pixtral): [https://console.mistral.ai/api-keys/](https://console.mistral.ai/api-keys/) - Any OpenAI-compatible API endpoint **Configuration:** 1. Get an API key from your chosen provider 2. Set the environment variables: - `LLM_API_URL`: The API endpoint URL (e.g., `https://api.openai.com/v1/chat/completions`) - `LLM_API_KEY`: Your API key - `LLM_MODEL`: Model name (e.g., `gpt-4o-mini` for OpenAI, `pixtral-12b-2409` for Mistral) ## CLI Commands Social Publish includes several CLI commands for managing users and configuration. These commands are particularly useful when running the application in a Docker container. ### Using the CLI in Docker When running in Docker, you can use the `./cli` wrapper script for cleaner output with minimal logging: ``` # Using docker exec with the cli wrapper (minimal logging) docker exec -it social-publish /opt/app/cli create-user --username myuser --password mypassword # Or use java-exec directly with verbose flag for detailed logging docker exec -it social-publish /opt/app/java-exec -jar /opt/app/app.jar create-user --username myuser --password mypassword --verbose ``` ### Available Commands **Create a new user:** ``` ./cli create-user --username --password # Or with environment variables: ./cli create-user --db-path $DB_PATH --username --password ``` **Change a user's password:** ``` ./cli change-password --username --new-password # Prompts for password if not provided: ./cli change-password --username ``` **Change a user's username:** ``` ./cli change-username --current-username --new-username # Prompts for usernames if not provided: ./cli change-username ``` **Generate a BCrypt password hash:** ``` ./cli gen-bcrypt-hash --password # Or let it prompt you (hides input): ./cli gen-bcrypt-hash ``` ### Verbose Logging By default, CLI commands use minimal logging (warnings and errors only). To see detailed logs, add the `--verbose` or `-v` flag: ``` ./cli create-user --username myuser --password mypass --verbose ``` ## RSS feed The RSS feed is exposed at `/rss` (e.g., `http://localhost:3000/rss`). Use it with automation tools like [ifttt.com](https://ifttt.com/) if you want additional workflows beyond the direct integrations. ## Developing This is a Kotlin multiplatform project with: - **Backend**: Ktor server with Arrow for functional programming - **Frontend**: Compose for Web (Kotlin/JS) - **Build**: Gradle with Kotlin DSL ### Development Commands To run the development environment with live reload: ``` make dev ``` This starts both the backend server (port 3000) and frontend dev server (port 3002) with hot reload enabled. To run backend and frontend separately: ``` # Backend only make dev-backend # Frontend only make dev-frontend ``` You can navigate to [http://localhost:3002](http://localhost:3002/) for the frontend, while the backend is available at [http://localhost:3000](http://localhost:3000/). ### Building To build the project: ``` make build ``` To run tests: ``` make test ``` To check and fix code formatting: ``` make lint # Check formatting make format # Auto-format code ``` ### Docker Images To build and test the Docker images locally: ``` # Build and run JVM image make docker-run-jvm ``` To run tests in a Docker environment that matches production: ``` # Run all tests in Docker make test-docker # Run specific ImageMagick tests in Docker make test-imagemagick-docker ``` See the [Makefile](https://github.com/alexandru/social-publish/blob/main/Makefile) for all available commands. ## License This project is licensed under the GNU Affero General Public License v3 (AGPL-3.0). See [LICENSE.txt](https://github.com/alexandru/social-publish/blob/main/LICENSE.txt) for details.
