Posting to Pleroma inbox

Hi, is it possible to POST e.g. a note to Pleroma user’s inbox using C2S API? I am not sure from reading the AP specs and even not sure after investigating the Pleroma code.

I’m trying to do POST like this:

curl --location --request POST 'https://greenish.red/users/nokton/inbox' \
--header 'Content-Type: application/activity+json' \
--header 'Authorization: Basic ...' \
--data-raw '{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Create",
 "to": ["https://greenish.red/users/nokton"],
 "actor": "https://greenish.red/users/nokton",
 "object": {"type": "Note",
            "attributedTo": "https://greenish.red/users/nokton",
            "to": ["https://greenish.red/users/nokton"],
            "content": "do I need id??"}}'

I guess the problem is there is no HTTP signature ({valid_signature: true} from the Pleroma source code), but I was hoping I could avoid it when using credentials (Basic auth).

So is there a way to somehow easily POST to user inbox using C2S AP? I’m sure @lanodan will know :slight_smile:

HTTP Signatures are for servers. Also the endpoint you want to use is /users/:nickname/outbox.

Here is a working setup with curl that I use for some real-life tests:

In create_note.json:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public",
    "https://queer.hacktivis.me/users/lanodan/followers"
  ],
  "actor": "https://queer.hacktivis.me/users/lanodan",
  "object": {
    "attributedTo": "https://queer.hacktivis.me/users/lanodan",
    "type": "Note",
    "to": [
      "https://www.w3.org/ns/activitystreams#Public",
      "https://queer.hacktivis.me/users/lanodan/followers"
    ],
    "content": "Dayum~"
  }
}

curl -v -XPOST -H 'Content-Type: application/activity+json' -d @create_note.json -u "lanodan:$(pass show queer.hacktivis.me/lanodan)" https://queer.hacktivis.me/users/lanodan/outbox | tee create_note_response.json

Notes:

  • I am using HTTP Basic Auth via -u here, and OAuth is also possible, just a bit harder to do (tested it via curl scripts as well).
  • I grab my password via my pass(1) storage you can also consider it the same as $REDACTED
2 Likes

Thank you.

First - I’m not sure we have the same context of the messages. I tried posting to my outbox and it worked. I want to post to somebody else’s inbox. Like I would want to send you a (private) message, not post a note to my followers. From the example exchange at https://www.w3.org/TR/activitypub/#Overview - I am Allyssa and want to send a note to Ben’s Inbox. From the LDN point of view (https://www.w3.org/TR/ldn/#overview), I, as a Sender, want to POST an AP message to the Receiver’s Inbox.

Or do I understand it incorrectly and when using C2S AP, I always have to send messages using my outbox? But that would mean I could only send messages to interlinked servers. From what I understand, LDN is designed so I can discover anybody’s Inbox (if he has made it public) and POST a message to it. I assume AP is building on top of this, so the flow is the same, just adding AP constraints/semantics (message format, Actors specification, etc.).

Yeah, you always send messages to your own outbox, quite like how with SMTP your client send messages to your own SMTP server, not directly the one of your target. This is why there is addressing like to/cc.

Yeah, you always send messages to your own outbox

Ok, and is this Pleroma-specific, or general AP? :slight_smile: Like if I wouldn’t use Pleroma and I was building a new AP client (only C2S) application, how would I send a message to some other AP implementation using C2S API? Would I have to implement also a server that would use federation with the other implementations of AP and in my client POST to my Outbox on my server in order to send message to the other person on different implementation?

I’d expect, like with LDN, just POST to the other person’s Inbox should work…

Only servers post to inboxes. To send a message to another user, you send it to your outbox with the to field referencing the target

If the C2S actor holds its own keys and does its own deliveries (meaning you only use C2S to your own server to check your inbox), then yes, it would work like this.

However, in most cases, the AP server does key escrow on your behalf, so you POST to your own outbox with an OAuth token or equivalent, and it signs the message and forwards it on to the target’s inbox on your behalf since it holds your signing key.

1 Like

If the C2S actor holds its own keys and does its own deliveries (meaning you only use C2S to your own server to check your inbox), then yes, it would work like this.

I think this phrasing it a little misleading. C2S is a term that’s used by the specification to mean a client communicating with its authoritative server using the outbox endpoint. If you’re communicating with a different server or posting to a user’s inbox, then you’re doing S2S. It’s as simple as that.

If you were building a new AP client (only c2s), and you wanted to send a message to a different AP server, then you would need to find an s2s server to pair your client with. it’s as simple as that. POST /outbox is c2s, POST /inbox is s2s. (and conversely GET /inbox is c2s, or you might think of it as “s2c”). That’s the fundamental “division of labor” between an ActivityPub server and an ActivityPub client. Clients handle authoring the message, and they pass it off to servers for delivery.

