How Mastodon was implemented part1

This is just a memo and I will modify it later for better understanding.

What happened when we follow a remote user

code

first find local, otherwise find remote in the current instance db

code

not found, than use webfinger

using username@host can help find out the user information provided by other mastondon instances

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ curl https://client.webfinger.net/lookup?resource=gyorou_bocchi%40mustodon.bocchi.tokyo

# response
{
"subject": "acct:gyorou_bocchi@mustodon.bocchi.tokyo",
"aliases": [
"https://mustodon.bocchi.tokyo/@gyorou_bocchi"
],
"links": [
{
"rel": "https://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://mustodon.bocchi.tokyo/@gyorou_bocchi"
},
{
"rel": "https://schemas.google.com/g/2010#updates-from",
"type": "application/atom+xml",
"href": "https://mustodon.bocchi.tokyo/users/gyorou_bocchi.atom"
},
{
"rel": "salmon",
"href": "https://mustodon.bocchi.tokyo/api/salmon/3"
},
{
"rel": "magic-public-key",
"href": "data:application/magic-public-key,RSA.uSUCVT8zv3GRmdR9mMFvPTif_9geHjYQkYPIgHnyzIhOJcagtZUCWh0fAd_zXYjxoLE7NVYnA5IocvUIHLqZblPWujjaGbtCWRW54GpDGaXBiJPwienneBrGhSwkWEWwFjsNqPzvZifXKKSnsXE_Ryf6acfNP-Xmi5mRGod75j1cCWC291PbXfoIT0Xt8wYB9VMGCOgUiVvKZx1vhS39C7jPuV37WNr-Y0y7mGI4zZtSrRAbM9GHo1B32CyhkgXgXnEJw2miomeYQcWMYeQXg4eDLCq-JNqv7ppIQZwBumlj1Zn0zTe0ZrDOWxyRiU_6JZDbcduUjY0PJ-O5gtnd1Q==.AQAB"
},
{
"rel": "https://ostatus.org/schema/1.0/subscribe"
}
]
}

mustodon will create an account object for each of these fields.

1
2
irb(main):001:0> Account
=> Account(id: integer, username: string, domain: string, secret: string, private_key: text, public_key: text, remote_url: string, salmon_url: string, hub_url: string, created_at: datetime, updated_at: datetime, note: text, display_name: string, uri: string, url: string, avatar_file_name: string, avatar_content_type: string, avatar_file_size: integer, avatar_updated_at: datetime, header_file_name: string, header_content_type: string, header_file_size: integer, header_updated_at: datetime, avatar_remote_url: string, subscription_expires_at: datetime, silenced: boolean, suspended: boolean, locked: boolean, header_remote_url: string, statuses_count: integer, followers_count: integer, following_count: integer)

In the above example, "https://mustodon.bocchi.tokyo/users/gyorou_bocchi.atom" will be the remote_url, which we can fetch the latest status of the remote user, and salmon_url will be https://mustodon.bocchi.tokyo/api/salmon/3, which we use salmon protocol to reply the status of the remote user.

hub_url is the pubsubhubbub server endpoint, we post to it to register our webhook.

the webhook callback url will be saved in the Subscription model.

1
2
3
irb(main):003:0> Subscription.first
Subscription Load (15.5ms) SELECT "subscriptions".* FROM "subscriptions" ORDER BY "subscriptions"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Subscription id: 1, callback_url: "https://abysswalking.net/api/subscriptions/2", secret: nil, expires_at: "2017-04-14 18:16:29", confirmed: false, account_id: 3, created_at: "2017-04-07 18:16:29", updated_at: "2017-04-07 18:16:29", last_successful_delivery_at: nil>

whenever we push a new status. we post the callback_url for each of our followers to notify them the update.
And the callback_url related action will save the new status received into a status object in the instance db.

1
2
3
4
irb(main):005:0> Status.first
Status Load (5.0ms) SELECT "statuses".* FROM "statuses" ORDER BY id desc LIMIT $1 [["LIMIT", 1]]
=> #<Status id: 4668, uri: "tag:oransns.com,2017-04-18:objectId=144679:objectT...", account_id: 211, text: "<p><span class=\"h-card\"><a href=\"https://mastodon....", created_at: "2017-04-18 15:14:14", updated_at: "2017-04-18 15:14:17", in_reply_to_id: 4661, reblog_of_id: nil, url: "https://oransns.com/users/okome/updates/8182", sensitive: false, visibility: "public", in_reply_to_account_id: 238, application_id: nil, spoiler_text: "", reply: true, favourites_count: 0, reblogs_count: 0>
irb(main):006:0>