Like Activity

Hi. I have two questions about the Like activity. First, an example Like for reference:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Like",
  "id": "https://mymath.rocks/activitypub/helge/like-c62dab9f-34fb-4940-bb72-98e8872f96be",
  "actor": "https://mymath.rocks/activitypub/helge",
  "content": "🐼",
  "object": "https://i.calckey.cloud/notes/9ajhcxg0lu",
  "to": ["https://i.calckey.cloud/users/99is5hpneh"]
}

  1. As far as I can tell, Mastodon sends Like activities without a to property. Is that valid behavior? My understanding of delivery in the spec:

Targets for delivery are determined by checking the ActivityStreams audience targeting; namely, the to, bto, cc, bcc, and audience fields of the activity.

suggests that Mastodon’s Like shouldn’t be delivered across the network. Can anybody confirm that Mastodon has indeed invalid behavior?

  1. I have used the CalcKey convention for an emote, here :cow:. Is the use for the content property for the emote type of Likes documented somewhere? I’ve documented it now here.

Thanks,
Helge

I see the same thing. I also see the same for Undo of a Like. Other activities like Create, Delete, Announce do have a “to” property.

I don’t know if it’s non-compliant.

From the spec (emphasis mine):

What to do when there are no recipients specified is not defined, however it’s recommended that if no recipients are specified the object remains completely private and access controls restrict the access to object.

It’s not clear to me what completely private means in this context. In the Like scenario, the object has already been created and might be public. I wouldn’t think the Like with no recipient would make it private.

I’ve been experimenting with writing some server code and it’s definitely challenging. Mastodon includes the audience information in both the Create activity and the object. For Likes, I dereference the object URI to get the original recipient information. I suspect that other AP-based servers will have different behavior.

targets for delivery is a C2S thing. if you receive an activity in your private inbox then it was obviously sent to you (this is a tautology). the only time to/cc would be necessary is if you delivered to sharedInbox instead, as then the receiving server would have to figure out visibility on its own.

the use of content on Like is a misskey extension and is used for emoji reactions. they did this so that emoji reactions will fall back to a Like for non-misskey users

The quotes above are from section 7.1 Delivery, which is in the “Server to Server Interactions” chapter.

let me rephrase: “targets for delivery” is up to the delivering server, not the receiving servers.

1 Like

Let me ask another question:

What is the reason for delivering an Activity without a to (or equivalent targeting field)?

Requiring the to field would make the targeting more transparent. Also it would allow people to valid “Should I really get this message?”

Consider the case of bto and bcc. Those properties are specified to be stripped from the activity prior to delivery. A Like activity that’s authored bto: "your actor" would appear on delivery exactly like these activities appear to you now.

to, cc, bto, and bcc are primarily instructions to the delivering server about which inboxes to send the activity to. Secondarily, they’re instructions for how to treat activities in the sharedInbox. Third and least their hints for the receiving client about how to display the activity to the user (e.g. “why did I receive this?”). For activities not delivered to sharedInbox where the reason for receiving them is patently obvious, it seems like nobody really felt the need to include them.

That said, I would like to see the presumption of privacy for these Like activities upheld a little bit more within Mastodon, and then use as:Public to signal which like activities are okay to display publicly e.g. as a list. From that perspective these activities (if Mastodon does generate them without any kind of “public” indicator at all) are suboptimal

1 Like

What is “Forwarding from Inbox” (section 7.1.2)? There’s a separate section about shared inbox delivery, so I assume this refers to local forwarding from a private inbox. It makes sense that an activity sent to a private inbox would be for the owner of the inbox. But is it only for that recipient (given the forwarding)?

In any case, Mastodon includes the “to” property when posting to a private inbox in some cases and not in others. My guess is that it’s just an artifact of the implementation that converts their internal data to AP representations. If not, it would be interesting to know the rationale for it.

Section 7.1.2 (Inbox Forwarding) is about when Actor A receives an activity from Actor B and forwards it along to related actors C, D, E, etc. For example, if Actor B sends a reply to Actor A’s post, and addresses it "to": ["actor A's followers collection"], then Actor A can “forward” that activity to their followers, according to the audience targeting.

2 Likes

Thanks nightpool. The argument with bcc, bto makes sense.

That said, I would like to see the presumption of privacy for these Like activities upheld a little bit more within Mastodon, and then use as:Public to signal which like activities are okay to display publicly e.g. as a list. From that perspective these activities (if Mastodon does generate them without any kind of “public” indicator at all) are suboptimal

This means that I should add a cc entry with as:Public to symbolize that it is ok to show the information that I liked something. I’ve included it in the sample Like Activity below. A privacy best practice implementation would display at most:

  • With the cc: Show the author a notification, I liked his content
    • and increment the like count
    • and display my name in the list of likers for the public
    • and include my activity in the liked collection of the original activity for the public
  • Without the cc: Show the author a notification, I liked his content
    • and increment the like count
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Like",
  "id": "https://mymath.rocks/activitypub/helge/like-c62dab9f-34fb-4940-bb72-98e8872f96be",
  "actor": "https://mymath.rocks/activitypub/helge",
  "content": "🐼",
  "object": "https://i.calckey.cloud/notes/9ajhcxg0lu",
  "to": ["https://i.calckey.cloud/users/99is5hpneh"],
  "cc": ["https://www.w3.org/ns/activitystreams#Public"]
}

