Mastodon & ActivityPub: An Overview

The Fediverse

It's any piece of software that implements the ActivityPub protocol.

Software that makes up the Fediverse

Micro blogging software built around 500 character text posts.

Mastodon alternatives


Misskey (https://misskey.io/)


Pleroma (https://pleroma.social/)


Write Freely (https://writefreely.org/)


Plume (https://joinplu.me/)

What else?


Peertube (https://joinpeertube.org)


Funkwhale (https://funkwhale.audio/)

There's more!


PixelFed (https://pixelfed.org/)


Friendica (https://friendi.ca/)


Nextcloud (https://nextcloud.com/)

An ActivityPub Actor

An ActivityPub Actor


{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Person",
 "id": "https://social.example/alyssa/",
 "name": "Alyssa P. Hacker",
 "preferredUsername": "alyssa",
 "summary": "Lisp enthusiast hailing from MIT",
 "inbox": "https://social.example/alyssa/inbox/",
 "outbox": "https://social.example/alyssa/outbox/",
 "followers": "https://social.example/alyssa/followers/",
 "following": "https://social.example/alyssa/following/",
 "liked": "https://social.example/alyssa/liked/"}
			

Post a message


{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Note",
 "to": ["https://chatty.example/ben/"],
 "attributedTo": "https://social.example/alyssa/",
 "content": "Say, did you finish reading that book I lent you?"}
		

Wrap in a create object


{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Create",
 "id": "https://social.example/alyssa/posts/a29a6843-9feb-4c74-a7f7-081b9c9201d3",
 "to": ["https://chatty.example/ben/"],
 "actor": "https://social.example/alyssa/",
 "object": {"type": "Note",
            "id": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
            "attributedTo": "https://social.example/alyssa/",
            "to": ["https://chatty.example/ben/"],
            "content": "Say, did you finish reading that book I lent you?"}}
		

Get a response


{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Create",
 "id": "https://chatty.example/ben/p/51086",
 "to": ["https://social.example/alyssa/"],
 "actor": "https://chatty.example/ben/",
 "object": {"type": "Note",
            "id": "https://chatty.example/ben/p/51085",
            "attributedTo": "https://chatty.example/ben/",
            "to": ["https://social.example/alyssa/"],
            "inReplyTo": "https://social.example/alyssa/posts/49e2d03d-b53a-4c4c-a95c-94a6abf45a19",
            "content": "

Argh, yeah, sorry, I'll get it back to you tomorrow.

I was reviewing the section on register machines, since it's been a while since I wrote one.

"}}

Alyssa hits 'like'


{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Like",
 "id": "https://social.example/alyssa/posts/5312e10e-5110-42e5-a09b-934882b3ecec",
 "to": ["https://chatty.example/ben/"],
 "actor": "https://social.example/alyssa/",
 "object": "https://chatty.example/ben/p/51086"}
		

New public post


{"@context": "https://www.w3.org/ns/activitystreams",
 "type": "Create",
 "id": "https://social.example/alyssa/posts/9282e9cc-14d0-42b3-a758-d6aeca6c876b",
 "to": ["https://social.example/alyssa/followers/",
        "https://www.w3.org/ns/activitystreams#Public"],
 "actor": "https://social.example/alyssa/",
 "object": {"type": "Note",
            "id": "https://social.example/alyssa/posts/d18c55d4-8a63-4181-9745-4e6cf7938fa1",
            "attributedTo": "https://social.example/alyssa/",
            "to": ["https://social.example/alyssa/followers/",
                   "https://www.w3.org/ns/activitystreams#Public"],
            "content": "Lending books to friends is nice.  Getting them back is even nicer! :)"}}
		

What is Mastodon?

Mastodon Moderation Principles

  • Community
  • Ratios
  • Approachable

Mastodon Design Decisions

Making the fediverse better a decision at a time

  • Deliberately does not support arbitary search
  • Delete and re-draft

Mastodon Design Decisions

Making the fediverse better a decision at a time

  • No quoting of messages
  • Privacy

Mastodon vs Twitter

  • No ads
  • No tracking
  • 500 character support
  • Polls
  • Content warning

Default interface

https://glasgow.social/tags/scotlug

Example: Peertube and Mastodon

Example: Peertube and Mastodon [comment]

Example: Peertube and Mastodon [comment]

Example: Peertube and Mastodon [comment]

Example: Peertube and Mastodon [subscribe]

Example: Peertube and Mastodon [subscribe]

Example: Peertube and Mastodon [subscribe]

Implementing a basic ActivityPub server

Goal: Send a Mastodon user a message from our own, non-Mastodon server

  1. Create a verifiable actor
  2. Format the message with ActivityPub
  3. Cryptographically sign the message then POST

Generate RSA keypair

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

Create an actor

{
	"@context": [
		"https://www.w3.org/ns/activitystreams",
		"https://w3id.org/security/v1"
	],

	"id": "https://my-example.com/actor",
	"type": "Person",
	"preferredUsername": "alice",
	"inbox": "https://my-example.com/inbox",

	"publicKey": {
		"id": "https://my-example.com/actor#main-key",
		"owner": "https://my-example.com/actor",
		"publicKeyPem": "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----"
	}
}

Discoverable via Webfinger

endpoint: /.well-known/webfinger
queries: /.well-known/webfinger?resource=acct:bob@my-example.com

{
	"subject": "acct:alice@my-example.com",

	"links": [
		{
			"rel": "self",
			"type": "application/activity+json",
			"href": "https://my-example.com/actor"
		}
	]
}

Construct our message

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

	"id": "https://my-example.com/create-hello-world",
	"type": "Create",
	"actor": "https://my-example.com/actor",

	"object": {
		"id": "https://my-example.com/hello-world",
		"type": "Note",
		"published": "2018-06-23T17:17:11Z",
		"attributedTo": "https://my-example.com/actor",
		"inReplyTo": "https://mastodon.social/@Gargron/100254678717223630",
		"content": "

Hello world

", "to": "https://www.w3.org/ns/activitystreams#Public" } }

Create HTTP signature and POST

require 'http'
require 'openssl'

document      = File.read('create-hello-world.json')
date          = Time.now.utc.httpdate
keypair       = OpenSSL::PKey::RSA.new(File.read('private.pem'))
signed_string = "(request-target): post /inbox\nhost: mastodon.social\ndate: #{date}"
signature     = Base64.strict_encode64(keypair.sign(OpenSSL::Digest::SHA256.new, signed_string))
header        = 'keyId="https://my-example.com/actor",headers="(request-target) host date",signature="' + signature + '"'

HTTP.headers({ 'Host': 'mastodon.social', 'Date': date, 'Signature': header })
    .post('https://mastodon.social/inbox', body: document)

Done!

Mastodon-cat

$input = file_get_contents("php://stdin");
require 'test_credentials.php';

$name = 'Adelaide'; $instance = 'glasgow.social';
$oAuth = new Colorfield\Mastodon\MastodonOAuth($name, $instance);
$oAuth->config->setClientId($client_id);
$oAuth->config->setClientSecret($client_secret);
$oAuth->config->setBearer($bearer);
$mastodonAPI = new Colorfield\Mastodon\MastodonAPI($oAuth->config);

$params['status'] = $input;
$params['visibility'] = "unlisted";
$post = $mastodonAPI->post('/statuses', $params);
	

Mastodon/Reddit PHP Bot

/* SNIP (Get Reddit RSS feed and save as $summary) */

$params['status'] = $summary;
$params['visibility'] = "public";
require 'bot_credentials.php';

$name = 'RedditBot'; $instance = 'glasgow.social';

$oAuth = new Colorfield\Mastodon\MastodonOAuth($name, $instance);
$oAuth->config->setClientId($client_id);
$oAuth->config->setClientSecret($client_secret);
$oAuth->config->setBearer($bearer);
$mastodonAPI = new Colorfield\Mastodon\MastodonAPI($oAuth->config);

$post = $mastodonAPI->post('/statuses', $params);

Problems/caveats

  • Adoption
  • Scalability
  • Migration

What comes next?

  • Check-ins/geolocation sharing
  • End-to-end encrypted messaging
  • ActivityPub enabled forums
  • Federated link aggregration
  • Payments/patreon
  • Libraries
References

Thank You!

These slides available at:
https://neil.mckillop.org/talks/mastodon_activitypub/