ActivityPub Plugin

:discourse2: Summary Discourse ActivityPub allows you to publish Discourse posts via ActivityPub so they can be read on services that support ActivityPub such as Mastodon.
:hammer_and_wrench: Repository Link GitHub - discourse/discourse-activity-pub: Adds ActivityPub support to Discourse.
:open_book: Install Guide How to install plugins in Discourse

If youā€™re unfamiliar with ActivityPub start by watching this short video:

When youā€™re ready to get started setting up the plugin, check out our activitypub Integrations topics:

58 Likes

Thank you for your work here! Really looking forward to turning this on, and thanks for releasing it for testing, so that those of us with various setups can contribute at least testing in a wider set of scenarios.

As I think about when to turn this on for my site, Iā€™m trying to think through what the ā€œone-way doorsā€ might be.

Sub-categories

I have 8 top-level categories, but in addition I have 21 public sub-categories. Having to follow 29 actors to follow all public site activity seems like a lot. Additionally, it might be a surprise to users that following a category doesnā€™t follow the sub-categories.

If I could select an option in category ActivityPub settings for ā€œalso federate public sub-categoriesā€ I would do that. Whether to separately federate sub-categories feels like a mostly one-way door from a user point of view, I think. So if you would consider an ā€œalso federate sub-categoriesā€ option, that might inform my plans. Our default sidebar shows only the top-level categories, and the Discourse default when looking at them is all the top-level and sub-category content together, so this seems reasonably consistent with Discourse expectations.

Federate all the things!

I think that it might be interesting to also add a site-wide setting for an ActivityPub actor that federates all public posts on a site. I would use that myself. But I donā€™t see that as a one-way door ā€” I would set up category federation as soon as it was otherwise ready, even if later I was going to add an ā€œall@ā€ actor to federate out everything.

Edit limitations

One of the things that is not obvious from your post is that it can be read that it is only the literal [note]...[/note] section that canā€™t be edited. For others here, what it really means is that if you federate, editing is currently disabled on all posts as soon as they are federated (except for posts with a literal [note]...[/note] section); this message shows up only when you try to save your edit:

My site intentionally allows sufficiently trusted users to edit indefinitely, so I canā€™t turn this on without the ability to disable this control (or the control being removed because edits are federated). By way of a general example applicable beyond such trusting sites, any category with wiki-mode posts will be broken by setting up federation for that category.

I tried to delete a federated post that I had made a wiki, and got this:

However, the post was deleted and the deletion was federated.

Personality

It would be nice to provide a profile picture for each designated actor. In Mastodon, I just see the gray elephant picture for each category actor, and Iā€™d love to add a little personality here. :smiling_face:

Error logs

I have gotten two kinds of errors in my logs from the activitypub plugin so far:

Message (3 copies reported)