there’s a couple of reasons for this, like the authentication & authorization concerns kaniini mentioned, but the most fundamental, IMO, is that ActivityPub generally expects the vast majority of messages you
pass between two servers to be publicly accessible somewhere on the open web (at their id property, specifically), not just transient payloads. It doesn’t make any sense for a client to “go around” its server and send messages directly when that would mean that any attempts to GET the activity later would just fail, since the authoritative server never knew about them in the first place.

LDN, as I understand it, does not have the concept of separate c2s and s2s APIs

I don’t know what this means. Yes, you could only send messages to servers that are accessible over ActivityPub, just like with LDN you can only send messages to Inboxes that you know about. Maybe there’s some nuance here that i’m missing?

1 Like

Yes I don’t really have a good way of phrasing it. It would be a “hybrid” approach basically in this situation, where your client acts both as a client (for checking an inbox in a more reliable location) while sending messages directly without using that server.

If anything is still unclear feel free to describe your use case.

I was building a new AP client (only C2S) application, how would I send a message to some other AP implementation using C2S API?

Do you want to use ActivityPub in a peer to peer context without servers?


For me the graphics and this recap helped:

Hey nice, so just as a recap:

  • You can POST to someone’s inbox to send them a message (server-to-server / federation only… this is federation!)
  • You can GET from your inbox to read your latest messages (client-to-server; this is like reading your social network stream)
  • You can POST to your outbox to send messages to the world (client-to-server)
  • You can GET from someone’s outbox to see what messages they’ve posted (or at least the ones you’re authorized to see). (client-to-server and/or server-to-server)

Of course, if that last one (GET’ing from someone’s outbox) was the only way to see what people have sent, this wouldn’t be a very efficient federation protocol! Indeed, federation happens usually by servers posting messages sent by actors to actors on other servers’ inboxes.

Hm, found this example today and tried to post this activity:

{
@context”: “https://www.w3.org/ns/activitystreams”,
“type”: “Create”,
“actor”: “https://social.liberta.vip/users/”,
“object”: {
“type”: “Note”,
“content”: “Test”
}
}

to a pleroma instane.

Response:
“Unhandled activity type”

That should also be possibel ??

curl --location --request POST ‘https://social.liberta.vip/users/r/outbox’
–header ‘Content-Type: application/activity+json’
–header ‘Authorization: Basic xxxxxxxxxx’
–header ‘Cookie: __Host…’
–data-raw ‘{
@context”: “https://www.w3.org/ns/activitystreams”,
“type”: “Note”,
“content”: “Test”
}’

Response:
“Unhandled activity type”

Does anybody see my problem ?
Thanks

The second snippet isn’t going to work, you’re sending in an object and
at the moment pleroma doesn’t auto-wraps them into Creates.
And it seems quite weird that you’re using Auth Basic and cookies,
shouldn’t cause this error message but not entirely ruling it out.

For the first one it’s weird to have this message, another one wouldn’t have
surprised me as it’s missing addressing(to or cc).

what is the correct address to use for “to” ? e.g. https://chaos.social/@naturzukunft

It must be the id of the actor in that case, e.g. https://chaos.social/users/naturzukunft. The URI you are using is instead the actor’s url property.

curl --location --request POST ‘https://social.liberta.vip/users/rdf4j7340/outbox
–header ‘Content-Type: application/activity+json’
–header ‘profile: https://www.w3.org/ns/activitystreams"’
–header ‘Authorization: Basic xxxxxxxxxxxxxx’
–data-raw ‘{
@context”: “https://www.w3.org/ns/activitystreams”,
“type”: “Create”,
“actor”: “https://social.liberta.vip/users/rdf4j7340”,
“to”: “https://chaos.social/users/naturzukunft”,
“object”: {
“type”: “Note”,
“content”: “Test”
}
}’

-> Internal Server Error

;-(

Pleroma probably wants to to be a list.

curl --location --request POST ‘https://social.liberta.vip/users/rdf4j7340/outbox
–header ‘Content-Type: application/activity+json’
–header ‘profile: https://www.w3.org/ns/activitystreams"’
–header ‘Authorization: Basic cmRmNGo3MzQwOkhhbGJlR2xhczI4Iw==’
–data-raw ‘{
@context”: “https://www.w3.org/ns/activitystreams”,
“type”: “Create”,
“actor”: “https://social.liberta.vip/users/rdf4j7340”,
“to”: [
https://chaos.social/users/naturzukunft
],
“object”: {
“type”: “Note”,
“content”: “Test”
}
}’

–>

{
“errors”: {
“detail”: “Internal server error”
}
}

Probably should also have an empty list for cc.

{
@context”: “https://www.w3.org/ns/activitystreams”,
“type”: “Create”,
“actor”: “https://social.liberta.vip/users/rdf4j7340”,
“to”: [
https://chaos.social/users/naturzukunft
],
“cc”: [],
“object”: {
“type”: “Note”,
“content”: “Test”
}
}

-> Internal server error