tweets-2-bsky#
Cross-post from Twitter/X to Bluesky with thread support, media handling, account mapping, and a web dashboard.
This repo is also mirrored on Tangled: j4ck.xyz/tweets2bsky
How It Works (Simple)#
- You connect one or more Twitter/X source accounts to a Bluesky account.
- The app reads tweets from X using
@the-convocation/twitter-scraperwith your cookies (auth_token+ct0). - It posts to Bluesky using the official AT Protocol client (
@atproto/api). - It tracks what was already posted in SQLite so it does not repost duplicates.
- A scheduler runs automatically, and you can also trigger
Run nowfrom the dashboard or CLI.
Installation (Pick One Path)#
Use either:
- Docker (recommended)
- Source install (PM2 or manual runtime)
Do not do both on the same machine unless you intentionally want two separate deployments.
Option A: Docker (Recommended)#
Prerequisite: Docker Desktop (macOS/Windows) or Docker Engine (Linux).
Start with the included compose file:
docker compose up -d
Open http://localhost:3000.
If you prefer docker run:
docker run -d \
--name tweets-2-bsky \
-p 3000:3000 \
-v tweets2bsky_data:/app/data \
--restart unless-stopped \
j4ckxyz/tweets-2-bsky:latest
Important: keep a persistent volume (-v tweets2bsky_data:/app/data) so mappings/history survive container recreation.
Useful Docker commands:
docker logs -f tweets-2-bsky
docker exec -it tweets-2-bsky bun dist/cli.js status
docker stop tweets-2-bsky
docker start tweets-2-bsky
Update Docker deployment:
docker pull j4ckxyz/tweets-2-bsky:latest
docker stop tweets-2-bsky
docker rm tweets-2-bsky
docker run -d \
--name tweets-2-bsky \
-p 3000:3000 \
-v tweets2bsky_data:/app/data \
--restart unless-stopped \
j4ckxyz/tweets-2-bsky:latest
Alternative image: ghcr.io/j4ckxyz/tweets-2-bsky:latest.
Option B: Source Install (PM2 or Manual)#
Prerequisites:
git- Bun 1.x+ (the installer auto-installs/upgrades Bun when needed)
- PM2 (optional, but recommended for background runtime)
Clone and install:
git clone https://github.com/j4ckxyz/tweets-2-bsky
cd tweets-2-bsky
chmod +x install.sh
./install.sh
install.sh does install/build/start and uses:
- PM2 when PM2 is available
nohupwhen PM2 is not installed
Useful installer commands:
./install.sh --status
./install.sh --stop
./install.sh --start-only
./install.sh --no-start
./install.sh --port 3100
PM2 Manual Runtime (if you want direct PM2 control)#
bun install
bun run build
pm2 start "$HOME/.bun/bin/bun" --name tweets-2-bsky --cwd "$PWD" -- dist/index.js
pm2 logs tweets-2-bsky
pm2 save
Manual Foreground Runtime (no PM2)#
bun install
bun run build
bun run start
Manual Nohup Runtime (no PM2)#
mkdir -p data/runtime
nohup bun run start > data/runtime/tweets-2-bsky.log 2>&1 &
echo $! > data/runtime/tweets-2-bsky.pid
Stop nohup process:
kill "$(cat data/runtime/tweets-2-bsky.pid)"
First-Time Setup (After Install)#
- Open
http://localhost:3000. - Register the first user (this account becomes admin).
- In Settings, add Twitter cookies (
auth_token,ct0; backup pair optional). - Add a mapping (Twitter source usernames -> Bluesky account).
- Click
Run now.
Twitter/X Integration Notes#
- This project does not use Twitter's paid official API.
- It uses
@the-convocation/twitter-scraperand authenticated browser cookies to read account/tweet data. - Required cookies:
auth_tokenandct0. - If cookies expire, update them in Settings.
- Keep cookies private; they are sensitive credentials.
For some quote-tweet screenshot fallbacks, Chromium is used (bundled in Docker, optional dependency for source installs).
CLI Quick Commands#
Always run CLI commands as:
bun run cli -- <command>
Common commands:
bun run cli -- status
bun run cli -- list
bun run cli -- run-now
bun run cli -- run-now --dry-run
bun run cli -- add-mapping
bun run cli -- backfill <mapping-id-or-handle> --limit 50
Updating#
Source installs:
./update.sh
Useful flags:
./update.sh --no-restart
./update.sh --skip-install --skip-build
Data and Security#
Important files:
config.json(mappings, credentials, users)data/database.sqlite(processed history)data/.jwt-secret(generated signing key whenJWT_SECRETis unset).env(runtime env values)
Security basics:
- First registered user becomes admin.
- Prefer Bluesky app passwords instead of your full Bluesky password.
- Set an explicit
JWT_SECRETin.envfor predictable secret management. - Keep
config.json, cookie values, and.envprivate.
Development#
bun run dev
bun run dev:web
bun run build
bun run typecheck
bun run lint
Troubleshooting#
See TROUBLESHOOTING.md.
Common native module recovery:
bun run rebuild:native
bun run build
bun run start
License#
MIT