Distinguish between posts and direct messages


ActivityPub seems to be mainly focused on posts, but some applications also make it possible to send direct messages, which I also want to make possible in my own application.

Unfortunately posts and direct messages have the same object type (Note).

So how do you mark a Note as a direct message?

I found this thread (Context vs conversation) where some differeneces between softwares are shown, that use the context or conversation property to mark a Note as direct message. I looked up how Mastodon marks direct messages and they use the property visibility with the value "direct".

But neither conversation nor visibility seems to be official properties of ActivtyPub or ActivityStreams objects. At least I haven’t found anything about them at ActivityPub and Activity Vocabulary. Or did I miss a source?

context is an official ActivityStreams object property, but how can my application parse its value to distinguish if it is a post or a direct message?

Pleroma has an extension which adds the ChatMessage type for this purpose. We also use it in Lemmy as its very convenient to avoid wrong parsing.


1 Like

Shouldn’t that be…

"type": ["Note", "lp:ChatMessage"]

… to be more conformant to AS spec?

(In general Re:Litepub, Zap and others. I think we should dedicate to an effort to aggregate all the extensions now in use)


Thank you for your replies.

I feared that it is not possible without extensions, because they are only interoperable if many applications use them.

And I also understood AS like it is only allowed to extend existing types. And because it is an extension of note, it should be written like aschrijver wrote. So applications that don’t understand http://litepub.social/ns#ChatMessage could still parse the message.

On https://litepub.social/ its is written:

Note: LitePub has been stalled since 2019-06-29 and is incomplete as a specification

So I am not sure if I should use an extension from an incomplete specification from a stalled project. Or should I ignore LitePub’s state, because this extension is used in healthy projects?

1 Like

