Switch platforms, keep your people
Bounce lets you migrate from one social network to another and keep all of your followers. It currently supports Mastodon and Bluesky. We hope to add support for Pixelfed, Threads, micro.blog, and more soon.
License: This project is placed in the public domain. You may also use it under the CC0 License.
Pull requests are welcome! First, fork and clone this repo. Then, install the Google Cloud SDK and run gcloud components install cloud-firestore-emulator to install the Firestore emulator. Once you have them, set up your environment by running these commands in the repo root directory:
gcloud config set project bounce-migrate
python3 -m venv local
source local/bin/activate
pip install -r requirements.txtNow, run the tests to check that everything is set up ok:
gcloud emulators firestore start --host-port=:8089 --database-mode=datastore-mode < /dev/null >& /dev/null &
python3 -m unittest discoverFinally, run this in the repo root directory to start the web app locally:
GAE_ENV=localdev FLASK_ENV=development flask run -p 8080If you send a pull request, please include (or update) a test for the new functionality!
If you hit an error during setup, check out the oauth-dropins Troubleshooting/FAQ section.
You may need to change granary, oauth-dropins, mf2util, or other dependencies as well as as Bounce. To do that, clone their repo locally, then install them in "source" mode with e.g.:
pip uninstall -y granary
pip install -e <path to granary>To deploy to the production instance on App Engine - if @snarfed has added you as an owner - run:
gcloud -q beta app deploy --no-cache --project bounce-migrate *.yamlBounce's current architecture and deployment is a bit unusual. It's an App Engine Standard app that runs in Bridgy Fed's Google Cloud project, bridgy-federated, so that it can access Bridgy Fed's Memorystore memcache instance.
Bounce originally ran in its own project, bounce-migrate, but I never managed to get it access to Bridgy Fed's memcache. That was tolerable for a while - I used Bridgy Fed's /admin/memcache-evict endpoint to manually evict stale cached datastore entities there when I modified them in Bounce - but eventually we needed to switch ATProto sequence number allocation to memcache, which meant Bounce _needed_access.
One awkward part of this is that Bounce still uses the datastore, task queues, etc in the old bounce-migrate project. The code is set up to point there.
Another awkward part is that Bounce now runs as the bridgy-federated App Engine service account, bridgy-federated@appspot.gserviceaccount.com. I had to give that account access to Datastore, Tasks, etc in bounce-migrate's IAM.
Originally, I tried to get memcache access via a Shared VPC. Here's what I tried.
- Set up a Shared VPC on
bridgy-federatedas the host project, for itsdefaultVPCandbounce-migrate` as the service project. - Added a
shared-serverless-vpc-connector-bouncesubnet tobridgy-federated's VPC with IP range10.9.0.0/28. - Added a Serverless VPC Access connector
to-bridgy-fed-sharedtobounce-migrateon that subnet. - Added this to Bounce's
app.yaml:vpc_access_connector: name: projects/bounce-migrate/locations/us-central1/connectors/to-bridgy-fed-shared - Redeployed Bounce.
That didn't work. Bounce couldn't connect to Bridgy Fed's memcache, and it also couldn't connect to Google APIs, eg for log collection. I tried a few more things, redeploying (if necessary) and testing after each one:
- Added lots of IAM roles, eg Serverless VPC Access User, Compute Network User on Bounce's GAE service account in the
bridgy-federatedproject, and many others - Turned on Private Google Access in the
shared-serverless-vpc-connector-bouncesubnet inbridgy-federated. - Added
egress_setting: all-traffictovpc_access_connectorinapp.yaml.- ...and then had to add a custom DNS zone for
googleapis.com.inbridgy-federatedto allow network traffic to Google APIs. (Started to feel like uncomfortable unnecessary yak shaving at this point.)
- ...and then had to add a custom DNS zone for
At this point, Bounce still couldn't connect to Bridgy Fed's memcache. It was connecting to most Google APIs, but still couldn't send logs to 199.36.153.*. I could log to stderr and get those collected and visible in Log Explorer, but not Python logs via google.cloud.logging.Client.
I removed egress_setting: all-traffic from app.yaml and redeployed, confirmed in the web console that it reverted to the default private-ip-ranges value, and it still couldn't connect to 199.36.153.* to send logs. Wtf?! It was now worse off than when I'd started. So, I reverted it all. Grr.
(I talked through a lot of this with Gemini Cloud Assist. It seemed helpful, but didn't actually get anything working.)