what Follow is ideally like in theory

  • you send a Follow
  • they send you an Accept Follow as a courtesy, and Add you to their followers
  • you receive the Accept Follow and take it as a signal to Add them to your following (perhaps you can even show the Accept Follow as proof of this, if it was signed!)

what Follow is actually like in practice

sharedInbox exists, and known follow relationships are used to calculate visibility on the receiving end (instead of relying on direct delivery). so now that Accept Follow actually matters… it’s not just a courtesy anymore.

because of this, Follows are realistically transient requests to update state. it is therefore enough to keep track of local state, then mutate state based on activities.

removing followers

ideally, it should be possible to remove a follower without notifying them. notifying them could still be done as a courtesy, perhaps with Remove targeting your followers collection.

in practice, mastodon first decided that they would send Undo Accept Follow, to undo the original acceptance of the follow request. this was soon after deemed to be too complex, and so mastodon settled upon sending a Reject Follow at any point after the original Accept Follow, which would be handled the same regardless of whether the Follow was ever accepted or not. the side effects of a Reject Follow in mastodon protocol are to destroy the follow request or follow relationship, with no regard to which one it actually is.

business logic

if you receive an Accept/Reject Follow, check ONLY for the following:

  • actor
  • type == Accept/Reject
  • object.actor == (you)
  • object.type == Follow
  • object.object == actor

in case of an Accept Follow, check that you have a local pending follow request. if you do not have a pending follow, then DO NOT process an incoming Accept Follow.

in case of a Reject Follow at any time, destroy any existing follow relationship, regardless of whether it was previously Accepted or not. note that you may also receive an Undo Accept Follow by some implementations. this is discouraged but should be handled as well

in either case, this is an idempotent action. the only id that matters is the outermost activity. if object is inlined, you don’t need to check that object.id is local. the above is enough information to handle the activity.

resyncing follow state

if you receive a Follow from someone you already Accepted, then send another Accept Follow to remind them. in practice, this usually means a state desync or database loss.

what happens if you ignore this guidance

state desync, you think you removed a follower but you didn’t. you send a post to sharedInbox and the remote server might end up showing it to someone you thought you removed successfully! and indeed, on your end, they were removed successfully. but the remote server doesn’t know or care what your local state is – it only cares for its own state.

https://github.com/misskey-dev/misskey/issues/9250