ActivityPub Plugin

@hellekin Thanks for the report. A certain amount of failed requests will always occur on an ActivityPub service as actors in the fediverse come and go. For example, it looks like

The verbosity of the logs are there to help debug, however if they’re cluttering your logs you can toggle them using the site setting activity pub verbose logging. The default is off.

We will be looking to make error handling improvements in phase two if necessary, but so far it looks like the snippets you posted are expected, i.e. the Actors are indeed no longer in the fediverse.

Currently, the way the plugin handles delivery failures is to track them in the same way Mastodon does, namely, if there are 7 days of failure to an endpoint it will be marked as “unavailable” and requests will no longer be attempted.


Yes, as indicated in the line prior to it.

Indeed, but the status code is 410, which means the account may have moved (if there is a Tombstone — is this condition checked?)

1 Like

No. It is error 410 Gone and means resource is deleted. What am I missing now?

1 Like

You missed this:

Hi @angus,

I just setup a brand new self-hosted discourse with your AP plugin over at, and am seeing some 403 errors in the Discourse Error Logs (and the posts aren’t sharing).

I’m wondering if it might be because there are hyphens maybe?

[Discourse Activity Pub] GET request to failed: Expected([200, 201, 202, 301, 302, 307, 308]) <=> Actual(403 Forbidden)

/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:66:in `rescue in perform'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:50:in `perform'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:34:in `get_json_ld'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/request.rb:106:in `get_json_ld'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/json_ld.rb:52:in `request_object'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/json_ld.rb:48:in `resolve_object'
/var/www/discourse/plugins/discourse-activity-pub/lib/discourse_activity_pub/ap/actor.rb:103:in `resolve_and_store'
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/concerns/discourse_activity_pub/signature_verification.rb:192:in `actor_from_key_id'
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/concerns/discourse_activity_pub/signature_verification.rb:57:in `signed_request_actor'
/var/www/discourse/plugins/discourse-activity-pub/app/controllers/concerns/discourse_activity_pub/signature_verification.rb:27:in `ensure_verified_signature'
activesupport- `block in make_lambda'
activesupport- `block (2 levels) in halting_and_conditional'
actionpack- `block (2 levels) in <module:Callbacks>'
activesupport- `block in halting_and_conditional'
activesupport- `block in invoke_before'
activesupport- `each'
activesupport- `invoke_before'
activesupport- `block in run_callbacks'
/var/www/discourse/app/controllers/application_controller.rb:418: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:418:in `with_resolved_locale'
activesupport- `block in run_callbacks'
activesupport- `run_callbacks'
actionpack- `process_action'
actionpack- `process_action'
actionpack- `block in process_action'
activesupport- `block in instrument'
activesupport- `instrument'
activesupport- `instrument'
actionpack- `process_action'
actionpack- `process_action'
activerecord- `process_action'
actionpack- `process'
actionview- `process'
rack-mini-profiler-3.1.0/lib/mini_profiler/profiling_methods.rb:85:in `block in profile_method'
actionpack- `dispatch'
actionpack- `dispatch'
actionpack- `dispatch'
actionpack- `serve'
actionpack- `block in serve'
actionpack- `each'
actionpack- `serve'
actionpack- `call'
railties- `call'
railties- `public_send'
railties- `method_missing'
actionpack- `block in <class:Constraints>'
actionpack- `serve'
actionpack- `block in serve'
actionpack- `each'
actionpack- `serve'
actionpack- `call'
/var/www/discourse/lib/middleware/omniauth_bypass_middleware.rb:74:in `call'
rack-2.2.7/lib/rack/tempfile_reaper.rb:15:in `call'
rack-2.2.7/lib/rack/conditional_get.rb:27:in `call'
rack-2.2.7/lib/rack/head.rb:12:in `call'
actionpack- `call'
/var/www/discourse/lib/content_security_policy/middleware.rb:12:in `call'
/var/www/discourse/lib/middleware/anonymous_cache.rb:367:in `call'
rack-2.2.7/lib/rack/session/abstract/id.rb:266:in `context'
rack-2.2.7/lib/rack/session/abstract/id.rb:260:in `call'
actionpack- `call'
actionpack- `block in call'
activesupport- `run_callbacks'
actionpack- `call'
actionpack- `call'
actionpack- `call'
logster-2.12.2/lib/logster/middleware/reporter.rb:43:in `call'
railties- `call_app'
railties- `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- `call'
actionpack- `call'
/var/www/discourse/lib/middleware/enforce_hostname.rb:24:in `call'
rack-2.2.7/lib/rack/method_override.rb:24:in `call'
actionpack- `call'
rack-2.2.7/lib/rack/sendfile.rb:110:in `call'
actionpack- `call'
rack-mini-profiler-3.1.0/lib/mini_profiler.rb:260:in `call'
message_bus-4.3.2/lib/message_bus/rack/middleware.rb:60:in `call'
/var/www/discourse/lib/middleware/request_tracker.rb:228:in `call'
railties- `call'
railties- `public_send'
railties- `method_missing'
rack-2.2.7/lib/rack/urlmap.rb:74:in `block in call'
rack-2.2.7/lib/rack/urlmap.rb:58:in `each'
rack-2.2.7/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>'

hostname	LILEJAP07-app
process_id	658
application_version	1abfe2e61d12b1b559aab0132ec3fb7cc8b87232
REQUEST_URI	/ap/actor/5ce52c043e670476a1426f9a66472c07
HTTP_USER_AGENT	Akkoma 3.8.0-0-gccae7ef; <>
HTTP_ACCEPT	application/activity+json
time	2:16 pm


I also would be interested in full length posts. In which case, the “article” type should be used rather than “note”.

In fact, announcements are much less interesting to me than federated forum features.

Is there a roadmap and/or place to send donations for features like this?

Hey there @gme thanks for trying out the plugin

A few things to initially note here

  1. The MVP plugin is tested against Mastodon. I see you’re using Pleroma there. I know Pleroma is ActivityPub compliant and works with Mastodon, however we haven’t looked closely at what tweaks might be required (if any) to ensure support for it yet. But still interested to see what’s going on here.

  2. It looks like the requests failed due to an authentication error on your Pleroma server (that’s what a 403 error means). As I’m able to GET that endpoint just using an unauthenticated cURL request I suspect it might be the http auth that’s failing on the Pleroma end.

To test the latter (i.e. 2), could you have a look at your Pleroma logs (it looks like you’re the admin on that server too?) if possible to see if you can get more detail on that end of things?

Thanks for the feedback @bmann. Could you expand on the use case you have in mind here? With an example if possible.

This topic is the best place to keep abreast of developments. When we settle on a phase 2 plan I’ll share it here. In the meantime, the best way to help is to share specific use cases you are using, or would like to use, the plugin for.


The use case is using Discourse as a fuller AP node. There are many easier ways to post content to AP (eg use category RSS feeds and Zapier or Buffer) — but developing fuller AP capability can only be done as a plugin/integration.

Article is the ActivityStream type that is meant for full articles. Depending on the client interface, it will show a preview and then a click through to show the entire article inline (much like content warnings, but read more).

Note is the micro-blogging type.

By having full Article posts, people can directly read / boost / reply in their AP clients.

And of course, it would be interesting to hear about your roadmap if you’re going to follow a more Microblogging AP instance, or go in a federated forum direction like Lemmy or Kbin, especially given the recent Reddit news.

1 Like

@angus, @pmusaraj have you seen NGI Sargasso funding open call? This is quite short notice but it might be useful to further development on this plugin (unless you have other plans already).

1 Like

Hey guys, I’m happy to say that the second phase of work on this plugin has been approved. This is what we’ve already started work on, with the aim of releasing it in about 3.5 months.

Support editing Note post-publication

Support restoring Notes

Support publishing posts publicly as well as followers only

  • Category-level setting
  • See further Audience Targeting and Mastodon’s docs on to/cc
  • Switch to public posts as the default

Improve Note content parsing

  • Handle special characters (perhaps use a different parser). See further.

Support use of Article instead of Note as the Object for a post.

  • Category level setting

Support accepting Activities in reply to a Note made on remotes and publishing Activities in reply to a Note made in Discourse.

  • Publish Activities concerning replies made on Discourse
    • Allow Discourse users to be Actors
    • Create Note objects for Discourse replies (posts)
    • Publish associated Create/Delete/Update/Undo Activities for their equivalent Discourse actions
  • Accept Activities concerning replies made on remotes
    • Stage the Actors of Activities from remote servers as Discourse users
    • Create Discourse replies (posts) from Note objects
    • Convert associated Create/Delete/Update/Undo Activities into their equivalent Discourse actions
  • Add a category setting to toggle between First Post only (current) and “Full Topic” which supports reply Activities.

Support Like Activity

Support Discourse users verifying their identity on Mastodon so Discourse posts created from their Toots are associated with their Discourse user account.

  • Allow a user to perform the Mastodon OAuth Authorization flow with the Mastodon server where their account is stored. This is initiated from the user’s Discourse account settings.
  • Using the Discourse user’s Mastodon access token, obtain and store the AP ID of their Mastodon account and store it with their Discourse account.
  • Associate all Discourse activities associated with AP Activities from an Actor bearing a Discourse user’s AP ID with that Discourse user, whether they were performed before or after the user verified their identity.

This is really exciting to see — a really high level of federation and interactivity. :tada:

Are you planning to do any intermediate releases, or will this be one big release in ~3.5 months?

There may be some intermediate releases, but I can’t promise anything on that front yet. I’ll keep you in the loop as we go.


110% agree - this includes a lot of great aspects. :tada:

Any chance there can at least be an intermediate release on this front?

To be honest, adding a “public posts as the default” intermediate release would be immediately welcome.


Note that public posting would depend on federating edit actions (the first listed bullet), I believe.

1 Like

I can’t make any promises at this stage but there may well be intermediate updates for both Update federation and Audience Targeting (public posting).



1 Like

That’s a known limitation. Until federating edits is supported, the plugin blocks edits to federated content, and there is no configuration to disable this.


To be clear, I was trying to edit a post here on meta and got this error.


Oh, I’m sorry to have misunderstood. and at least are being federated from here, and this choice is the number one reason I haven’t enabled this for Maker Forums…


Yes, this is the first item in Phase 2 that I’ve worked on. In fact, there’s already a PR for it, so you’ll get some relief on that front soonish.