A single-user bridge service that automatically cross-posts from your Bluesky account to your Mastodon account. Built specifically for Val.town with TypeScript and Deno.
- Connects your accounts: Securely links your Bluesky and Mastodon accounts using OAuth
- Automatic syncing: Checks for new Bluesky posts every 15 minutes and cross-posts them to Mastodon
- Smart transformations: Converts Bluesky mentions (@handle.bsky.social) to profile links since they don't exist on Mastodon
- Media support: Uploads images and videos from Bluesky to your Mastodon instance
- Duplicate prevention: Tracks synced posts to avoid posting the same content twice
- Error handling: Retries failed posts and logs errors for troubleshooting
✅ OAuth authentication for both Bluesky and Mastodon
✅ Media cross-posting (images & videos)
✅ Mention transformation (handles → profile links)
✅ Duplicate prevention via content hashing
✅ Retry mechanism with exponential backoff
✅ Error logging and sync tracking
✅ Setup wizard for easy configuration
Note: This bridge now features simplified setup! You only need to set one environment variable (
VALTOWN_URL
) and the OAuth configuration is handled automatically.Upgrading? You can delete
client-metadata.json
andclient-metadata.json.template
files - they're no longer needed!
Copy all files from this repository into your Val.town account. The project structure should look like:
├── backend/ │ ├── index.ts # Main HTTP handler │ ├── database/ │ ├── routes/ │ └── services/ ├── cronjob.ts # Cron job for syncing ├── frontend/ │ ├── index.html # Landing page │ ├── setup.html # Setup wizard UI │ ├── dashboard.html # Dashboard UI │ └── *.tsx # React components └── shared/ └── types.ts # Shared interfaces
HTTP Trigger (for the web interface and API):
- Set
backend/index.ts
as an HTTP val - This serves the setup wizard and handles OAuth callbacks
Cron Trigger (for automatic syncing):
- Set
cronjob.ts
as a Cron val - Schedule:
*/15 * * * *
(every 15 minutes) - For paid accounts, you can use shorter intervals like
*/5 * * * *
(every 5 minutes)
Set this single environment variable in your Val.town account:
VALTOWN_URL=https://your-val-url.web.val.run
Replace your-val-url
with your actual Val.town HTTP val URL. For example:
https://tijs--2e2e3aac63bc11f085130224a6c84d84.web.val.run
That's it! The OAuth client configuration is automatically generated based on this URL.
The OAuth client metadata is automatically generated based on your VALTOWN_URL
environment variable. No manual configuration is needed!
The client metadata will be available at:
https://your-val-url.web.val.run/client
https://your-val-url.web.val.run/client-metadata.json
The service automatically registers with your Mastodon instance during setup - no manual configuration needed.
- Visit your HTTP val URL (e.g.,
https://your-val-url.web.val.run
) - Click "Get Started" to begin the setup wizard
- Connect your Bluesky account by entering your handle
- Connect your Mastodon account by entering your instance URL
- Complete the setup - that's all!
Once configured, the service runs automatically:
- Every 15 minutes (or your configured interval), the cron job runs
- It fetches new posts from your Bluesky account
- Transforms the content (mentions become profile links)
- Uploads any media to your Mastodon instance
- Creates the cross-post on Mastodon
- Logs the result for your review
✅ Included:
- Regular posts with text
- Posts with images/videos
- Posts with links and hashtags
- Posts with mentions (converted to profile links)
❌ Excluded:
- Replies to other posts
- Empty posts
- Posts you've already cross-posted
- Connection Status: See which accounts are connected
- Sync Controls: Enable/disable auto-sync and trigger manual syncs
- Recent Activity: View recently synced posts and their status
- Error Logs: Track sync failures and troubleshoot issues
- Disconnect Options: Disconnect individual accounts if needed
Visit your val's dashboard to see:
- Setup status and account connections
- Recent sync activity and statistics
- Error logs for troubleshooting
- Post sync history
- Free Val.town accounts: 15-minute minimum sync interval
- Bluesky media limits: 1MB images, 100MB videos
- Mastodon compatibility: Works with all Mastodon instances
- Single user: Designed for personal use (one Bluesky → one Mastodon)
OAuth failures: Check your client metadata URL is publicly accessible
Sync not working: Verify both accounts are connected in the dashboard
Missing posts: Check the sync logs for specific error messages
Media upload fails: Bluesky/Mastodon may have different file size limits
- All tokens are stored securely in SQLite
- No data leaves your Val.town instance
- OAuth follows security best practices (PKCE, DPoP)
- You can disconnect accounts anytime from the dashboard
- Single-user architecture prevents data mixing
This is an open-source project. For issues or feature requests, check the repository or Val.town community forums.