Problems POSTing to Mastodon inbox

Hi,

Following the Instructions in How to implement a basic ActivityPub server, I tried to POST a response to a Mastodon post from my own server.

The thing is: It does not show up, although I am getting a postive HTTP response (202 Accepted) and I am trying to figure out why. Perhaps someone here can give me a hint what is going on.

This is the kind of request I am making:

POST https://mastodon.social/inbox
Host: mastodon.social
Date: Mon, 13 Jul 2020 18:33:11 GMT
Signature: keyId="https://angelo.veltens.org/profile/actor#main-key",headers="(request-target) host date",signature="Q2n8kP3kqfqjP3FVhWwLQ/tqSSnRw5QYuNgodXzLM98fykRmp3eIvQJajOaAKgpMxlbeajFsbS8AjOQqX/mw4dcQnoYMRvlNxEwwmVGXxqIcuhjSqEdDScTCZhoNcI6yDthq0BwR3nUvd5uHhpsQ7nPYw6eOzxVQMXUSx6R95gAVwYh7cILmqE6/wUhduskcblNoFPsQUt1e9mEFYB1QR6QTN5d1MXmOvtV1Ze/VdTMmMpx3GuMklmtbOYtsqyfurBYPBYP2uYTxYCI7LzH8YpaD0uL+AdAV45ArUKvH8o6nhV2ZcPVUiuBrS5XVPMLdmgB2QRV85K9SE4KHlJWutQ=="

{
  "@context": "https://www.w3.org/ns/activitystreams",

  "id": "https://angelo.veltens.org/public/toots/hello-solid#create",
  "type": "Create",
  "actor": "https://angelo.veltens.org/profile/actor",

  "object": {
    "id": "https://angelo.veltens.org/public/toots/hello-solid#note",
    "type": "Note",
    "published": "2020-07-13T20:33:10+0200",
    "attributedTo": "https://angelo.veltens.org/profile/actor",
    "inReplyTo": "https://mastodon.social/@Gargron/100254678717223630",
    "content": "<p>Hello again from Solid</p>",
    "to": "https://www.w3.org/ns/activitystreams#Public"
  }
}

And this is the response:

HTTP/1.1 202 Accepted
Date: Mon, 13 Jul 2020 18:33:13 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Server: Mastodon
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Set-Cookie: _mkra_stck=postgres%3A1594665198.514459; path=/; max-age=10; expires=Mon, 13 Jul 2020 18:33:23 GMT; HttpOnly
Vary: Accept-Encoding
Cache-Control: no-cache
Content-Security-Policy: base-uri 'none'; default-src 'none'; frame-ancestors 'none'; font-src 'self' https://static-cdn.mastodon.social; img-src 'self' https: data: blob: https://static-cdn.mastodon.social; style-src 'self' https://static-cdn.mastodon.social 'nonce-PoTtEgRXnAWfIn/QUIV9qg=='; media-src 'self' https: data: https://static-cdn.mastodon.social; frame-src 'self' https:; manifest-src 'self' https://static-cdn.mastodon.social; connect-src 'self' data: blob: https://static-cdn.mastodon.social https://files.mastodon.social wss://mastodon.social; script-src 'self' https://static-cdn.mastodon.social; child-src 'self' blob: https://static-cdn.mastodon.social; worker-src 'self' blob: https://static-cdn.mastodon.social
X-Request-Id: ddcbf0ea-4af9-4dd9-be57-1d8c6c8265c9
X-Runtime: 0.029216

<Response body is empty>

Response code: 202 (Accepted); Time: 109ms; Content length: 0 bytes

If I mess up the Signature header deliberately I am getting 401 (Unauthorized), so I guess my signature is fine.

What else could be wrong or what reasons exist, that the post does not show up?

Thank you very much for your help.

Have you tried putting the to field onto the Create Activity itself, and not just the object?

It’s a little odd that this is the recommended payload by Mastodon where to is on the object and not the Activity, since it violates expectations in 7.1 (server to server delivery) which also points to 6.1 (client addressing).

Thanks for the tip, but it still does not show up.

I tried adding "to": "https://www.w3.org/ns/activitystreams#Public" to the Create as well as

"to": [
    "https://www.w3.org/ns/activitystreams#Public",
    "https://mastodon.social/users/Gargron"
  ],

I also tried to deliver to https://mastodon.social/users/Gargron/inbox, but nothing works out.

When I run:

curl https://angelo.veltens.org/public/toots/hello-solid#note -H"Accept: application/activity+json"

Or:

curl https://angelo.veltens.org/public/toots/hello-solid#note -H"Accept: application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""

I get:

Cannot serve requested type: application/ld+json

I am not sure if Mastodon is also attempting to fetch the note to verify it exists at that ID, but you definitely want to be serving that created note at that endpoint.

JSON-LD without profile is working:

â–¶ curl https://angelo.veltens.org/public/toots/hello-solid\#note -H"Accept: application/ld+json"
{
  "@context": "https://www.w3.org/ns/activitystreams",

  "id": "https://angelo.veltens.org/public/toots/hello-solid#create",
  "type": "Create",
  "actor": "https://angelo.veltens.org/profile/actor",
  "to": "https://www.w3.org/ns/activitystreams#Public",

  "object": {
    "id": "https://angelo.veltens.org/public/toots/hello-solid#note",
    "type": "Note",
    "published": "2020-07-13T20:33:10+0200",
    "attributedTo": "https://angelo.veltens.org/profile/actor",
    "inReplyTo": "https://mastodon.social/@Gargron/100254678717223630",
    "content": "<p>Hello from Solid</p>",
    "to": "https://www.w3.org/ns/activitystreams#Public"
  }
}