Mastodon does not use a visibility property in its ActivityPub representation (it uses it internally and in its client-to-server REST API). For Mastodon, a Direct Message is a message that is addressed (to) specific users and does not address (in to or cc) the public collection (https://www.w3.org/ns/activitystreams#Public), or the poster’s followers collection.


With Mastodon’s plans for encryption and the differences that exist in how messaging is handled between apps, now would be a great time to start fleshing out a FEP for the mechanism, to evolve along with this. It would be fabulous if someone of @mastodon or @pleroma would dedicate to help move that along?

As I recall there were already several projects using a directMessage property in various namespaces, so we just used that. Boolean.

So it looks like we have at least 3, potentially 6, and who knows maybe more different ways to do the same thing? :thinking:

This seems to be the most sensible approach.
I’m doing this as well, looking for “Public” and “followers” addressing, considering everything else direct message. No additional properties required.

Thanks for your clarification @Claire. I looked at the wrong place, my fault. :see_no_evil:

This seems to work in most cases, but there are some special cases where I am not sure if it works:

  • Replys on private posts
    When Alice replies to a private post from Bob, she can’t specify her followers as recipients (to), since they won’t necessarily be able to see Bob’s message (or can/should she?). So would this reply be interpreted as direct message in Mastodon? Or would it be interpreted as normal post, because it has Bob’s follower collection as recipients? Or does Mastodon look up if the Note which is replied to is a direct message?

  • posting on the private blog of someone else
    I know this is not supported in all AP-applications, but it could cause similar problems as the above case.

  • Chat rooms
    Depending on the answer to the first case, this wouldn’t be a problem. But in chat rooms, where new users should be allowed to read old messages, the recipient of every message is the chat rooms follower collection (or at least I think this would be the right way to do it, not sure how other applications implement this). So there is a followers collection as recipient, but the note should still be interpreted as direct message.

@Claire I won’t disagree with how Mastodon determines a DM, but I believe there’s a bug in how they determine a followers-only post; which turns many of our DMs into the wrong privacy icon on that platform.

I believe that the mere presence of a ‘cc’ field causes what would otherwise be a DM to be displayed as followers-only on Mastodon and I believe this is a bug. Tested with a DM with an audience of 2 (plus the originator so 3 total), where one of the DM recipients is in the cc. Mastodon displays this as followers-only even though no followers collections were addressed.

I’m also curious what folks are doing to determine that an addressed entity is in fact a followers collection. Hope you’re not basing it on the existence of “followers” in the id as there is no guarantee that a server will use that pathname component. That’s why we ended up using the ‘directMessage’ attribute as a hint so as to avoid the requirement to fetch every object and check its type. Can’t always do that on our platforms because we implement permissions and some fetches are going to be refused if you lack them.

The actor object has a followers property (at least at Example 1 ActivityPub). So one can fetch this object and check if its followers property is included in the to property of the note.
I don’t know if anyone does it that way, but it would solve the problem of different naming conventions.

So one can fetch this object and check if its followers property is included

Certainly. But we can also include lists/circles/aspects and these are also addressable collections, but they aren’t going to show up in your list of actor collections and these could also be restricted from casual access.

All I’m saying is that without a hint by the sending server, determining with absolute precision that a particular activity is a direct message is an unsolvable problem. At best we can only examine the recipients and ultimately make a wild guess.


Indeed, what I’ve written here is a simplified version of what we’re doing: if the audience of a message that would otherwise be considered a DM includes actors that are not also mentioned (have a Mention entry in the tag property of the object), it will be considered as having “limited” visibility (presented to the client as followers-only). This was a way to have basic support for supporting circles-like features from other software. I assume that it is what is happening there.

We’re checking whether the collection identified by the followers attribute of the sender actor is included in the audience of the message.

I proposed doing that to disambiguate some “limited” VS “direct” cases in Mastodon, but it didn’t get any traction:

Emphasis mine:

I agree with this. Wouldn’t the best way be to say that a DirectMessage is a different type of object than a public post? Both are based on as:Note so the type property as an array is used to indicate that, as mentioned above.

Besides that broadly supporting type as an array as the standard indicates would be an advantage for future extensions to be built on the Fediverse, it would leave as:Note the lowest common denominator that actually represents… a note. Not a bag of properties that must be parsed to find out all the types it does represent.

Having DM’s be a different type would also more clearly indicate that they will have different behavior, and it will be easier to separately evolve what constitutes a Direct Message. When I didn’t know how DM’s worked in Mastodon I found some things to be very unintuitive and DM’ing a ‘handle with care’ feature:

  • Mention someone in a DM adds them to the conversation (“Warning, @FedizenXYZ is a troll” … oops they’re now DM’ed that).
  • If someone else rigorously cleans up their DM inbox, they also clean up my Inbox while I want to archive these msgs.

I totally agree with this.
The current Mastodon solution is good for some cases, but has to be handled with care (like aschrijver wrote).
I can also add the following case to aschrijvers list:

  • If you reply to a message and remove the mention, it won’t be send to the recipient. (this was not intuitive for me, I thought “why should I mention the recipient in a 1to1 DM?” so I deleted the mention (I know Mastodon warns you about this, but other applications don’t)).

So there are two solutions:

  1. A new property for Note that marks it as DM or
  2. A new type of object for DMs

If this solution is already used, it sounds like a good idea to make this the “offical way” and write an FEP for this.

But I also see the advantages of defining a new type. Article and Note are different types with the same properties, so why not make ChatMessage a new type? The expected behavior of ChatMessages is so different from posts, that an own type would be justified.

To me a property-only approach to indicate we deal with something different, feels like bolting on all kinds of things on top of something that in nature represents a simple note, a blurb of short text. It is sort of hijacking the Note and say: this is a Microblogging post (or status, or… whatever app-specific names are used). There are tons more usages of notes, and it is really just a base type, a social primitive that ActivityStreams hands us to build on top of. That it has gained so much meaning in a Microblogging context (domain-specific) is mostly historical reasons.

The ‘Additional Type’ construct offered by type being an array with the base type first (to retain basic compatibility) and other types to indicate specializations makes sense to me. With it you can say: "This is a as:Note AND a forge:Comment AND a security:EncryptedMessage". Then the properties that may or may not occur in this object come from 3 clearly defined AS/AP vocabularies or extensions thereof, and you find the documentation how they work as clear specs in some well-defined locations.

The only issue I have with this being an array element of ‘type’ is that very few projects support type as an array. I’m not even certain that more than about 1/3 of fediverse projects recognise url as an array 4-5 years on. But sure, we could do a sub-class instead of adding a boolean property. Or either, or both. Since there’s already a large installed base using a boolean flag, we’ll have to keep that for backward compatibility in any case.

I do have an issue designating these as a “ChatMessage”. It’s just a private conversation between two (or more) people. “Chat” refers more to a visual presentation of a conversation and perhaps a protocol selector than a basic information type. If it is to be an array, “directMessage” would still be the best descriptor for those of us who just want to know the intended privacy scope, and not force us to deal with any other implications of ChatMessage like STUN/TURN/ICE configuration and online presence and offline storage. But I’m ok if you want to have type [ Note, directMessage, ChatMessage, forge:Message ] if you want to sub-class the best message handler for your requirements.

Yes indeed, that is something that is essential. Using type as array might be part of a new FEP, indicating desired way for the future and based on it we can continue to encourage all projects to migrate to it gradually, until other mechanisms can be safely deprecated.

Totally agree. Choosing a name that is semantically a best-fit is important and I think “DirectMessage” comes way closer. Do you have a reason why you don’t start it with a uppercase character? I feel starting with a capital for types as a convention (upper camel case) makes it easier to distinguish from properties with lowercase starting char (lower camel case).

I don’t have a preference. In the case of the boolean attribute, that was the existing convention.

1 Like