NoMethodError (undefined method `ap_type' for nil:NilClass)
(eval):7:in `_fast_attributes'
app/controllers/application_controller.rb:418:in `block in with_resolved_locale'
app/controllers/application_controller.rb:418:in `with_resolved_locale'
lib/middleware/omniauth_bypass_middleware.rb:74:in `call'
lib/content_security_policy/middleware.rb:12:in `call'
lib/middleware/anonymous_cache.rb:367:in `call'
config/initializers/100-quiet_logger.rb:20:in `call'
config/initializers/100-silence_logger.rb:29:in `call'
lib/middleware/enforce_hostname.rb:24:in `call'
lib/middleware/request_tracker.rb:228:in `call'

Backtrace

plugins/discourse-activity-pub/lib/discourse_activity_pub/ap/activity.rb:18:in `object'
plugins/discourse-activity-pub/app/serializers/discourse_activity_pub/ap/activity_serializer.rb:18:in `_object'
plugins/discourse-activity-pub/app/serializers/discourse_activity_pub/ap/activity_serializer.rb:9:in `attributes'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:480:in `_serializable_hash'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:359:in `serializable_hash'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:347:in `as_json'
plugins/discourse-activity-pub/lib/discourse_activity_pub/ap/object.rb:50:in `json'
plugins/discourse-activity-pub/app/serializers/discourse_activity_pub/ap/collection/ordered_collection_serializer.rb:7:in `map'
plugins/discourse-activity-pub/app/serializers/discourse_activity_pub/ap/collection/ordered_collection_serializer.rb:7:in `ordered_items'
(eval):7:in `_fast_attributes'
Message

Job exception: undefined method `model' for #<DiscourseActivityPubActivity id: 2, ap_id: "https://social.makerforums.info/cf2e1e8d-83e3-447e...", ap_key: nil, ap_type: "Follow", local: nil, actor_id: 3, object_id: "1", object_type: "DiscourseActivityPubActor", summary: nil, published_at: nil, created_at: "2023-06-01 02:23:13.798564000 +0000", updated_at: "2023-06-01 02:23:13.798564000 +0000">


Backtrace

/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activemodel-7.0.4.3/lib/active_model/attribute_methods.rb:458:in `method_missing'
/var/www/discourse/plugins/discourse-activity-pub/app/models/discourse_activity_pub_activity.rb:79:in `after_deliver'
/var/www/discourse/plugins/discourse-activity-pub/app/jobs/discourse_activity_pub_deliver.rb:42:in `perform_request'
/var/www/discourse/plugins/discourse-activity-pub/app/jobs/discourse_activity_pub_deliver.rb:13:in `execute'
/var/www/discourse/app/jobs/base.rb:249:in `block (2 levels) in perform'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/rails_multisite-5.0.0/lib/rails_multisite/connection_management.rb:82:in `with_connection'
/var/www/discourse/app/jobs/base.rb:236:in `block in perform'
/var/www/discourse/app/jobs/base.rb:232:in `each'
/var/www/discourse/app/jobs/base.rb:232:in `perform'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/sidekiq-6.5.9/lib/sidekiq/processor.rb:202:in `execute_job'

Thanks again for your work on this!

5 Likes

Iā€™m happy to say that the SocialHub is already running it:

ā€¦ tap, tap, tapā€¦

It should have appeared on the Fediverse by nowā€¦ Looking at the logsā€¦

Oh, there are issues with the first try at following an actor:

  • Job exception: undefined method modelā€™ for #<DiscourseActivityPubActivity id: 2, ap_id: ā€œhttps://ps.s10y.eu/820e6bf2-ec02-49f8-ab5e-e3cefbbā€¦ā€, ap_key: nil, ap_type: ā€œFollowā€, local: nil, actor_id: 4, object_id: ā€œ2ā€, object_type: ā€œDiscourseActivityPubActorā€, summary: nil, published_at: nil, created_at: ā€œ2023-06-01 13:30:16.131136000 +0000ā€, updated_at: ā€œ2023-06-01 13:30:16.131136000 +0000ā€>`
  • Job exception: undefined method modelā€™ for #<DiscourseActivityPubActivity id: 8, ap_id: ā€œhttps://mastodon.social/ce62fd77-fe03-4060-a951-79ā€¦ā€, ap_key: nil, ap_type: ā€œFollowā€, local: nil, actor_id: 8, object_id: ā€œ2ā€, object_type: ā€œDiscourseActivityPubActorā€, summary: nil, published_at: nil, created_at: ā€œ2023-06-01 13:39:14.512966000 +0000ā€, updated_at: ā€œ2023-06-01 13:39:14.512966000 +0000ā€>`
  • Job exception: undefined method modelā€™ for #<DiscourseActivityPubActivity id: 6, ap_id: ā€œhttps://social.coop/4236c87a-0d22-402d-8546-e37a21ā€¦ā€, ap_key: nil, ap_type: ā€œFollowā€, local: nil, actor_id: 6, object_id: ā€œ2ā€, object_type: ā€œDiscourseActivityPubActorā€, summary: nil, published_at: nil, created_at: ā€œ2023-06-01 13:38:54.493118000 +0000ā€, updated_at: ā€œ2023-06-01 13:38:54.493118000 +0000ā€>`
Here is the backtrace for the last one
ctivemodel-7.0.4.3/lib/active_model/attribute_methods.rb:458:in `method_missing'

/var/www/discourse/plugins/discourse-activity-pub/app/models/discourse_activity_pub_activity.rb:79:in `after_deliver'

/var/www/discourse/plugins/discourse-activity-pub/app/jobs/discourse_activity_pub_deliver.rb:42:in `perform_request'

/var/www/discourse/plugins/discourse-activity-pub/app/jobs/discourse_activity_pub_deliver.rb:13:in `execute'

/var/www/discourse/app/jobs/base.rb:249:in `block (2 levels) in perform'

rails_multisite-5.0.0/lib/rails_multisite/connection_management.rb:82:in `with_connection'
/var/www/discourse/app/jobs/base.rb:236:in `block in perform'

/var/www/discourse/app/jobs/base.rb:232:in `each'

/var/www/discourse/app/jobs/base.rb:232:in `perform'

sidekiq-6.5.9/lib/sidekiq/processor.rb:202:in `execute_job'

sidekiq-6.5.9/lib/sidekiq/processor.rb:170:in `block (2 levels) in process'

sidekiq-6.5.9/lib/sidekiq/middleware/chain.rb:177:in `block in invoke'

/var/www/discourse/lib/sidekiq/pausable.rb:134:in `call'

sidekiq-6.5.9/lib/sidekiq/middleware/chain.rb:179:in `block in invoke'

sidekiq-6.5.9/lib/sidekiq/middleware/chain.rb:182:in `invoke'

sidekiq-6.5.9/lib/sidekiq/processor.rb:169:in `block in process'

sidekiq-6.5.9/lib/sidekiq/processor.rb:136:in `block (6 levels) in dispatch'

sidekiq-6.5.9/lib/sidekiq/job_retry.rb:113:in `local'

sidekiq-6.5.9/lib/sidekiq/processor.rb:135:in `block (5 levels) in dispatch'

sidekiq-6.5.9/lib/sidekiq.rb:44:in `block in <module:Sidekiq>'

sidekiq-6.5.9/lib/sidekiq/processor.rb:131:in `block (4 levels) in dispatch'

sidekiq-6.5.9/lib/sidekiq/processor.rb:263:in `stats'

sidekiq-6.5.9/lib/sidekiq/processor.rb:126:in `block (3 levels) in dispatch'

sidekiq-6.5.9/lib/sidekiq/job_logger.rb:13:in `call'

sidekiq-6.5.9/lib/sidekiq/processor.rb:125:in `block (2 levels) in dispatch'

sidekiq-6.5.9/lib/sidekiq/job_retry.rb:80:in `global'

sidekiq-6.5.9/lib/sidekiq/processor.rb:124:in `block in dispatch'

sidekiq-6.5.9/lib/sidekiq/job_logger.rb:39:in `prepare'

sidekiq-6.5.9/lib/sidekiq/processor.rb:123:in `dispatch'

sidekiq-6.5.9/lib/sidekiq/processor.rb:168:in `process'

sidekiq-6.5.9/lib/sidekiq/processor.rb:78:in `process_one'

sidekiq-6.5.9/lib/sidekiq/processor.rb:68:in `run'

sidekiq-6.5.9/lib/sidekiq/component.rb:8:in `watchdog'

sidekiq-6.5.9/lib/sidekiq/component.rb:17:in `block in safe_thread'

The Note does not seem to appear in my feed :cry:

(feel free to move this message to a more appropriate place)

7 Likes

In the long run, I agree, this should be specific per actor and configurable. But atm, the site logo should be picked up. It does for me for @feature@meta.discourse.org:

3 Likes

Ok, probably this was related to one of the errors I saw, then. Site logo makes lots of sense to me.

Thinking about thisā€¦ If there would be a possibility to use the category color in there it would be awesome but I donā€™t know whether there would be a place for that.

3 Likes

I retried andā€¦

image

Partly working: the note was shortened a bit too much (71 characters) from the original:

Weā€™ve been waiting for it: now thanks to Angus McLeod and the pavilion team, as well as CDCK, Inc. the company behind #Discourse, the #SocialHub is now federated! You can follow @fediverse@socialhub.activitypub.rocks for general announcements, and @feps@socialhub.activitypub.rocks for Fediverse Enhancement Proposals.
:hugs:

3 Likes

General recommendation:

Think on it for a few days before you enable Activitypub integration.
Remember that these accounts will be listed forever within the fediverse of social networks and can create confusion if you realize the account/category name should be changed after the fact. :heart:

1 Like

I believe that in the long run, it would be possible to additionally implement account movement to rename the actor for a category. Itā€™s just more work that has to be valuable to someone willing to do the work, so I doubt itā€™s a short term thing. :smiling_face: At least, I hope it comes later than other more valuable work! :grin:

That doesnā€™t invalidate your point at all.

2 Likes

Thanks for the feedback guys :slight_smile:

@hellekin This will address the exceptions you were reporting. @mcdanlj It will also address the second exception you reported (same issue).

@mcdanlj The first issue you reported is a little stranger how frequently are you seeing it? Could you possibly share more details?

Thanks for the feedback on that, will definitely consider it in phase 2 of the work :+1:

I think thatā€™s probably not going to happen in phase 2, but noted :+1:

Editing is only disabled on the content of the post that makes up the Note. If you donā€™t use the Note tags, thatā€™s the first 500 characters, or whatever you set activity pub note excerpt maxlength to. One of the things Iā€™m definitely considering for phase 2 is to require [note][/note] tags for a Note as I can see confusion arising out of this.

You can address this by using [note][/note] tags to separate the Note from the other content.

Noted! We havenā€™t explicitly tested this with wiki posts. I havenā€™t been able to reproduce your issue with deleting wiki posts so far. Can you reliably reproduce it? Iā€™ve added some specs in this PR which are all passing for that scenario, but keen to get to the bottom of that one.

The excerpt parsing is currently handled by the same system used by the topic excerpt parsing in Discourse (for the purposes of the topic list) which is probably why it cut out a fair chunk of that (topic list excerpts would have as well). I think we might need to use a separate parser as I can forsee this creating a few issues. Sit tight on this one; itā€™ll definitely be addressed in phase 2.

Keep in mind that you can change the display name as much as you like and that the limitation on changing the username is not unique to this plugin. So while what you say is true, this is true for a fair chunk of the fediverse.

Moreover, I havenā€™t included the username in the Actorā€™s id, partly to allow for the possibility of adding the ability to change the username in the future.

The pluginā€™s ActivityPub data is abstracted from Discourseā€™s data. Thereā€™s no inherent limitation in changing the Discourse model (i.e. what category) an actor is associated with. You could even possibly associate the actor with a different type of Discourse model in the future, i.e. associate an Actor you created for a category with a user instead (the relationship between Actors and Discourse models is polymorphic). Not saying these will be explicitly added as features, but more to say that there arenā€™t hard limitations in this respect.

In general, yes itā€™s worth considering what categories you want as actors, but overall this implementation has a relatively high degree of flexibility so your degree of concern should be relative to that context. Iā€™ve explained a little more about the nature of the data flow in the plugin, which further elucidates this point, here

Thanks again everyone! Itā€™s great to have engaged users.

4 Likes

I think it was associated with deleting posts, since I deleted three test posts and there were three instances of the error.

That would make it absolutely unusable for my site. Iā€™m not trying to use Discourse as a harder way to write posts to the fediverse than just writing them in Mastodon. Trying to force my entire community to start using [note]...[/note] bbcode annotations, including the frequent new site users seeking help, would be a non-starter.

No, I definitely canā€™t. Itā€™s not about me. Itā€™s about my site users, who are Discourse users, not discourse/discourse-activity-pub.git users.

Itā€™s just an edit.

Federating edits is normal. To the best of my understanding, most of the widely-used ActivityPub implementations out there have allowed edits for a long time. Mastodon was the odd hold-out among popular implementations until last year when they finally caved and allowed edits.

I recognize that edits werenā€™t part of the MVP release, but until it either federates edits or gives me the option to turn off making the note uneditable, I canā€™t turn this on for my site. I donā€™t know whether my use case is weird or normal, of course.

My own goal for using the ActivityPub plugin is to make it easier to follow and share content as it is being created by my community forum members using Discourse. Itā€™s not to use Discourse as a weird uneditable post-only replacement for Mastodon. :smiling_face:

My understanding is that you do intend to eventually support federating edits, since the error message says ā€œnot currently supportedā€ ā€” is that right? In that case, this is all just a temporary limitation and our difference in use case are not important. But it does make me wonder about investing effort into protecting edits vs. supporting federating edits.

I had forgotten to remove the retort plugin from my sandbox site when I removed it from my production site. Removing retort fixed both the weird error log and the weird error message. My mistake!

Does this mean that as the plugin stands today, we could use the same actor for multiple categories? Or that the data model is very flexible and the implementation details are easy to change?

FWIW in my use case, my preference would be to post the whole thing without any limitations. I know thatā€™s not how others might configure it, but I think in the end I would set the max size to the same as the max size for a post on my Discourse, which isā€¦ large! :smiling_face:

1 Like

Gotcha, Iā€™ll investigate further soon.

Understood. My comment was more that editing non-note content is indeed possible after publication currently. Youā€™re right that this first version is not going to be without its UX issues for general use by any user. It does support other uses quite well though, e.g. federating announcements.

Yes, weā€™ll be adding support for editing Note content post-publication in phase 2. Note that editing is currently supported prior to the note being published. When you build something like this you have to reduce the number of moving parts as much as possible.

No, not yet. Could you explain what you mean here a bit more? Are you thinking of having a site-wide actor or are you more thinking about the sub-category issue?

Yes, although I wouldnā€™t necessarily use the term ā€œeasyā€ :wink:

Iā€™m curious where youā€™re anticipating federating the unrestricted content? Genuine question; I want to understand where youā€™re coming from a bit more here.

1 Like

I forgot to edit that out after discovering that my leaving retort in place accidentally was the source of the error. :flushed:

Oops.

Good enough! :pray:

Iā€™ll not worry about this any more, and in phase 2 Iā€™ll help test that.

I vaguely wondered about both, but it was a low-value question. Was just curious how far I could carry my experimentation with my staging site, and didnā€™t want to try things that were known to be impossible already. :+1:

Iā€™ll try!

Context: Mastodon limits the length of post it will allow users to author but not the length of post it will display. I run a Mastodon-glitch server configured with a 2000-character limit; Iā€™ve seen several people post about configuring a 31337-character limit (because of course). I see long posts in my feed from folks using pleroma, akkoma, etc.

We have some really cool content showing up on Maker Forums (my discourse) from time to time (in my opinion, anyway!), and Iā€™d like to promote it on the Fediverse generally to make it accessible to a wider audience. I know that Mastodon has been opinionated that 500 characters is the magic length for microblogging ā€” but ActivityPub isnā€™t limited to microblogging. Itā€™s federation, including but not limited to microblogging. When I follow an ActivityPub actor in my Mastodon account, itā€™s not limited to short posts. I can read, comment on, and boost longer posts.

So the ā€œwhere youā€™re anticipating federatingā€ is a confusing question to me.

  • I would boost the content from my Mastodon-glitch server. Boosts do not apply the character limit that applies to authoring posts even on stock Mastodon.
  • I would expect community members to follow from any ActivityPub server (recognizing this will be more Mastodon than others at least right now), and they wonā€™t all have the same opinionated view of how long an ActivityPub note should be.

Ultimately, as the server administrator, Iā€™m not in control of where this content is federated to. This is the decision of every follower. Itā€™s not obvious to me why excepting vs federating the entire discourse post as an activitypub note would be the default even for followers on Mastodon, but more so throughout the Fediverse.

Iā€™m not sure that answers your question. But Iā€™m not sure I understand it well, either. :grin: Happy to dive deeper on this if youā€™d like.

1 Like

No worries! Glad weā€™ve covered the exceptions for now then. Let us know if you see any more. @hellekin Likewise, let us know if you see any other issues or strange behaviour.

Great! Thank you.

It does! Thank you, itā€™s useful context for our planning of phase 2.

4 Likes

Iā€™m about to upgrade to the latest version. One thing that would be useful is to choose the audience: whether the post will be public or followers-only. Now itā€™s the latter, so we cannot boost it, and must follow the account prior to receiving notices. I appreciate that because it remains experimental, but it should definitely be an option to post publicly so we can advertise the feature more widely to the Fedizens :slight_smile:

4 Likes

Yes, thatā€™s something weā€™ll be considering in phase 2 :+1:

4 Likes

Do you have a public roadmap for the development phases?

2 Likes

Thereā€™s no public roadmap, but I will let you know when we figure out the specifics. Any reasonable suggestions for what should be in it are welcome! Specific use case examples like @mcdanljā€™s are always helpful too.

5 Likes

I made some proposals on the SocialHub that were already made here some time agoā€¦

1 Like

I hadnā€™t gotten to the point of noticing this yet in my testing from a staging server, but Iā€™d like to acknowledge that boosting is central to my own use case as well.

5 Likes

Handling 410 Gone

My logs have a lot of (changed URL):

[Discourse Activity Pub] GET request to https://activitypub.example/users/missing-account failed: Expected([200, 201, 202, 301, 302, 307, 308]) <=> Actual(410 Gone)

I believe this is related to: Object retrieval SHOULD respond with 410 Gone status code if Tombstone is in response body, otherwise responds with 404 Not Found (server:object-retrieval:deleted-object:410-status): the account has moved to another instance.


Also getting a few:



[Discourse Activity Pub] update_stored_from_json failed to save actor. AR errors: Ap has already been taken. Actor JSON: {"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","toot":"http://joinmastodon.org/ns#","featured":{"@id":"toot:featured","@type":"@id"},"featuredTags":{"@id":"toot:featuredTags","@type":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","@type":"@id"},"movedTo":{"@id":"as:movedTo","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value","discoverable":"toot:discoverable","Device":"toot:Device","Ed25519Signature":"toot:Ed25519Signature","Ed25519Key":"toot:Ed25519Key","Curve25519Key":"toot:Curve25519Key","EncryptedMessage":"toot:EncryptedMessage","publicKeyBase64":"toot:publicKeyBase64","deviceId":"toot:deviceId","claim":{"@type":"@id","@id":"toot:claim"},"fingerprintKey":{"@type":"@id","@id":"toot:fingerprintKey"},"identityKey":{"@type":"@id","@id":"toot:identityKey"},"devices":{"@type":"@id","@id":"toot:devices"},"messageFranking":"toot:messageFranking","messageType":"toot:messageType","cipherText":"toot:cipherText","suspended":"toot:suspended"}],"id":"https://mas.to/users/rikvipcode","type":"Person","following":"https://mas.to/users/rikvipcode/following","followers":"https://mas.to/users/rikvipcode/followers","inbox":"https://mas.to/users/rikvipcode/inbox","outbox":"https://mas.to/users/rikvipcode/outbox","featured":"https://mas.to/users/rikvipcode/collections/featured","featuredTags":"https://mas.to/users/rikvipcode/collections/tags","preferredUsername":"rikvipcode","name":"","summary":"","url":"https://mas.to/@rikvipcode","manuallyApprovesFollowers":false,"discoverable":false,"published":"2023-05-04T00:00:00Z","devices":"https://mas.to/users/rikvipcode/collections/devices","suspended":true,"publicKey":{"id":"https://mas.to/users/rikvipcode#main-key","owner":"https://mas.to/users/rikvipcode","publicKeyPem":"--...


And

[Discourse Activity Pub] GET request to https://mastodon.social/users/ejovoni46709 failed: Expected([200, 201, 202, 301, 302, 307, 308]) <=> Actual(410 Gone)
[Discourse Activity Pub] Failed to process https://mastodon.social/users/ejovoni46709: Could not resolve actor
[Discourse Activity Pub] Failed to process https://mastodon.social/users/ejovoni46709#delete: Could not create actor
[Discourse Activity Pub] Failed to process https://mastodon.social/users/epybisezax9438#delete: Could not find object
2 Likes