The blogposts states:

For our purposes, we don’t actually need to host this document publicly

This of course might have changed meanwhile, since the article is 2 years old. So I will give it a try. If anyone knows for sure what is needed and what not, let me know please.

so I guess my signature is fine.

Unfortunately probably not.
A known bug in mastodon is that signature errors return 202 !
e.g. https://github.com/snarfed/bridgy-fed/issues/16

I also had this error when I followed the instructions.
What I can offer you is an undocumented 245 lines nodeJS snippet where the folder name says “worked” but without guarantees :wink:

Mastodon does not return 202 if the signature is valid, although we would be well within our rights to per the spec. But for HTTP signatures specifically we always process those synchronously and return a 401 with a specific error reason. Like the spec says, we return 202 if we accept the payload for further asynchronous processing, regardless of whether that processing succeeds or not. (since it’s asynchronous, there’s no way to know)

(If you read the actual issue you linked, you’d know that this is the case, since it concludes by saying "We know now that 202 is okay in terms of signature)

My guess is that the actor profile is being served with the same server, correct? Mastodon needs to be able to fetch your actor’s profile before it can attribute any posts to it, so if neither activity+json or ld+json with profile are responded to then that will cause a failure. I would suggest using mastodon’s “search box” as a debugging tool—put the URL of your actor into the search box, and if a profile comes up successfully, you can check off that step

Right, the actor has the same issue with profile. I did not think about that, because I actually was able to find it on mastodon using angelo@angelo.veltens.org and felt safe.

But you are right, the actual actor URL does not give a result. This probably needs a fix on Node Solid Server.

Ok, I just tried something else: I responded to a post of my account on mastodon.online and it worked!

I wonder why. Might mastodon.social and mastodon.online behave differently?

Or did it work, because mastodon.online already knew about @angelo@angelo.veltens.org, because I did the search there before, but I did not do that on mastodon.social (due lack of account there).

Lucky that it worked, but still confused :confused:

The reason might also be that your IDs aren’t permanent, as in, they contain a #fragment. Posts and their corresponding Create activities are supposed to be resolvable — which means one should be able to send a GET request to the ID URL and get the object back. This can’t be done with an URL that contains a fragment as the fragment is not a part of the HTTP exchange, it’s processed on the client.

Both URIs are permanent. Of course the client will resolve the document URL https://angelo.veltens.org/public/toots/hello-solid but both resources are described in that document.

I guess the problem is, that mastodon.social could not get a JSON-LD representation of the actor, while mastodon.online already had one cached from the webfinger search. Although this still does not explain how it could retrieve the actor during the search.

Feels to me like that’s not a good thing. These are expected to be two distinct URLs — one that returns the Note wrapped in Create, and another one that returns the Note by itself.

See for yourself how Mastodon does it. Try sending a GET request with Accept: application/activity+json to these URLs:
https://mastodon.social/users/Gargron/statuses/104517499488193307/activity
https://mastodon.social/users/Gargron/statuses/104517499488193307

@aveltens yes, this is certainly the reason! Mastodon will not try to (or need to) resolve your account again if it already knows about it

My guess is that the webfinger -> URL code is slightly less strict about the Accept header, because it already knows it’s looking for an AS2 document.

2 Likes

I don’t think this is absolutely required—mastodon had a #activity URL for a long time. Since the network as a whole already uses this same behavior widely for #main-key, I can’t see where the harm would be in allowing it to be used here as well

1 Like

I don’t think fragments offer any information about where to actually find the object unless the implementation walks every node looking for a matching string

That’s not true, and actually ony of the (hard to see :thinking: ) benefits of JSON-LD

Proper framing solves the problem:

https://json-ld.org/playground/#startTab=tab-framed&json-ld=https%3A%2F%2Fangelo.veltens.org%2Fpublic%2Ftoots%2Fhello-solid%23note&frame={"%40context"%3A"https%3A%2F%2Fwww.w3.org%2Fns%2Factivitystreams"%2C"id"%3A"https%3A%2F%2Fangelo.veltens.org%2Fpublic%2Ftoots%2Fhello-solid%23note"}&context={}

That just looks like tree traversal with extra steps

Yes, technically ActivityStreams is JSON-LD, but very few implementations would actually accept any of the alternative forms

All you need to do is keep track, when deserializing, of the different “id” nodes, and build a hashmap to them—or even just keep a single pointer to the id you’re looking for. It’s not going to be a performance bottleneck in most applications.

(Mastodon does not currently do this, but we’re very flexible about whether implementations want to send us the activity or the object, and tend to treat them interchangeably, so i don’t think we’d run into any problems here)

Is this JSON-LD processing something that Mastodon does?

I know go-fed does not do JSON-LD processing at runtime, which means it simply assumes performing a GET request to an IRI will yield an ActivityStreams payload that has that identifier as its root object.