Final comment: I fought with myself with the formulations above, as I don’t think showing like counts can be considered best practice. They are hard to federate across different instances, and lead to unnecessary gamification.

Final final comment: I never expected a Like Activity to be THIS complicated before starting to implement bovine. I’ll try to make this type of choices somewhat transparent in it. But that requires more thinking.

Edit to record it somewhere:

A like from Friendica, I tried to censor a bunch of stuff.

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1",
    {
      "Hashtag": "as:Hashtag",
      "PropertyValue": "schema:PropertyValue",
      "conversation": "ostatus:conversation",
      "dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
      "diaspora": "https://diasporafoundation.org/ns/",
      "directMessage": "litepub:directMessage",
      "discoverable": "toot:discoverable",
      "featured": {
        "@id": "toot:featured",
        "@type": "@id"
      },
      "litepub": "http://litepub.social/ns#",
      "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
      "quoteUrl": "as:quoteUrl",
      "schema": "http://schema.org#",
      "sensitive": "as:sensitive",
      "toot": "http://joinmastodon.org/ns#",
      "value": "schema:value",
      "vcard": "http://www.w3.org/2006/vcard/ns#"
    }
  ],
  "actor": "https://venera.social/profile/manolo_ssa",
  "cc": [
    "https://social.network.europa.eu/......",
    "https://mymath.rocks/activitypub/helge/followers",
    "https://venera.social/followers/......"
  ],
  "diaspora:guid": ........",
  "diaspora:like": "{\"author\":\"......@venera.social\",\"guid\":\"......\",\"parent_guid\":\".......\",\"parent_type\":\"Comment\",\"positive\":\"true\",\"author_signature\":\".......\"}",
  "id": "https://venera.social/.......",
  "instrument": {
    "name": "Friendica 'Giant Rhubarb' 2023.01-1502",
    "type": "Service",
    "url": "https://venera.social"
  },
  "object": "https://mymath.rocks/activitypub/helge/42581f32-cc7b-419e-b3a5-7b4ea9f1b1b2",
  "published": "2023-01-29T08:52:18Z",
  "signature": {
    "created": "2023-01-29T08:52:29Z",
    "creator": "https://venera.social/.....#main-key",
    "nonce": "6.....",
    "signatureValue": ".......",
    "type": "RsaSignature2017"
  },
  "to": [
    "https://mymath.rocks/activitypub/helge",
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "type": "Like"
}

agreed, and this argument can be extended to showing any counts

idk if addressing is really necessary (although it can’t really hurt?)

the main thing you’d want in this situation is a proof or guarantee that the Like activity was actually/verifiably sent by the person you’re claiming it was sent from. if i fetch the likes collection on a post then nothing is stopping that server from serving up a collection containing whatever it wants. if the Like is publicly fetchable, then you can fetch the id given to you
 if the Like is not fetchable then it probably needs a signature on it (previously done with LD Signatures, although see FEP-8b32 for a more modern take on that)

basically something like this, imagine three activities:

  1. Create
  2. Like
  3. Like
id: example.com/posts/1/likes
type: Collection
items: [
  "example.com/activities/2",
  "example.com/activities/3",
  # and so on, you get the idea
]

you can try to fetch activities 2 and 3 and you may get something or you may get nothing. if you got something then you can show it. if you got nothing then you can ignore it or consider it a “private like” or whatever

Bovine looks interesting. I’m doing some experimentation with writing an AP server too.

After reviewing the parts of the spec related to the Liked collection (vs Likes), in one place it says:

Section 5.5 Liked Collection
Every actor MAY have a liked collection. This is a list of every object from all of the actor’s Like activities, added as a side effect

If I’m interpreting this correctly, it’s describing the liked collection as containing the objects of the actor’s Like activities.

but then in Section 5.7 the spec says:

liked: Specifically a property of actors. This is a collection of Like activities performed by the actor

A collection of Like activities makes sense to me for likes but not so much for liked (and it appears to be inconsistent with the earlier definition).

I’m assuming one representation has been used in practice and that’s the one an AP implementer should use for compatibility.

1 Like

I’m not aware of an implementation that exposes either, actually. I agree that activities makes more sense.

if i were an implementer that cared for publishing likes or liked, i would lean toward using activities forlikes and objects for liked, as that is what “liked” implies grammatically – “the objects that i liked”. i’m inclined to assume 5.5 is correct and 5.7 is missing a few words (and it should be “a collection of the objects of Like activities performed by the actor”)

I’ve now documented the choices, I made based on this thread.

Thanks again everyone for your input.

It’s months later, but this issue came up again in the context of someone running my Python port of the activitypub.rocks test suite. I was going to enter an issue about it in the AP issue tracker, but someone beat me to it (long ago, in July, 2018). The issue is still open.

Spec contradicts itself when describing what the liked collection should contain · Issue #317 · w3c/activitypub (github.com)

FWIW, the Pubstrate test suite looks for a Note in the actor’s “liked” collection rather than a Like activity (assuming I’m reading the Guile code correctly).