ActivityPub Plugin

I tried to install this plugin and always get this warn in /logs:

[Discourse Activity Pub] failed to schedule for delivery: no recipients


/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/delivery_handler.rb:73:in `log_failure' 
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/delivery_handler.rb:31:in `can_deliver?' 
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/delivery_handler.rb:16:in `perform' 
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/delivery_handler.rb:23:in `perform' 
/var/www/discourse/plugins/discourse-activity-pub/app/models/concerns/discourse_activity_pub/ap/model_callbacks.rb:143:in `activity_pub_deliver_activity' 
/var/www/discourse/plugins/discourse-activity-pub/app/models/concerns/discourse_activity_pub/ap/model_callbacks.rb:33:in `perform_activity_pub_activity' 
/var/www/discourse/plugins/discourse-activity-pub/plugin.rb:494:in `block (2 levels) in activate!' 
/var/www/discourse/lib/plugin/instance.rb:316:in `public_send' 
/var/www/discourse/lib/plugin/instance.rb:316:in `block (2 levels) in add_to_class' 
/var/www/discourse/plugins/discourse-activity-pub/plugin.rb:502:in `block (2 levels) in activate!' 
/var/www/discourse/lib/plugin/instance.rb:316:in `public_send' 
/var/www/discourse/lib/plugin/instance.rb:316:in `block (2 levels) in add_to_class' 
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/discourse_activity_pub/post_controller.rb:13:in `schedule' 
actionpack-7.0.7/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action' 
actionpack-7.0.7/lib/abstract_controller/base.rb:215:in `process_action' 
actionpack-7.0.7/lib/action_controller/metal/rendering.rb:165:in `process_action' 
actionpack-7.0.7/lib/abstract_controller/callbacks.rb:234:in `block in process_action' 
activesupport-7.0.7/lib/active_support/callbacks.rb:118:in `block in run_callbacks' 
/var/www/discourse/app/controllers/application_controller.rb:421:in `block in with_resolved_locale' 
i18n-1.14.1/lib/i18n.rb:322:in `with_locale' 
/var/www/discourse/app/controllers/application_controller.rb:421:in `with_resolved_locale' 
activesupport-7.0.7/lib/active_support/callbacks.rb:127:in `block in run_callbacks' 
activesupport-7.0.7/lib/active_support/callbacks.rb:138:in `run_callbacks' 
actionpack-7.0.7/lib/abstract_controller/callbacks.rb:233:in `process_action' 
actionpack-7.0.7/lib/action_controller/metal/rescue.rb:23:in `process_action' 
actionpack-7.0.7/lib/action_controller/metal/instrumentation.rb:67:in `block in process_action' 
activesupport-7.0.7/lib/active_support/notifications.rb:206:in `block in instrument' 
activesupport-7.0.7/lib/active_support/notifications/instrumenter.rb:24:in `instrument' 
activesupport-7.0.7/lib/active_support/notifications.rb:206:in `instrument' 
actionpack-7.0.7/lib/action_controller/metal/instrumentation.rb:66:in `process_action' 
actionpack-7.0.7/lib/action_controller/metal/params_wrapper.rb:259:in `process_action' 
activerecord-7.0.7/lib/active_record/railties/controller_runtime.rb:27:in `process_action' 
actionpack-7.0.7/lib/abstract_controller/base.rb:151:in `process' 
actionview-7.0.7/lib/action_view/rendering.rb:39:in `process' 
rack-mini-profiler-3.1.1/lib/mini_profiler/profiling_methods.rb:111:in `block in profile_method' 
actionpack-7.0.7/lib/action_controller/metal.rb:188:in `dispatch' 
actionpack-7.0.7/lib/action_controller/metal.rb:251:in `dispatch' 
actionpack-7.0.7/lib/action_dispatch/routing/route_set.rb:49:in `dispatch' 
actionpack-7.0.7/lib/action_dispatch/routing/route_set.rb:32:in `serve' 
actionpack-7.0.7/lib/action_dispatch/journey/router.rb:50:in `block in serve' 
actionpack-7.0.7/lib/action_dispatch/journey/router.rb:32:in `each' 
actionpack-7.0.7/lib/action_dispatch/journey/router.rb:32:in `serve' 
actionpack-7.0.7/lib/action_dispatch/routing/route_set.rb:852:in `call' 
railties-7.0.7/lib/rails/engine.rb:530:in `call' 
railties-7.0.7/lib/rails/railtie.rb:226:in `public_send' 
railties-7.0.7/lib/rails/railtie.rb:226:in `method_missing' 
actionpack-7.0.7/lib/action_dispatch/routing/mapper.rb:19:in `block in <class:Constraints>' 
actionpack-7.0.7/lib/action_dispatch/routing/mapper.rb:48:in `serve' 
actionpack-7.0.7/lib/action_dispatch/journey/router.rb:50:in `block in serve' 
actionpack-7.0.7/lib/action_dispatch/journey/router.rb:32:in `each' 
actionpack-7.0.7/lib/action_dispatch/journey/router.rb:32:in `serve' 
actionpack-7.0.7/lib/action_dispatch/routing/route_set.rb:852:in `call' 
/var/www/discourse/lib/middleware/omniauth_bypass_middleware.rb:74:in `call' 
rack-2.2.8/lib/rack/tempfile_reaper.rb:15:in `call' 
rack-2.2.8/lib/rack/conditional_get.rb:40:in `call' 
rack-2.2.8/lib/rack/head.rb:12:in `call' 
actionpack-7.0.7/lib/action_dispatch/http/permissions_policy.rb:38:in `call' 
/var/www/discourse/lib/content_security_policy/middleware.rb:12:in `call' 
/var/www/discourse/lib/middleware/anonymous_cache.rb:389:in `call' 
/var/www/discourse/lib/middleware/gtm_script_nonce_injector.rb:10:in `call' 
rack-2.2.8/lib/rack/session/abstract/id.rb:266:in `context' 
rack-2.2.8/lib/rack/session/abstract/id.rb:260:in `call' 
actionpack-7.0.7/lib/action_dispatch/middleware/cookies.rb:704:in `call' 
actionpack-7.0.7/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call' 
activesupport-7.0.7/lib/active_support/callbacks.rb:99:in `run_callbacks' 
actionpack-7.0.7/lib/action_dispatch/middleware/callbacks.rb:26:in `call' 
actionpack-7.0.7/lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call' 
actionpack-7.0.7/lib/action_dispatch/middleware/show_exceptions.rb:29:in `call' 
logster-2.13.0/lib/logster/middleware/reporter.rb:40:in `call' 
railties-7.0.7/lib/rails/rack/logger.rb:40:in `call_app' 
railties-7.0.7/lib/rails/rack/logger.rb:27:in `call' 
/var/www/discourse/config/initializers/100-quiet_logger.rb:20:in `call' 
/var/www/discourse/config/initializers/100-silence_logger.rb:29:in `call' 
actionpack-7.0.7/lib/action_dispatch/middleware/remote_ip.rb:93:in `call' 
actionpack-7.0.7/lib/action_dispatch/middleware/request_id.rb:26:in `call' 
/var/www/discourse/lib/middleware/enforce_hostname.rb:24:in `call' 
rack-2.2.8/lib/rack/method_override.rb:24:in `call' 
actionpack-7.0.7/lib/action_dispatch/middleware/executor.rb:14:in `call' 
rack-2.2.8/lib/rack/sendfile.rb:110:in `call' 
actionpack-7.0.7/lib/action_dispatch/middleware/host_authorization.rb:131:in `call' 
rack-mini-profiler-3.1.1/lib/mini_profiler.rb:413:in `call' 
message_bus-4.3.8/lib/message_bus/rack/middleware.rb:60:in `call' 
/var/www/discourse/lib/middleware/request_tracker.rb:233:in `call' 
railties-7.0.7/lib/rails/engine.rb:530:in `call' 
railties-7.0.7/lib/rails/railtie.rb:226:in `public_send' 
railties-7.0.7/lib/rails/railtie.rb:226:in `method_missing' 
rack-2.2.8/lib/rack/urlmap.rb:74:in `block in call' 
rack-2.2.8/lib/rack/urlmap.rb:58:in `each' 
rack-2.2.8/lib/rack/urlmap.rb:58:in `call' 
unicorn-6.1.0/lib/unicorn/http_server.rb:634:in `process_client'
unicorn-6.1.0/lib/unicorn/http_server.rb:739:in `worker_loop'
unicorn-6.1.0/lib/unicorn/http_server.rb:547:in `spawn_missing_workers'
unicorn-6.1.0/lib/unicorn/http_server.rb:143:in `start'
unicorn-6.1.0/bin/unicorn:128:in `<top (required)>'
/var/www/discourse/vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `load' 
/var/www/discourse/vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `<main>' 

Also, I cannot find the account I set up, on Mastodon.

Is there any solution to this?

You’re getting that warning because your category has no followers. You need at least one follower for a delivery to occur.

  1. Copy the handle from the category settings.
  2. Search for it on mastodon (it should appear).
  3. Follow it with your mastodon account.

So the problem is, I searched for it and I got a big 404.
Is it because it takes a while for it to take effect?



What do the ActivityPub category settings look like? Could you share a screenshot of them (particularly the status badge). Perhaps send it to me in a private message so we don’t take over this topic.


Is there (or will be) a way to put old topics on fediverse as well? I’ve got topics with thousands of replies and they’re not federated. Not sure how one would proceed to federate those topics?

Admins can now trigger the publishing of an individual topic in a configured category. This is available in the post’s wrench menu:

However, we don’t have a system in place to bulk publish old topics and it’s not on our priority list, given the ephemeral nature of social media, I’m not sure there’s a lot of interest in historical posts being published to ActivityPub. That said, this is likely doable with a rake task.


Is there a way to go the opposite way, say from Mastodon back to Discourse?

We have automated release notes tooted to our Mastodon, so having them mirrored into Discourse would be amazing.


Currently this is not supported, but it is in our roadmap and we plan to work on it over the next 2-3 months.


Can these roadmaps be publicly accessed, like on GitHub?


Yes, I will post an update here shortly. (The roadmap for the features added to date is a couple of dozen posts earlier in this topic.)


Right, so here’s what we have planned for the plugin for the next 2-3 months:

  • improve the plugin’s performance (with no user-facing changes)
  • add better UI elements to an ActivityPub-enabled category in Discourse
    • this means including more details about the category’s ActivityPub stats, as well as an easier flow for users wanting to follow the category in their ActivityPub client
  • allow Discourse categories to follow external actors
    • this would enable ActivityPub content to be synced to a Discourse category, this includes DIscourse-to-Discourse syncing via ActivityPub, i.e. a category in Discourse site A can follow a category in Discourse site B (where both sites have the plugin enabled and configured as appropriate)
  • bug fixes and improvements to existing functionality
    • handling avatar sync issues and edits of AP-enabled posts by other users (staff, etc.)
    • if possible, addressing current limitations to author changes and wikis

This could be an incredible development (or at least the start of something incredible :-). Right now discourse instances are maybe the highest quality online meeting and discussion places on the internet but they are all separate islands. Some sort of federation among them and maybe with other type of servers (when it makes sense) would open a new window. Watching this with some excitement!


Just to let you guys know, we just merged the PR that allows a Discourse category to follow any actor in the fediverse, including other Discourse categories. Yes, that means you can now federate a category between two (or three, or more) Discourses. There’s a few more PRs in the pipeline as part of this phase, but that was a big one.

Here’s a video me federating across two Discourse instances and a Mastodon instance running on my computer


I’m hopeful for assigning activitypub actors by tag as opposed to category. I think this would also work through chat-integration plugin support, plus that would allow further content filtering.

Another interesting idea would be to put every activitypub post into a queue for admin and mod approval before it goes live. This would be great if it could allow a group to confirm whether the topic was ready for crossposting. Same could apply to replies, rather than auto-posting them from the fediverse back into the forum.

Thanks either way for working on this awesome plugin.


Since this call if for ActivityPub software, maybe there’s a category here on Meta that we could federate with Discourse - SocialHub :slight_smile:


We’ve been enjoying the plugin, but there’s a bug in the current version between the excerpt maxlength and the link to forum. If the excerpt maxlength hits in the middle of a link, the “Discuss this on our forum” link gets a weird target:

For example:

This gets a final forum link:

I’m not conversant in Ruby, but I’d guess the code to crop the post at maxlength should check to make sure it’s not in the middle of a link before adding the “link to forum” end.


I note that as of commit fb83e554fd the wiki restriction appears removed, and that at this point the restriction on changing the post owner in federated categories remains.

I don’t change post owner often but category description posts are an example that matters to me. I set up the most active category moderator to be the owner when that changes, at which point I encourage them to update it when appropriate. This is now the biggest restriction I’m aware of that is keeping me from implementing this.

I recognize the model mismatch between Discourse and ActivityPub that creates this concern. Would it make sense to implement some privileged (admin?) action “defederate this post” that would send a delete activity for the post if it has been federated, mark it never to federate, and then allow owner change?


I hear your use case here, but I don’t see this as super high priority at the moment. For now, I’d like to focus our efforts on fine-tuning the Discourse-to-Discourse sync feature, I think that is going to require a fair bit of attention over the next few weeks.

That’s a good find, thanks for reporting the issue. We’re going to look into it.

1 Like

Will this “backfill” from the linked ActivityPub actor?

Populating a Discourse category with advisory notices already posted to Mastodon.


Backfilling is not supported yet. I’d like to support it especially for the Discourse-to-Discourse federation scenario. It might be a little bit trickier to do for the use case you described.