zurück / back

Mastodon for ActivityPub development

Why?

I'm working on my own implementation of ActivityPub in Rust. activitypub.rocks is a great starting point, the specs are quite readable and there is the very good book written by Evan. But those are not very helpful if I think I followed their rules, yet other servers don't want to talk to my code.

In theory "other servers" means every other AP server, but I had to get started somewhere. The obvious choice was Mastodon, because it is the most common one. So my idea was:

Is it feasible to have a local Mastodon instance for development? I would let my code talk to it, and if something goes wrong, I'll fire up a debugger to have a live view of why Mastodon is unhappy with my request.

The answer is "Yes!" and this document will show I got this working without too much effort.

Setting up a dev environment

Mastodon actually has very straightforward docs for this. I chose the "Manual install from source" option, because I wanted to attach the process to a Ruby debugger later. I have only very basic Ruby skills and don't want to increase the challenge by adding Docker to the mix.

Most additional services are optional, but you will need Postgres and Redis. This compose.yaml is all you need:

services:
  db:
    image: "postgres:17"
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: adminpwd
      PGDATA: /var/lib/postgresql/data/pgdata 
    volumes:
      - type: bind
        source: ./pg_data
        target: /var/lib/postgresql/data 
  redis:
    image: "redis"
    ports:
      - "6379:6379"

Run docker compose up and you should be ready to go.

Using a public domain

Some features require me to expose my development instance to the public Internet. My general approach of using Caddy and a tiny Hetzner VM is described here. The Caddyfile for this use case looks like this:

dev.my.domain {
  reverse_proxy * 0.0.0.0:3333
}
masto.my.domain {
  handle_path /api/v1/streaming {
    reverse_proxy * 0.0.0.0:4000
  }
  reverse_proxy * 0.0.0.0:3000
}

To prevent confusion: Port 3333 is used by my own app and it uses the dev subdomain. Port 3000 is used by the main Rails application. The Vite server uses port 4000. Getting the streaming stuff right was the most challenging part, because it requires additional tweaking.

Setting up the environment

A few required settings are missing to get everything working via the public URL. Look at the great Mastodon docs for details. Using a Fish shell, I set the required variables to those values:

set -x LOCAL_DOMAIN masto.my.domain

set -x RAILS_LOG_LEVEL debug
set -x RAILS_ENV development

set -x DB_USER mastodon
set -x DB_PASS mastodon
set -x DB_HOST localhost

set -x RAILS_SERVE_STATIC_FILES 1
set -x STREAMING_API_BASE_URL wss://masto.my.domain

Most settings should be obvious, assuming you have basic web dev skills. The trickiest part for me was the last line. The development setup expects requests from the local machine. The WebSocket connection will not work via the SSH tunnel from the public domain. Hardcoding STREAMING_API_BASE_URL to the correct wss connection was the simplest fix I could find.

Now would be a good time to have a break and check that everything is working fine.

Debugging

The remaining steps were suprisingly easy. I'm a user of Neovim, but for this debugging experiment I decided to go for Visual Studio Code. After a fresh install, I added the "Ruby LSP" and "VSCode rdbg Ruby Debugger" extensions.

Finally, open the Procfile.dev in your Mastodon folder and change the line for web to this one:

web: env PORT=3000 bundle exec rdbg --open -n -c --\
  bundle exec puma -C config/puma.rb

The bundle exec rdbg --open -n -c part runs Mastodon with remote debugging enabled. After restarting your instance:

Impressum