FeaturesTemplatesShowcaseTownie
AI
BlogDocsPricing
Log inSign up
tijs

tijs

atproto-to-fediverse

Syncing Bluesky posts to Mastodon - Prod
Public
Like
1
atproto-to-fediverse
Home
Code
15
.claude
1
backend
7
frontend
8
shared
1
tests
5
.gitignore
.vtignore
CLAUDE.md
README.md
client-metadata.json
client-metadata.json.template
cookies.txt
C
cronjob.ts
deno.json
val-town.md
Branches
1
Pull requests
Remixes
2
History
Environment variables
3
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
README.md
Code
/
README.md
Search
7/18/2025
Viewing readonly version of main branch: v175
View latest version
README.md

ATProto to Fediverse Bridge

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.

What it does

  • 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

Features

✅ 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

Setup on Val.town

1. Fork or Import Files

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

2. Configure Val.town Triggers

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)

3. Environment Variables

Set these environment variables in your Val.town account:

ATPROTO_CLIENT_ID=https://your-val-url.web.val.run/client-metadata.json

Replace your-val-url with your actual Val.town HTTP val URL. For example: https://tijs--2e2e3aac63bc11f085130224a6c84d84.web.val.run/client-metadata.json

The client-metadata.json file will be served by your HTTP val (see OAuth setup below).

4. OAuth Setup

For ATProto/Bluesky

Create a client-metadata.json file in your project root by copying the template:

cp client-metadata.json.template client-metadata.json

Then update the URLs in client-metadata.json to match your Val.town HTTP val URL (replace YOUR-VAL-URL with your actual Val.town URL):

{ "client_id": "https://your-val-url.web.val.run/client-metadata.json", "client_name": "ATProto to Fediverse Bridge", "redirect_uris": [ "https://your-val-url.web.val.run/api/oauth/atproto/callback" ], "scope": "atproto transition:generic", "response_types": ["code"], "grant_types": ["authorization_code", "refresh_token"], "token_endpoint_auth_method": "none", "dpop_bound_access_tokens": true }

For Mastodon

The service automatically registers with your Mastodon instance during setup - no manual configuration needed.

5. Initial Setup

  1. Visit your HTTP val URL (e.g., https://your-val-url.web.val.run)
  2. Click "Get Started" to begin the setup wizard
  3. Connect your Bluesky account by entering your handle
  4. Connect your Mastodon account by entering your instance URL
  5. Complete the setup

Usage

Once configured, the service runs automatically:

  1. Every 15 minutes (or your configured interval), the cron job runs
  2. It fetches new posts from your Bluesky account
  3. Transforms the content (mentions become profile links)
  4. Uploads any media to your Mastodon instance
  5. Creates the cross-post on Mastodon
  6. Logs the result for your review

What Gets Cross-Posted

✅ 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

Dashboard Features

  • 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

Monitoring

Visit your val's dashboard to see:

  • Setup status and account connections
  • Recent sync activity and statistics
  • Error logs for troubleshooting
  • Post sync history

Limitations

  • 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)

Troubleshooting

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

Privacy & Security

  • 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

Support

This is an open-source project. For issues or feature requests, check the repository or Val.town community forums.


Built with ❤️ for the decentralized social web

FeaturesVersion controlCode intelligenceCLI
Use cases
TeamsAI agentsSlackGTM
ExploreDocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareersBrandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.