MRF, or Message Rewrite Facility, is a way for Pleroma to trigger side-effects on ActivityPub messages, unlocking nearly infinite possibilities.
When you author an up-to-5000 character post on Pleroma, it goes through Pleroma's ActivityPub "Pipeline." Within this pipeline is a layer called MRF, which lets users decide what can happen to ActivityPub objects.
Of course, posts aren't the only type of ActivityPub object, there are also follows, blocks, deletes, and more.
With MRF, any side-effect you can imagine is possible. We could achieve (but aren’t limited to):
- Blocking all activities from certain servers.
- Discarding all messages with offensive slurs.
- Discarding all messages in French.
- Duplicating each message 100 times.
- Automatically submitting each message to the FBI.
- Displaying all text in reverse.
- Replacing all text with just the letter “e”.
- Using AI to detect pictures of anime girls and discarding them.
- Using AI to detect pictures of anime girls and discarding everything except them.
- Switching around everyone’s display names and avatars.
- Making an LED blink every time an activity is processed.
- Making a smart home flip its light off and on every time a message containing
#fediblock
is processed.
How do I enable an MRF policy?
At the time of writing, Pleroma ships with 21 MRF policies an admin can configure on their Pleroma server. To do so, they can be added through AdminFE.

Once enabled, an MRF policy can be configured with settings specific to that policy. Each policy is configured differently.

The 21 bundled MRF policies are listed below.
ActivityExpirationPolicy
- Adds expiration to all local Create activitiesAntiFollowbotPolicy
- Prevent followbots from following with a bit of heuristicAntiLinkSpamPolicy
DropPolicy
- Drop and log everything receivedEnsureRePrepended
- Ensure a re: is prepended on replies to a post with a SubjectForceBotUnlistedPolicy
- Remove bot posts from federated timelineHellthreadPolicy
- Block messages with too much mentions (configurable)KeywordPolicy
- Reject or Word-Replace messages with a keyword or regexMediaProxyWarmingPolicy
- Preloads any attachments in the MediaProxy cache by prefetching themMentionPolicy
- Block messages which mention a userNoOpPolicy
- Does nothing (lets the messages go through unmodified)NoPlaceholderTextPolicy
- Ensure no content placeholder is present (such as the dot from mastodon)NormalizeMarkup
- Scrub configured hypertext markupObjectAgePolicy
- Filter activities depending on their ageRejectNonPublic
- Rejects non-public (followers-only, direct) activitiesSimplePolicy
- Filter activities depending on their origin instanceStealEmojiPolicy
- Detect new emojis by their shortcode and steals themSubchainPolicy
- Processes messages through an alternate pipeline when a given message matches certain criteriaTagPolicy
- Apply policies based on user tagsUserAllowListPolicy
- Accept-list of users from specified instancesVocabularyPolicy
- Filter messages which belong to certain activity vocabularies
How it works (in code)
So how does this work? In short, an MRF policy defines a function like def filter(object)
, which gets called by Pleroma's pipeline for every object that goes in or out. The simplest example is Pleroma's NoOp policy, which does nothing:
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
@moduledoc "Does nothing (lets the messages go through unmodified)"
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
def filter(object) do
{:ok, object}
end
@impl true
def describe, do: {:ok, %{}}
end
To do more, you could add some more logic, perhaps something like this:
defmodule AnimeMRF do
@moduledoc "Filters out messages containing anime girls."
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
def filter(object) do
if has_anime_girl(object) do
{:reject, "Sorry, no anime girls allowed."}
else
{:ok, object}
end
end
defp has_anime_girl(object) do
# TODO: Some artificial intelligence on the image...
end
@impl true
def describe, do: {:ok, %{}}
end
It helps to know what an Activity looks like. In ActivityPub, a post (aka "Note") looks something like this:
{
"type": "Create",
"id": "https://gleasonator.com/objects/1",
"actor": "https://gleasonator.com/users/alex",
"object": {
"type": "Note",
"attributedTo": "https://gleasonator.com/users/alex",
"content": "Soapbox FE v1.1 was released today! 🎉 This comes after a lot of hard work.",
"attachment": [
"https://media.gleasonator.com/uploads/soapbox.jpg"
],
"id": "https://gleasonator.com/objects/2",
"published": "2020-10-05T17:25:00Z",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://gleasonator.com/users/alex/followers"
]
},
"published": "2020-10-05T17:25:00Z",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://gleasonator.com/users/alex/followers"
]
}
With this knowledge, it's possible to do pattern matching in Elixir to easily filter images with attachments. This helps us only check posts that have attachments, and let all else pass through.
defmodule AnimeMRF do
@moduledoc "Filters out messages containing anime girls."
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
def filter(object) do
if has_anime_girl(object) do
{:reject, "Sorry, no anime girls allowed."}
else
{:ok, object}
end
end
# Only gets called if there's an attachment
defp has_anime_girl(%{"object" => %{"attachment" => attachment}}) do
# Call a library to do an AI check on the image
AnimeChecker.is_anime_girl(attachment)
end
# If there's no attachment, there's no anime girl
defp has_anime_girl(_), do: false
@impl true
def describe, do: {:ok, %{}}
end
MRFs from the community
Due to its power and flexibility, some users have made custom MRF policies not found in Pleroma itself. These modules can be added to Pleroma by copying them in and configuring them manually.
BlockPolicy
by nik - Notify local users upon remote block.NotifyLocalUsersPolicy
by a1batross - Notifies all local users by configurable tag in subject field (only for admins)- ... a lot more that I don't have links for (send them my way?)
To find out more about MRF, follow the links below: